From 3a03a9030db55153d911408249fff7bbb47b78b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=83=A1=E9=95=87?= Date: Fri, 14 Jun 2024 18:34:05 +0800 Subject: [PATCH 01/11] docs: 3.x initial upload --- .editorconfig | 8 + .prettierrc.js | 2 +- codesandbox@3/00-create-alova/react.js | 9 + codesandbox@3/00-create-alova/svelte.js | 9 + .../00-create-alova/vueComposition.js | 9 + codesandbox@3/00-create-alova/vueOptions.js | 9 + .../react-useRequest.en.jsx | 42 + .../react-useRequest.zh.jsx | 42 + .../svelte-useRequest.en.svelte | 44 + .../svelte-useRequest.zh.svelte | 44 + .../vueComposition-useRequest.en.vue | 41 + .../vueComposition-useRequest.zh.vue | 41 + .../02-use-watcher/react-search.en.jsx | 45 + .../02-use-watcher/react-search.zh.jsx | 49 + .../02-use-watcher/svelte-search.en.svelte | 33 + .../02-use-watcher/svelte-search.zh.svelte | 33 + .../vueComposition-search.en.vue | 37 + .../vueComposition-search.zh.vue | 36 + .../02-use-watcher/vueOptions-search.en.vue | 44 + .../02-use-watcher/vueOptions-search.zh.vue | 44 + .../02-getting-started/02-quick-start.md | 242 +- docs/tutorial/02-getting-started/03-method.md | 100 +- docs/tutorial/02-getting-started/04-alova.md | 16 +- .../05-global-interceptor.md | 273 +- .../02-getting-started/06-method-metadata.md | 8 +- .../07-combine-framework.md | 284 ++ docs/tutorial/02-getting-started/08-server.md | 150 + .../09-plugin-integration.md | 93 + .../02-getting-started/10-congratulations.md | 26 + docs/tutorial/02-getting-started/README.md | 72 +- docs/tutorial/03-client/01-use-request.md | 227 ++ docs/tutorial/03-client/02-use-watcher.md | 148 + docs/tutorial/03-client/03-use-pagination.md | 699 ++++ docs/tutorial/03-client/04-use-form.md | 488 +++ .../03-client/05-token-authentication.md | 507 +++ .../tutorial/03-client/06-use-auto-request.md | 132 + .../07-action-delegation-middleware.md | 205 ++ .../02-virtual-data.md | 177 + .../03-start-silent-factory.md | 68 + .../04-conservative-request.md | 136 + .../05-modify-response.md | 260 ++ .../06-request-retry.md | 0 .../07-data-compensation.md | 56 + .../08-edit-item.md | 111 + .../09-what-more.md | 353 ++ .../08-sensorless-data-interaction}/README.md | 0 .../_category_.json | 0 docs/tutorial/03-client/09-use-captcha.md | 180 + .../03-client/10-use-serial-request.md | 107 + .../03-client/11-use-serial-watcher.md | 108 + .../03-client/12-use-retriable-request.md | 232 ++ docs/tutorial/03-client/13-use-sse.md | 248 ++ .../03-client/14-use-breakpoint-uploader.md | 6 + docs/tutorial/03-client/15-use-uploader.md | 6 + .../03-client/16-rate-limit-middleware.md | 8 + .../20-typescript.md} | 0 docs/tutorial/03-client/README.md | 19 + docs/tutorial/03-client/_category_.json | 3 + docs/tutorial/04-server/01-retry.md | 129 + docs/tutorial/04-server/02-send-captcha.md | 8 + .../04-server/03-rate-limit-middleware.md | 8 + docs/tutorial/04-server/README.md | 24 + docs/tutorial/04-server/_category_.json | 3 + .../01-manage-apis.md | 0 .../02-skills.md | 0 .../03-manage-cache-by-indexeddb.md | 0 .../04-multiple-servers.md | 0 .../05-middleware.md | 0 .../_category_.json | 0 .../{04-cache => 07-cache}/01-mode.md | 0 .../02-auto-invalidate.md | 0 .../03-manually-invalidate.md | 0 .../04-force-request.md | 0 .../05-set-and-query.md | 0 .../06-controlled-cache.md | 0 docs/tutorial/07-cache/07-server-cache.md | 8 + .../tutorial/{04-cache => 07-cache}/README.md | 0 .../{04-cache => 07-cache}/_category_.json | 0 docusaurus.config.ts | 12 +- .../current.json | 148 +- .../02-getting-started/02-quick-start.md | 242 +- .../tutorial/02-getting-started/03-method.md | 23 +- .../tutorial/02-getting-started/04-alova.md | 16 +- .../05-global-interceptor.md | 273 +- .../02-getting-started/06-method-metadata.md | 8 +- .../07-combine-framework.md | 280 ++ .../tutorial/02-getting-started/08-server.md | 150 + .../09-plugin-integration.md | 92 + .../02-getting-started/10-congratulations.md | 26 + .../tutorial/02-getting-started/README.md | 66 +- .../tutorial/03-client/01-use-request.md | 229 ++ .../tutorial/03-client/02-use-watcher.md | 148 + .../tutorial/03-client/03-use-pagination.md | 699 ++++ .../current/tutorial/03-client/04-use-form.md | 488 +++ .../03-client/05-token-authentication.md | 509 +++ .../tutorial/03-client/06-use-auto-request.md | 132 + .../07-action-delegation-middleware.md | 205 ++ .../02-virtual-data.md | 177 + .../03-start-silent-factory.md | 68 + .../04-conservative-request.md | 136 + .../05-modify-response.md | 260 ++ .../06-request-retry.md | 0 .../07-data-compensation.md | 56 + .../08-edit-item.md | 111 + .../09-what-more.md | 353 ++ .../08-sensorless-data-interaction}/README.md | 0 .../_category_.json | 0 .../tutorial/03-client/09-use-captcha.md | 174 + .../03-client/10-use-serial-request.md | 107 + .../03-client/11-use-serial-watcher.md | 108 + .../03-client/12-use-retriable-request.md | 232 ++ .../current/tutorial/03-client/13-use-sse.md | 248 ++ .../03-client/14-use-breakpoint-uploader.md | 6 + .../tutorial/03-client/15-use-uploader.md | 177 + .../03-client/16-rate-limit-middleware.md | 8 + .../20-typescript.md} | 0 .../current/tutorial/03-client/README.md | 17 + .../tutorial/03-client/_category_.json | 3 + .../current/tutorial/04-server/01-retry.md | 128 + .../tutorial/04-server/02-send-captcha.md | 8 + .../04-server/03-rate-limit-middleware.md | 8 + .../current/tutorial/04-server/README.md | 24 + .../tutorial/04-server/_category_.json | 3 + .../01-manage-apis.md | 0 .../02-skills.md | 0 .../03-manage-cache-by-indexeddb.md | 0 .../04-multiple-servers.md | 0 .../05-middleware.md | 0 .../12-parallel-request.md | 0 .../13-serial-request.md | 0 .../{04-cache => 07-cache}/01-mode.md | 0 .../02-auto-invalidate.md | 0 .../03-manually-invalidate.md | 0 .../04-force-request.md | 0 .../05-set-and-query.md | 0 .../06-controlled-cache.md | 0 .../tutorial/07-cache/07-server-cache.md | 8 + .../tutorial/{04-cache => 07-cache}/README.md | 0 .../{04-cache => 07-cache}/_category_.json | 0 .../version-2.x/api/01-alova.md | 314 ++ .../version-2.x/api/02-method.md | 481 +++ .../version-2.x/api/03-core-hooks.md | 268 ++ .../version-2.x/api/04-cache.md | 133 + .../version-2.x/api/05-states.md | 120 + .../version-2.x/api/06-global-config.md | 36 + .../version-2.x/api/_category_.json | 6 + .../version-2.x/contributing/01-overview.md | 176 + .../contributing/02-become-core-member.md | 36 + .../contributing/03-developing-guidelines.md | 138 + .../contributing/04-code-of-conduct.md | 90 + .../version-2.x/contributing/_category_.json | 6 + .../tutorial/01-example/01-init-page.md | 16 + .../tutorial/01-example/02-submit-form.md | 16 + .../01-example/03-condition-search.md | 14 + .../tutorial/01-example/04-memory-cache.md | 21 + .../01-example/05-storage-placeholder.md | 21 + .../tutorial/01-example/06-storage-restore.md | 21 + .../tutorial/01-example/07-update-state.md | 20 + .../tutorial/01-example/08-prefetch.md | 21 + .../tutorial/01-example/09-load-more.md | 23 + .../tutorial/01-example/10-paginated-list.md | 23 + .../11-controlled-cache-by-indexeddb.md | 23 + .../01-example/12-silent-submit-setting.md | 23 + .../13-silent-submit-simple-list.md | 23 + .../01-example/14-silent-submit-notes.md | 23 + .../tutorial/01-example/15-form-hook.md | 18 + .../tutorial/01-example/16-captcha-send.md | 18 + .../tutorial/01-example/17-retriable-hook.md | 18 + .../18.action-delegation-middleware.md | 18 + .../tutorial/01-example/19-serial-request.md | 19 + .../tutorial/01-example/_category_.json | 7 + .../02-getting-started/02-quick-start.md | 115 + .../tutorial/02-getting-started/03-method.md | 306 ++ .../tutorial/02-getting-started/04-alova.md | 67 + .../05-global-interceptor.md | 137 + .../02-getting-started/06-method-metadata.md | 157 + .../tutorial/02-getting-started/README.md | 148 + .../02-getting-started/_category_.json | 3 + .../03-combine-framework/02-use-request.md | 0 .../03-combine-framework/03-use-watcher.md | 0 .../03-combine-framework/04-initial-data.md | 0 .../03-combine-framework/05-response.md | 0 .../03-combine-framework/06-abort-request.md | 0 .../07-download-upload-progress.md | 0 .../03-combine-framework/08-receive-params.md | 0 .../03-combine-framework/10-typescript.md | 159 + .../tutorial/03-combine-framework/README.md | 0 .../03-combine-framework/_category_.json | 0 .../version-2.x/tutorial/04-cache/01-mode.md | 218 ++ .../tutorial/04-cache/02-auto-invalidate.md | 91 + .../04-cache/03-manually-invalidate.md | 102 + .../tutorial/04-cache/04-force-request.md | 75 + .../tutorial/04-cache/05-set-and-query.md | 284 ++ .../tutorial/04-cache/06-controlled-cache.md | 83 + .../version-2.x/tutorial/04-cache/README.md | 9 + .../tutorial/04-cache/_category_.json | 3 + .../02-virtual-data.md | 0 .../03-start-silent-factory.md | 0 .../04-conservative-request.md | 0 .../05-modify-response.md | 0 .../06-request-retry.md | 144 + .../07-data-compensation.md | 0 .../08-edit-item.md | 0 .../09-what-more.md | 0 .../01-sensorless-data-interaction/README.md | 146 + .../_category_.json | 3 + .../tutorial/05-strategy/02-usePagination.md | 0 .../tutorial/05-strategy/03-useForm.md | 0 .../05-strategy/04-tokenAuthentication.md | 0 .../tutorial/05-strategy/05-useUploader.md | 0 .../tutorial/05-strategy/06-useAutoRequest.md | 0 .../05-strategy/07-useBreakpointUploader.md | 0 .../tutorial/05-strategy/08-useCaptcha.md | 0 .../09-actionDelegationMiddleware.md | 0 .../05-strategy/10-useSerialRequest.md | 0 .../05-strategy/11-useSerialWatcher.md | 0 .../05-strategy/12-useRetriableRequest.md | 0 .../tutorial/05-strategy/13-useSSE.md | 0 .../05-strategy/14-rateLimitMiddleware.md | 0 .../tutorial/05-strategy/README.md | 0 .../tutorial/05-strategy/_category_.json | 0 .../tutorial/06-advanced/01-use-fetcher.md | 307 ++ .../02-update-across-components.md | 102 + .../tutorial/06-advanced/03-method-matcher.md | 141 + .../tutorial/06-advanced/04-middleware.md | 405 +++ .../06-advanced/05-custom-method-key.md | 26 + .../tutorial/06-advanced/06-error-logger.md | 51 + .../tutorial/06-advanced/07-cache-logger.md | 60 + .../06-advanced/08-manage-extra-states.md | 199 ++ .../tutorial/06-advanced/09-ssr.md | 228 ++ .../tutorial/06-advanced/README.md | 9 + .../tutorial/06-advanced/_category_.json | 3 + .../07-best-practice/01-manage-apis.md | 154 + .../tutorial/07-best-practice/02-skills.md | 330 ++ .../03-manage-cache-by-indexeddb.md | 123 + .../07-best-practice/04-multiple-servers.md | 20 + .../07-best-practice/05-middleware.md | 29 + .../07-best-practice/12-parallel-request.md | 60 + .../07-best-practice/13-serial-request.md | 49 + .../08-request-adapter/01-alova-mock.md | 329 ++ .../02-alova-adapter-xhr.md | 337 ++ .../03-alova-adapter-axios.md | 291 ++ .../04-alova-adapter-taro.md | 436 +++ .../05-alova-adapter-uniapp.md | 279 ++ .../08-request-adapter/_category_.json | 6 + .../tutorial/09-framework/01-vue-options.md | 334 ++ .../tutorial/09-framework/02-solid.md | 6 + .../tutorial/09-framework/03-angular.md | 6 + .../tutorial/09-framework/04-native-mp.md | 6 + .../tutorial/09-framework/05-preact.md | 6 + .../tutorial/09-framework/06-qwik.md | 6 + .../tutorial/09-framework/07-lit.md | 6 + .../tutorial/09-framework/08-stencil.md | 6 + .../tutorial/10-custom/01-overview.md | 26 + .../10-custom/02-custom-http-adapter.md | 193 ++ .../10-custom/03-custom-storage-adapter.md | 48 + .../10-custom/04-custom-stateshook.md | 101 + .../tutorial/10-custom/_category_.json | 6 + .../version-2.x/tutorial/11-others/01-RSM.md | 61 + .../tutorial/11-others/02-comparison.md | 93 + .../version-2.x/tutorial/11-others/03-Q&A.md | 24 + .../tutorial/11-others/04-future.md | 36 + .../tutorial/11-others/05-react-native.md | 15 + .../tutorial/11-others/06-use-in-static.md | 139 + .../11-others/07-hide-recommend-tips.md | 46 + .../tutorial/11-others/_category_.json | 6 + package-lock.json | 3000 ++++++++--------- package.json | 102 +- static/img/overview_flow_cn.png | Bin 0 -> 60554 bytes static/img/overview_flow_en.png | Bin 0 -> 29394 bytes versioned_docs/version-2.x/api/01-alova.md | 314 ++ versioned_docs/version-2.x/api/02-method.md | 481 +++ .../version-2.x/api/03-core-hooks.md | 268 ++ versioned_docs/version-2.x/api/04-cache.md | 133 + versioned_docs/version-2.x/api/05-states.md | 120 + .../version-2.x/api/06-global-config.md | 36 + .../version-2.x/api/_category_.json | 6 + .../version-2.x/contributing/01-overview.md | 175 + .../contributing/02-become-core-member.md | 32 + .../contributing/03-developing-guidelines.md | 138 + .../contributing/04-code-of-conduct.md | 137 + .../version-2.x/contributing/_category_.json | 6 + .../tutorial/01-example/01-init-page.md | 16 + .../tutorial/01-example/02-submit-form.md | 16 + .../01-example/03-condition-search.md | 16 + .../tutorial/01-example/04-memory-cache.md | 21 + .../01-example/05-storage-placeholder.md | 21 + .../tutorial/01-example/06-storage-restore.md | 21 + .../tutorial/01-example/07-update-state.md | 20 + .../tutorial/01-example/08-prefetch.md | 21 + .../tutorial/01-example/09-load-more.md | 23 + .../tutorial/01-example/10-paginated-list.md | 23 + .../11-controlled-cache-by-indexeddb.md | 23 + .../01-example/12-silent-submit-setting.md | 23 + .../13-silent-submit-simple-list.md | 23 + .../01-example/14-silent-submit-notes.md | 23 + .../tutorial/01-example/15-form-hook.md | 18 + .../tutorial/01-example/16-captcha-send.md | 18 + .../tutorial/01-example/17-retriable-hook.md | 18 + .../18.action-delegation-middleware.md | 18 + .../tutorial/01-example/19-serial-request.md | 19 + .../tutorial/01-example/_category_.json | 7 + .../02-getting-started/02-quick-start.md | 115 + .../tutorial/02-getting-started/03-method.md | 306 ++ .../tutorial/02-getting-started/04-alova.md | 67 + .../05-global-interceptor.md | 137 + .../02-getting-started/06-method-metadata.md | 157 + .../tutorial/02-getting-started/README.md | 136 + .../02-getting-started/_category_.json | 3 + .../03-combine-framework/02-use-request.md | 0 .../03-combine-framework/03-use-watcher.md | 0 .../03-combine-framework/04-initial-data.md | 0 .../03-combine-framework/05-response.md | 0 .../03-combine-framework/06-abort-request.md | 0 .../07-download-upload-progress.md | 0 .../03-combine-framework/08-receive-params.md | 0 .../03-combine-framework/10-typescript.md | 159 + .../tutorial/03-combine-framework/README.md | 0 .../03-combine-framework/_category_.json | 0 .../version-2.x/tutorial/04-cache/01-mode.md | 215 ++ .../tutorial/04-cache/02-auto-invalidate.md | 91 + .../04-cache/03-manually-invalidate.md | 102 + .../tutorial/04-cache/04-force-request.md | 75 + .../tutorial/04-cache/05-set-and-query.md | 284 ++ .../tutorial/04-cache/06-controlled-cache.md | 83 + .../version-2.x/tutorial/04-cache/README.md | 9 + .../tutorial/04-cache/_category_.json | 3 + .../02-virtual-data.md | 0 .../03-start-silent-factory.md | 0 .../04-conservative-request.md | 0 .../05-modify-response.md | 0 .../06-request-retry.md | 144 + .../07-data-compensation.md | 0 .../08-edit-item.md | 0 .../09-what-more.md | 0 .../01-sensorless-data-interaction/README.md | 146 + .../_category_.json | 3 + .../tutorial/05-strategy/02-usePagination.md | 0 .../tutorial/05-strategy/03-useForm.md | 0 .../05-strategy/04-tokenAuthentication.md | 0 .../tutorial/05-strategy/05-useUploader.md | 0 .../tutorial/05-strategy/06-useAutoRequest.md | 0 .../05-strategy/07-useBreakpointUploader.md | 0 .../tutorial/05-strategy/08-useCaptcha.md | 0 .../09-actionDelegationMiddleware.md | 0 .../05-strategy/10-useSerialRequest.md | 0 .../05-strategy/11-useSerialWatcher.md | 0 .../05-strategy/12-useRetriableRequest.md | 0 .../tutorial/05-strategy/13-useSSE.md | 0 .../05-strategy/14-rateLimitMiddleware.md | 0 .../tutorial/05-strategy/README.md | 0 .../tutorial/05-strategy/_category_.json | 0 .../tutorial/06-advanced/01-use-fetcher.md | 307 ++ .../02-update-across-components.md | 102 + .../tutorial/06-advanced/03-method-matcher.md | 141 + .../tutorial/06-advanced/04-middleware.md | 405 +++ .../06-advanced/05-custom-method-key.md | 26 + .../tutorial/06-advanced/06-error-logger.md | 37 + .../tutorial/06-advanced/07-cache-logger.md | 60 + .../06-advanced/08-manage-extra-states.md | 198 ++ .../tutorial/06-advanced/09-ssr.md | 228 ++ .../tutorial/06-advanced/README.md | 9 + .../tutorial/06-advanced/_category_.json | 3 + .../07-best-practice/01-manage-apis.md | 154 + .../tutorial/07-best-practice/02-skills.md | 330 ++ .../03-manage-cache-by-indexeddb.md | 123 + .../07-best-practice/04-multiple-servers.md | 20 + .../07-best-practice/05-middleware.md | 29 + .../tutorial/07-best-practice/_category_.json | 6 + .../08-request-adapter/01-alova-mock.md | 331 ++ .../02-alova-adapter-xhr.md | 337 ++ .../03-alova-adapter-axios.md | 293 ++ .../04-alova-adapter-taro.md | 436 +++ .../05-alova-adapter-uniapp.md | 279 ++ .../08-request-adapter/_category_.json | 6 + .../tutorial/09-framework/01-vue-options.md | 333 ++ .../tutorial/09-framework/02-solid.md | 6 + .../tutorial/09-framework/03-angular.md | 6 + .../tutorial/09-framework/04-native-mp.md | 6 + .../tutorial/09-framework/05-preact.md | 6 + .../tutorial/09-framework/06-qwik.md | 6 + .../tutorial/09-framework/07-lit.md | 6 + .../tutorial/09-framework/08-stencil.md | 6 + .../tutorial/09-framework/_category_.json | 6 + .../tutorial/10-custom/01-overview.md | 26 + .../10-custom/02-custom-http-adapter.md | 193 ++ .../10-custom/03-custom-storage-adapter.md | 48 + .../10-custom/04-custom-stateshook.md | 101 + .../tutorial/10-custom/_category_.json | 6 + .../version-2.x/tutorial/11-others/01-RSM.md | 61 + .../tutorial/11-others/02-comparison.md | 93 + .../version-2.x/tutorial/11-others/03-Q&A.md | 24 + .../tutorial/11-others/04-future.md | 36 + .../tutorial/11-others/05-react-native.md | 15 + .../tutorial/11-others/06-use-in-static.md | 139 + .../11-others/07-hide-recommend-tips.md | 46 + .../tutorial/11-others/_category_.json | 6 + versioned_sidebars/version-2.x-sidebars.json | 20 + versions.json | 3 + 399 files changed, 32581 insertions(+), 2347 deletions(-) create mode 100644 .editorconfig create mode 100644 codesandbox@3/00-create-alova/react.js create mode 100644 codesandbox@3/00-create-alova/svelte.js create mode 100644 codesandbox@3/00-create-alova/vueComposition.js create mode 100644 codesandbox@3/00-create-alova/vueOptions.js create mode 100644 codesandbox@3/01-getting-started/07-combine-framework/react-useRequest.en.jsx create mode 100644 codesandbox@3/01-getting-started/07-combine-framework/react-useRequest.zh.jsx create mode 100644 codesandbox@3/01-getting-started/07-combine-framework/svelte-useRequest.en.svelte create mode 100644 codesandbox@3/01-getting-started/07-combine-framework/svelte-useRequest.zh.svelte create mode 100644 codesandbox@3/01-getting-started/07-combine-framework/vueComposition-useRequest.en.vue create mode 100644 codesandbox@3/01-getting-started/07-combine-framework/vueComposition-useRequest.zh.vue create mode 100644 codesandbox@3/02-client/02-use-watcher/react-search.en.jsx create mode 100644 codesandbox@3/02-client/02-use-watcher/react-search.zh.jsx create mode 100644 codesandbox@3/02-client/02-use-watcher/svelte-search.en.svelte create mode 100644 codesandbox@3/02-client/02-use-watcher/svelte-search.zh.svelte create mode 100644 codesandbox@3/02-client/02-use-watcher/vueComposition-search.en.vue create mode 100644 codesandbox@3/02-client/02-use-watcher/vueComposition-search.zh.vue create mode 100644 codesandbox@3/02-client/02-use-watcher/vueOptions-search.en.vue create mode 100644 codesandbox@3/02-client/02-use-watcher/vueOptions-search.zh.vue create mode 100644 docs/tutorial/02-getting-started/07-combine-framework.md create mode 100644 docs/tutorial/02-getting-started/08-server.md create mode 100644 docs/tutorial/02-getting-started/09-plugin-integration.md create mode 100644 docs/tutorial/02-getting-started/10-congratulations.md create mode 100644 docs/tutorial/03-client/01-use-request.md create mode 100644 docs/tutorial/03-client/02-use-watcher.md create mode 100644 docs/tutorial/03-client/03-use-pagination.md create mode 100644 docs/tutorial/03-client/04-use-form.md create mode 100644 docs/tutorial/03-client/05-token-authentication.md create mode 100644 docs/tutorial/03-client/06-use-auto-request.md create mode 100644 docs/tutorial/03-client/07-action-delegation-middleware.md create mode 100644 docs/tutorial/03-client/08-sensorless-data-interaction/02-virtual-data.md create mode 100644 docs/tutorial/03-client/08-sensorless-data-interaction/03-start-silent-factory.md create mode 100644 docs/tutorial/03-client/08-sensorless-data-interaction/04-conservative-request.md create mode 100644 docs/tutorial/03-client/08-sensorless-data-interaction/05-modify-response.md rename docs/tutorial/{05-strategy/01-sensorless-data-interaction => 03-client/08-sensorless-data-interaction}/06-request-retry.md (100%) create mode 100644 docs/tutorial/03-client/08-sensorless-data-interaction/07-data-compensation.md create mode 100644 docs/tutorial/03-client/08-sensorless-data-interaction/08-edit-item.md create mode 100644 docs/tutorial/03-client/08-sensorless-data-interaction/09-what-more.md rename docs/tutorial/{05-strategy/01-sensorless-data-interaction => 03-client/08-sensorless-data-interaction}/README.md (100%) rename docs/tutorial/{05-strategy/01-sensorless-data-interaction => 03-client/08-sensorless-data-interaction}/_category_.json (100%) create mode 100644 docs/tutorial/03-client/09-use-captcha.md create mode 100644 docs/tutorial/03-client/10-use-serial-request.md create mode 100644 docs/tutorial/03-client/11-use-serial-watcher.md create mode 100644 docs/tutorial/03-client/12-use-retriable-request.md create mode 100644 docs/tutorial/03-client/13-use-sse.md create mode 100644 docs/tutorial/03-client/14-use-breakpoint-uploader.md create mode 100644 docs/tutorial/03-client/15-use-uploader.md create mode 100644 docs/tutorial/03-client/16-rate-limit-middleware.md rename docs/tutorial/{03-combine-framework/10-typescript.md => 03-client/20-typescript.md} (100%) create mode 100644 docs/tutorial/03-client/README.md create mode 100644 docs/tutorial/03-client/_category_.json create mode 100644 docs/tutorial/04-server/01-retry.md create mode 100644 docs/tutorial/04-server/02-send-captcha.md create mode 100644 docs/tutorial/04-server/03-rate-limit-middleware.md create mode 100644 docs/tutorial/04-server/README.md create mode 100644 docs/tutorial/04-server/_category_.json rename docs/tutorial/{07-best-practice => 05-best-practice}/01-manage-apis.md (100%) rename docs/tutorial/{07-best-practice => 05-best-practice}/02-skills.md (100%) rename docs/tutorial/{07-best-practice => 05-best-practice}/03-manage-cache-by-indexeddb.md (100%) rename docs/tutorial/{07-best-practice => 05-best-practice}/04-multiple-servers.md (100%) rename docs/tutorial/{07-best-practice => 05-best-practice}/05-middleware.md (100%) rename docs/tutorial/{07-best-practice => 05-best-practice}/_category_.json (100%) rename docs/tutorial/{04-cache => 07-cache}/01-mode.md (100%) rename docs/tutorial/{04-cache => 07-cache}/02-auto-invalidate.md (100%) rename docs/tutorial/{04-cache => 07-cache}/03-manually-invalidate.md (100%) rename docs/tutorial/{04-cache => 07-cache}/04-force-request.md (100%) rename docs/tutorial/{04-cache => 07-cache}/05-set-and-query.md (100%) rename docs/tutorial/{04-cache => 07-cache}/06-controlled-cache.md (100%) create mode 100644 docs/tutorial/07-cache/07-server-cache.md rename docs/tutorial/{04-cache => 07-cache}/README.md (100%) rename docs/tutorial/{04-cache => 07-cache}/_category_.json (100%) create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/07-combine-framework.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/08-server.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/09-plugin-integration.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/10-congratulations.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-use-request.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/02-use-watcher.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/03-use-pagination.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/04-use-form.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/05-token-authentication.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/06-use-auto-request.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/07-action-delegation-middleware.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/02-virtual-data.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/03-start-silent-factory.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/04-conservative-request.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/05-modify-response.md rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{05-strategy/01-sensorless-data-interaction => 03-client/08-sensorless-data-interaction}/06-request-retry.md (100%) create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/07-data-compensation.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/08-edit-item.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/09-what-more.md rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{05-strategy/01-sensorless-data-interaction => 03-client/08-sensorless-data-interaction}/README.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{05-strategy/01-sensorless-data-interaction => 03-client/08-sensorless-data-interaction}/_category_.json (100%) create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-use-captcha.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/10-use-serial-request.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/11-use-serial-watcher.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/12-use-retriable-request.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/13-use-sse.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/14-use-breakpoint-uploader.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/15-use-uploader.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/16-rate-limit-middleware.md rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{03-combine-framework/10-typescript.md => 03-client/20-typescript.md} (100%) create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/README.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/_category_.json create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/01-retry.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/02-send-captcha.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/03-rate-limit-middleware.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/README.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/_category_.json rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{07-best-practice => 05-best-practice}/01-manage-apis.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{07-best-practice => 05-best-practice}/02-skills.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{07-best-practice => 05-best-practice}/03-manage-cache-by-indexeddb.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{07-best-practice => 05-best-practice}/04-multiple-servers.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{07-best-practice => 05-best-practice}/05-middleware.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{07-best-practice => 05-best-practice}/12-parallel-request.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{07-best-practice => 05-best-practice}/13-serial-request.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{04-cache => 07-cache}/01-mode.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{04-cache => 07-cache}/02-auto-invalidate.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{04-cache => 07-cache}/03-manually-invalidate.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{04-cache => 07-cache}/04-force-request.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{04-cache => 07-cache}/05-set-and-query.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{04-cache => 07-cache}/06-controlled-cache.md (100%) create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/07-server-cache.md rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{04-cache => 07-cache}/README.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{04-cache => 07-cache}/_category_.json (100%) create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/01-alova.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/02-method.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/03-core-hooks.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/04-cache.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/05-states.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/06-global-config.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/_category_.json create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/contributing/01-overview.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/contributing/02-become-core-member.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/contributing/03-developing-guidelines.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/contributing/04-code-of-conduct.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/contributing/_category_.json create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/01-init-page.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/02-submit-form.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/03-condition-search.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/04-memory-cache.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/05-storage-placeholder.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/06-storage-restore.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/07-update-state.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/08-prefetch.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/09-load-more.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/10-paginated-list.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/11-controlled-cache-by-indexeddb.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/12-silent-submit-setting.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/13-silent-submit-simple-list.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/14-silent-submit-notes.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/15-form-hook.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/16-captcha-send.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/17-retriable-hook.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/18.action-delegation-middleware.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/19-serial-request.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/_category_.json create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/02-quick-start.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/03-method.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/04-alova.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/05-global-interceptor.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/06-method-metadata.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/README.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/_category_.json rename i18n/zh-CN/docusaurus-plugin-content-docs/{current => version-2.x}/tutorial/03-combine-framework/02-use-request.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/{current => version-2.x}/tutorial/03-combine-framework/03-use-watcher.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/{current => version-2.x}/tutorial/03-combine-framework/04-initial-data.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/{current => version-2.x}/tutorial/03-combine-framework/05-response.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/{current => version-2.x}/tutorial/03-combine-framework/06-abort-request.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/{current => version-2.x}/tutorial/03-combine-framework/07-download-upload-progress.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/{current => version-2.x}/tutorial/03-combine-framework/08-receive-params.md (100%) create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/10-typescript.md rename i18n/zh-CN/docusaurus-plugin-content-docs/{current => version-2.x}/tutorial/03-combine-framework/README.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/{current => version-2.x}/tutorial/03-combine-framework/_category_.json (100%) create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/01-mode.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/02-auto-invalidate.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/03-manually-invalidate.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/04-force-request.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/05-set-and-query.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/06-controlled-cache.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/README.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/_category_.json rename i18n/zh-CN/docusaurus-plugin-content-docs/{current => version-2.x}/tutorial/05-strategy/01-sensorless-data-interaction/02-virtual-data.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/{current => version-2.x}/tutorial/05-strategy/01-sensorless-data-interaction/03-start-silent-factory.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/{current => version-2.x}/tutorial/05-strategy/01-sensorless-data-interaction/04-conservative-request.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/{current => version-2.x}/tutorial/05-strategy/01-sensorless-data-interaction/05-modify-response.md (100%) create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/06-request-retry.md rename i18n/zh-CN/docusaurus-plugin-content-docs/{current => version-2.x}/tutorial/05-strategy/01-sensorless-data-interaction/07-data-compensation.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/{current => version-2.x}/tutorial/05-strategy/01-sensorless-data-interaction/08-edit-item.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/{current => version-2.x}/tutorial/05-strategy/01-sensorless-data-interaction/09-what-more.md (100%) create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/README.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/_category_.json rename i18n/zh-CN/docusaurus-plugin-content-docs/{current => version-2.x}/tutorial/05-strategy/02-usePagination.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/{current => version-2.x}/tutorial/05-strategy/03-useForm.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/{current => version-2.x}/tutorial/05-strategy/04-tokenAuthentication.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/{current => version-2.x}/tutorial/05-strategy/05-useUploader.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/{current => version-2.x}/tutorial/05-strategy/06-useAutoRequest.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/{current => version-2.x}/tutorial/05-strategy/07-useBreakpointUploader.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/{current => version-2.x}/tutorial/05-strategy/08-useCaptcha.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/{current => version-2.x}/tutorial/05-strategy/09-actionDelegationMiddleware.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/{current => version-2.x}/tutorial/05-strategy/10-useSerialRequest.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/{current => version-2.x}/tutorial/05-strategy/11-useSerialWatcher.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/{current => version-2.x}/tutorial/05-strategy/12-useRetriableRequest.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/{current => version-2.x}/tutorial/05-strategy/13-useSSE.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/{current => version-2.x}/tutorial/05-strategy/14-rateLimitMiddleware.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/{current => version-2.x}/tutorial/05-strategy/README.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/{current => version-2.x}/tutorial/05-strategy/_category_.json (100%) create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/01-use-fetcher.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/02-update-across-components.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/03-method-matcher.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/04-middleware.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/05-custom-method-key.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/06-error-logger.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/07-cache-logger.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/08-manage-extra-states.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/09-ssr.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/README.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/_category_.json create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/01-manage-apis.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/02-skills.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/03-manage-cache-by-indexeddb.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/04-multiple-servers.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/05-middleware.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/12-parallel-request.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/13-serial-request.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/01-alova-mock.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/02-alova-adapter-xhr.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/03-alova-adapter-axios.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/04-alova-adapter-taro.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/05-alova-adapter-uniapp.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/_category_.json create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/01-vue-options.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/02-solid.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/03-angular.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/04-native-mp.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/05-preact.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/06-qwik.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/07-lit.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/08-stencil.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/10-custom/01-overview.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/10-custom/02-custom-http-adapter.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/10-custom/03-custom-storage-adapter.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/10-custom/04-custom-stateshook.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/10-custom/_category_.json create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/01-RSM.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/02-comparison.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/03-Q&A.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/04-future.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/05-react-native.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/06-use-in-static.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/07-hide-recommend-tips.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/_category_.json create mode 100644 static/img/overview_flow_cn.png create mode 100644 static/img/overview_flow_en.png create mode 100644 versioned_docs/version-2.x/api/01-alova.md create mode 100644 versioned_docs/version-2.x/api/02-method.md create mode 100644 versioned_docs/version-2.x/api/03-core-hooks.md create mode 100644 versioned_docs/version-2.x/api/04-cache.md create mode 100644 versioned_docs/version-2.x/api/05-states.md create mode 100644 versioned_docs/version-2.x/api/06-global-config.md create mode 100644 versioned_docs/version-2.x/api/_category_.json create mode 100644 versioned_docs/version-2.x/contributing/01-overview.md create mode 100644 versioned_docs/version-2.x/contributing/02-become-core-member.md create mode 100644 versioned_docs/version-2.x/contributing/03-developing-guidelines.md create mode 100644 versioned_docs/version-2.x/contributing/04-code-of-conduct.md create mode 100644 versioned_docs/version-2.x/contributing/_category_.json create mode 100644 versioned_docs/version-2.x/tutorial/01-example/01-init-page.md create mode 100644 versioned_docs/version-2.x/tutorial/01-example/02-submit-form.md create mode 100644 versioned_docs/version-2.x/tutorial/01-example/03-condition-search.md create mode 100644 versioned_docs/version-2.x/tutorial/01-example/04-memory-cache.md create mode 100644 versioned_docs/version-2.x/tutorial/01-example/05-storage-placeholder.md create mode 100644 versioned_docs/version-2.x/tutorial/01-example/06-storage-restore.md create mode 100644 versioned_docs/version-2.x/tutorial/01-example/07-update-state.md create mode 100644 versioned_docs/version-2.x/tutorial/01-example/08-prefetch.md create mode 100644 versioned_docs/version-2.x/tutorial/01-example/09-load-more.md create mode 100644 versioned_docs/version-2.x/tutorial/01-example/10-paginated-list.md create mode 100644 versioned_docs/version-2.x/tutorial/01-example/11-controlled-cache-by-indexeddb.md create mode 100644 versioned_docs/version-2.x/tutorial/01-example/12-silent-submit-setting.md create mode 100644 versioned_docs/version-2.x/tutorial/01-example/13-silent-submit-simple-list.md create mode 100644 versioned_docs/version-2.x/tutorial/01-example/14-silent-submit-notes.md create mode 100644 versioned_docs/version-2.x/tutorial/01-example/15-form-hook.md create mode 100644 versioned_docs/version-2.x/tutorial/01-example/16-captcha-send.md create mode 100644 versioned_docs/version-2.x/tutorial/01-example/17-retriable-hook.md create mode 100644 versioned_docs/version-2.x/tutorial/01-example/18.action-delegation-middleware.md create mode 100644 versioned_docs/version-2.x/tutorial/01-example/19-serial-request.md create mode 100644 versioned_docs/version-2.x/tutorial/01-example/_category_.json create mode 100644 versioned_docs/version-2.x/tutorial/02-getting-started/02-quick-start.md create mode 100644 versioned_docs/version-2.x/tutorial/02-getting-started/03-method.md create mode 100644 versioned_docs/version-2.x/tutorial/02-getting-started/04-alova.md create mode 100644 versioned_docs/version-2.x/tutorial/02-getting-started/05-global-interceptor.md create mode 100644 versioned_docs/version-2.x/tutorial/02-getting-started/06-method-metadata.md create mode 100644 versioned_docs/version-2.x/tutorial/02-getting-started/README.md create mode 100644 versioned_docs/version-2.x/tutorial/02-getting-started/_category_.json rename {docs => versioned_docs/version-2.x}/tutorial/03-combine-framework/02-use-request.md (100%) rename {docs => versioned_docs/version-2.x}/tutorial/03-combine-framework/03-use-watcher.md (100%) rename {docs => versioned_docs/version-2.x}/tutorial/03-combine-framework/04-initial-data.md (100%) rename {docs => versioned_docs/version-2.x}/tutorial/03-combine-framework/05-response.md (100%) rename {docs => versioned_docs/version-2.x}/tutorial/03-combine-framework/06-abort-request.md (100%) rename {docs => versioned_docs/version-2.x}/tutorial/03-combine-framework/07-download-upload-progress.md (100%) rename {docs => versioned_docs/version-2.x}/tutorial/03-combine-framework/08-receive-params.md (100%) create mode 100644 versioned_docs/version-2.x/tutorial/03-combine-framework/10-typescript.md rename {docs => versioned_docs/version-2.x}/tutorial/03-combine-framework/README.md (100%) rename {docs => versioned_docs/version-2.x}/tutorial/03-combine-framework/_category_.json (100%) create mode 100644 versioned_docs/version-2.x/tutorial/04-cache/01-mode.md create mode 100644 versioned_docs/version-2.x/tutorial/04-cache/02-auto-invalidate.md create mode 100644 versioned_docs/version-2.x/tutorial/04-cache/03-manually-invalidate.md create mode 100644 versioned_docs/version-2.x/tutorial/04-cache/04-force-request.md create mode 100644 versioned_docs/version-2.x/tutorial/04-cache/05-set-and-query.md create mode 100644 versioned_docs/version-2.x/tutorial/04-cache/06-controlled-cache.md create mode 100644 versioned_docs/version-2.x/tutorial/04-cache/README.md create mode 100644 versioned_docs/version-2.x/tutorial/04-cache/_category_.json rename {docs => versioned_docs/version-2.x}/tutorial/05-strategy/01-sensorless-data-interaction/02-virtual-data.md (100%) rename {docs => versioned_docs/version-2.x}/tutorial/05-strategy/01-sensorless-data-interaction/03-start-silent-factory.md (100%) rename {docs => versioned_docs/version-2.x}/tutorial/05-strategy/01-sensorless-data-interaction/04-conservative-request.md (100%) rename {docs => versioned_docs/version-2.x}/tutorial/05-strategy/01-sensorless-data-interaction/05-modify-response.md (100%) create mode 100644 versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/06-request-retry.md rename {docs => versioned_docs/version-2.x}/tutorial/05-strategy/01-sensorless-data-interaction/07-data-compensation.md (100%) rename {docs => versioned_docs/version-2.x}/tutorial/05-strategy/01-sensorless-data-interaction/08-edit-item.md (100%) rename {docs => versioned_docs/version-2.x}/tutorial/05-strategy/01-sensorless-data-interaction/09-what-more.md (100%) create mode 100644 versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/README.md create mode 100644 versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/_category_.json rename {docs => versioned_docs/version-2.x}/tutorial/05-strategy/02-usePagination.md (100%) rename {docs => versioned_docs/version-2.x}/tutorial/05-strategy/03-useForm.md (100%) rename {docs => versioned_docs/version-2.x}/tutorial/05-strategy/04-tokenAuthentication.md (100%) rename {docs => versioned_docs/version-2.x}/tutorial/05-strategy/05-useUploader.md (100%) rename {docs => versioned_docs/version-2.x}/tutorial/05-strategy/06-useAutoRequest.md (100%) rename {docs => versioned_docs/version-2.x}/tutorial/05-strategy/07-useBreakpointUploader.md (100%) rename {docs => versioned_docs/version-2.x}/tutorial/05-strategy/08-useCaptcha.md (100%) rename {docs => versioned_docs/version-2.x}/tutorial/05-strategy/09-actionDelegationMiddleware.md (100%) rename {docs => versioned_docs/version-2.x}/tutorial/05-strategy/10-useSerialRequest.md (100%) rename {docs => versioned_docs/version-2.x}/tutorial/05-strategy/11-useSerialWatcher.md (100%) rename {docs => versioned_docs/version-2.x}/tutorial/05-strategy/12-useRetriableRequest.md (100%) rename {docs => versioned_docs/version-2.x}/tutorial/05-strategy/13-useSSE.md (100%) rename {docs => versioned_docs/version-2.x}/tutorial/05-strategy/14-rateLimitMiddleware.md (100%) rename {docs => versioned_docs/version-2.x}/tutorial/05-strategy/README.md (100%) rename {docs => versioned_docs/version-2.x}/tutorial/05-strategy/_category_.json (100%) create mode 100644 versioned_docs/version-2.x/tutorial/06-advanced/01-use-fetcher.md create mode 100644 versioned_docs/version-2.x/tutorial/06-advanced/02-update-across-components.md create mode 100644 versioned_docs/version-2.x/tutorial/06-advanced/03-method-matcher.md create mode 100644 versioned_docs/version-2.x/tutorial/06-advanced/04-middleware.md create mode 100644 versioned_docs/version-2.x/tutorial/06-advanced/05-custom-method-key.md create mode 100644 versioned_docs/version-2.x/tutorial/06-advanced/06-error-logger.md create mode 100644 versioned_docs/version-2.x/tutorial/06-advanced/07-cache-logger.md create mode 100644 versioned_docs/version-2.x/tutorial/06-advanced/08-manage-extra-states.md create mode 100644 versioned_docs/version-2.x/tutorial/06-advanced/09-ssr.md create mode 100644 versioned_docs/version-2.x/tutorial/06-advanced/README.md create mode 100644 versioned_docs/version-2.x/tutorial/06-advanced/_category_.json create mode 100644 versioned_docs/version-2.x/tutorial/07-best-practice/01-manage-apis.md create mode 100644 versioned_docs/version-2.x/tutorial/07-best-practice/02-skills.md create mode 100644 versioned_docs/version-2.x/tutorial/07-best-practice/03-manage-cache-by-indexeddb.md create mode 100644 versioned_docs/version-2.x/tutorial/07-best-practice/04-multiple-servers.md create mode 100644 versioned_docs/version-2.x/tutorial/07-best-practice/05-middleware.md create mode 100644 versioned_docs/version-2.x/tutorial/07-best-practice/_category_.json create mode 100644 versioned_docs/version-2.x/tutorial/08-request-adapter/01-alova-mock.md create mode 100644 versioned_docs/version-2.x/tutorial/08-request-adapter/02-alova-adapter-xhr.md create mode 100644 versioned_docs/version-2.x/tutorial/08-request-adapter/03-alova-adapter-axios.md create mode 100644 versioned_docs/version-2.x/tutorial/08-request-adapter/04-alova-adapter-taro.md create mode 100644 versioned_docs/version-2.x/tutorial/08-request-adapter/05-alova-adapter-uniapp.md create mode 100644 versioned_docs/version-2.x/tutorial/08-request-adapter/_category_.json create mode 100644 versioned_docs/version-2.x/tutorial/09-framework/01-vue-options.md create mode 100644 versioned_docs/version-2.x/tutorial/09-framework/02-solid.md create mode 100644 versioned_docs/version-2.x/tutorial/09-framework/03-angular.md create mode 100644 versioned_docs/version-2.x/tutorial/09-framework/04-native-mp.md create mode 100644 versioned_docs/version-2.x/tutorial/09-framework/05-preact.md create mode 100644 versioned_docs/version-2.x/tutorial/09-framework/06-qwik.md create mode 100644 versioned_docs/version-2.x/tutorial/09-framework/07-lit.md create mode 100644 versioned_docs/version-2.x/tutorial/09-framework/08-stencil.md create mode 100644 versioned_docs/version-2.x/tutorial/09-framework/_category_.json create mode 100644 versioned_docs/version-2.x/tutorial/10-custom/01-overview.md create mode 100644 versioned_docs/version-2.x/tutorial/10-custom/02-custom-http-adapter.md create mode 100644 versioned_docs/version-2.x/tutorial/10-custom/03-custom-storage-adapter.md create mode 100644 versioned_docs/version-2.x/tutorial/10-custom/04-custom-stateshook.md create mode 100644 versioned_docs/version-2.x/tutorial/10-custom/_category_.json create mode 100644 versioned_docs/version-2.x/tutorial/11-others/01-RSM.md create mode 100644 versioned_docs/version-2.x/tutorial/11-others/02-comparison.md create mode 100644 versioned_docs/version-2.x/tutorial/11-others/03-Q&A.md create mode 100644 versioned_docs/version-2.x/tutorial/11-others/04-future.md create mode 100644 versioned_docs/version-2.x/tutorial/11-others/05-react-native.md create mode 100644 versioned_docs/version-2.x/tutorial/11-others/06-use-in-static.md create mode 100644 versioned_docs/version-2.x/tutorial/11-others/07-hide-recommend-tips.md create mode 100644 versioned_docs/version-2.x/tutorial/11-others/_category_.json create mode 100644 versioned_sidebars/version-2.x-sidebars.json create mode 100644 versions.json diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..79fe80267 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,8 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +insert_final_newline = true diff --git a/.prettierrc.js b/.prettierrc.js index 73491bef9..7be494afe 100644 --- a/.prettierrc.js +++ b/.prettierrc.js @@ -1,5 +1,5 @@ module.exports = { - printWidth: 120, // 换行字符串阈值 + printWidth: 96, // 换行字符串阈值 tabWidth: 2, // 设置工具每一个水平缩进的空格数 useTabs: false, // 是否使用tab缩进 semi: true, // 句末是否加分号 diff --git a/codesandbox@3/00-create-alova/react.js b/codesandbox@3/00-create-alova/react.js new file mode 100644 index 000000000..4ea71c41c --- /dev/null +++ b/codesandbox@3/00-create-alova/react.js @@ -0,0 +1,9 @@ +import { createAlova } from 'alova'; +import GlobalFetch from 'alova/GlobalFetch'; +import ReactHook from 'alova/react'; +export const alovaInstance = createAlova({ + baseURL: 'https://jsonplaceholder.typicode.com', + statesHook: ReactHook, + requestAdapter: GlobalFetch(), + responded: response => response.json() +}); diff --git a/codesandbox@3/00-create-alova/svelte.js b/codesandbox@3/00-create-alova/svelte.js new file mode 100644 index 000000000..ee849b7f9 --- /dev/null +++ b/codesandbox@3/00-create-alova/svelte.js @@ -0,0 +1,9 @@ +import { createAlova } from 'alova'; +import GlobalFetch from 'alova/GlobalFetch'; +import SvelteHook from 'alova/svelte'; +export const alovaInstance = createAlova({ + baseURL: 'https://jsonplaceholder.typicode.com', + statesHook: SvelteHook, + requestAdapter: GlobalFetch(), + responded: response => response.json() +}); diff --git a/codesandbox@3/00-create-alova/vueComposition.js b/codesandbox@3/00-create-alova/vueComposition.js new file mode 100644 index 000000000..97da70a23 --- /dev/null +++ b/codesandbox@3/00-create-alova/vueComposition.js @@ -0,0 +1,9 @@ +import { createAlova } from 'alova'; +import GlobalFetch from 'alova/GlobalFetch'; +import VueHook from 'alova/vue'; +export const alovaInstance = createAlova({ + baseURL: 'https://jsonplaceholder.typicode.com', + statesHook: VueHook, + requestAdapter: GlobalFetch(), + responded: response => response.json() +}); diff --git a/codesandbox@3/00-create-alova/vueOptions.js b/codesandbox@3/00-create-alova/vueOptions.js new file mode 100644 index 000000000..f9800bffe --- /dev/null +++ b/codesandbox@3/00-create-alova/vueOptions.js @@ -0,0 +1,9 @@ +import { VueOptionsHook } from '@alova/vue-options'; +import { createAlova } from 'alova'; +import GlobalFetch from 'alova/GlobalFetch'; +export const alovaInstance = createAlova({ + baseURL: 'https://jsonplaceholder.typicode.com', + statesHook: VueOptionsHook, + requestAdapter: GlobalFetch(), + responded: response => response.json() +}); diff --git a/codesandbox@3/01-getting-started/07-combine-framework/react-useRequest.en.jsx b/codesandbox@3/01-getting-started/07-combine-framework/react-useRequest.en.jsx new file mode 100644 index 000000000..8a6352e27 --- /dev/null +++ b/codesandbox@3/01-getting-started/07-combine-framework/react-useRequest.en.jsx @@ -0,0 +1,42 @@ +import { useRequest } from 'alova/client'; +import { alovaInstance } from './api'; + +const App = () => { + // Use the alova instance to create a method and pass it to useRequest to send a request + const { loading, data, error, send, update, onSuccess } = useRequest( + alovaInstance.Get('/todos/1', { + cacheFor: 0 + }), + { + initialData: {}, // Set the initial data of the data state + immediate: true // Whether to send the request immediately, the default is true + } + ); + onSuccess(event => { + event.method; //The method of the current request + event.data; //Response data of the current request + }); + + const handleSend = () => { + send(); + }; + const handleUpdate = () => { + update({ + data: { title: 'new title' } + }); + }; + + if (loading) { + return
Loading...
; + } else if (error) { + return
{error.message}
; + } + return ( +
+
Request result: {JSON.stringify(data)}
+ + +
+ ); +}; +export default App; diff --git a/codesandbox@3/01-getting-started/07-combine-framework/react-useRequest.zh.jsx b/codesandbox@3/01-getting-started/07-combine-framework/react-useRequest.zh.jsx new file mode 100644 index 000000000..050879e3c --- /dev/null +++ b/codesandbox@3/01-getting-started/07-combine-framework/react-useRequest.zh.jsx @@ -0,0 +1,42 @@ +import { useRequest } from 'alova/client'; +import { alovaInstance } from './api'; + +const App = () => { + // 使用alova实例创建method并传给useRequest即可发送请求 + const { loading, data, error, send, update, onSuccess } = useRequest( + alovaInstance.Get('/todos/1', { + cacheFor: 0 + }), + { + initialData: {}, // 设置data状态的初始数据 + immediate: true // 是否立即发送请求,默认为true + } + ); + onSuccess(event => { + event.method; // 当前请求的method + event.data; // 当前请求的响应数据 + }); + + const handleSend = () => { + send(); + }; + const handleUpdate = () => { + update({ + data: { title: 'new title' } + }); + }; + + if (loading) { + return
Loading...
; + } else if (error) { + return
{error.message}
; + } + return ( +
+
请求结果: {JSON.stringify(data)}
+ + +
+ ); +}; +export default App; diff --git a/codesandbox@3/01-getting-started/07-combine-framework/svelte-useRequest.en.svelte b/codesandbox@3/01-getting-started/07-combine-framework/svelte-useRequest.en.svelte new file mode 100644 index 000000000..f41e6852e --- /dev/null +++ b/codesandbox@3/01-getting-started/07-combine-framework/svelte-useRequest.en.svelte @@ -0,0 +1,44 @@ + + +{#if $loading} +
Loading...
+{:else if $error} +
{ $error.message }
+{:else} +
+
Request result: {{ data }}
+ + +
+{/if} diff --git a/codesandbox@3/01-getting-started/07-combine-framework/svelte-useRequest.zh.svelte b/codesandbox@3/01-getting-started/07-combine-framework/svelte-useRequest.zh.svelte new file mode 100644 index 000000000..495fec121 --- /dev/null +++ b/codesandbox@3/01-getting-started/07-combine-framework/svelte-useRequest.zh.svelte @@ -0,0 +1,44 @@ + + +{#if $loading} +
Loading...
+{:else if $error} +
{ $error.message }
+{:else} +
+
请求结果: {{ data }}
+ + +
+{/if} diff --git a/codesandbox@3/01-getting-started/07-combine-framework/vueComposition-useRequest.en.vue b/codesandbox@3/01-getting-started/07-combine-framework/vueComposition-useRequest.en.vue new file mode 100644 index 000000000..72323da87 --- /dev/null +++ b/codesandbox@3/01-getting-started/07-combine-framework/vueComposition-useRequest.en.vue @@ -0,0 +1,41 @@ + + + diff --git a/codesandbox@3/01-getting-started/07-combine-framework/vueComposition-useRequest.zh.vue b/codesandbox@3/01-getting-started/07-combine-framework/vueComposition-useRequest.zh.vue new file mode 100644 index 000000000..3b995c5d1 --- /dev/null +++ b/codesandbox@3/01-getting-started/07-combine-framework/vueComposition-useRequest.zh.vue @@ -0,0 +1,41 @@ + + + diff --git a/codesandbox@3/02-client/02-use-watcher/react-search.en.jsx b/codesandbox@3/02-client/02-use-watcher/react-search.en.jsx new file mode 100644 index 000000000..3ac6378a8 --- /dev/null +++ b/codesandbox@3/02-client/02-use-watcher/react-search.en.jsx @@ -0,0 +1,45 @@ +import { useWatcher } from 'alova'; +import { useState } from 'react'; +import { alovaInstance } from './api'; + +//Create method instance +const filterTodoList = userId => { + return alovaInstance.Get(`/users/${userId}/todos`); +}; + +const App = () => { + const [userId, setUserId] = useState(1); + const { loading, data = [] } = useWatcher( + // Must be set to a function that returns a method instance + () => filterTodoList(userId), + + // The monitored status array, these status changes will trigger a request + [userId] + ); + + return ( + <> + + + {/* Render the filtered todo list */} + {loading ?
Loading...
: null} + {!loading ? ( + + ) : null} + + ); +}; +export default App; diff --git a/codesandbox@3/02-client/02-use-watcher/react-search.zh.jsx b/codesandbox@3/02-client/02-use-watcher/react-search.zh.jsx new file mode 100644 index 000000000..6e0ca9a25 --- /dev/null +++ b/codesandbox@3/02-client/02-use-watcher/react-search.zh.jsx @@ -0,0 +1,49 @@ +import { useWatcher } from 'alova'; +import { useState } from 'react'; +import { alovaInstance } from './api'; + +// 创建method实例 +const filterTodoList = userId => { + return alovaInstance.Get(`/users/${userId}/todos`); +}; + +const App = () => { + const [userId, setUserId] = useState(1); + const { + loading, + data = [], + error + } = useWatcher( + // 必须设置为返回method实例的函数 + () => filterTodoList(userId), + + // 被监听的状态数组,这些状态变化将会触发一次请求 + [userId] + ); + + return ( + <> + + + {/* 渲染筛选后的todo列表 */} + {loading ?
Loading...
: null} + {!loading ? ( + + ) : null} + + ); +}; +export default App; diff --git a/codesandbox@3/02-client/02-use-watcher/svelte-search.en.svelte b/codesandbox@3/02-client/02-use-watcher/svelte-search.en.svelte new file mode 100644 index 000000000..8abd29e14 --- /dev/null +++ b/codesandbox@3/02-client/02-use-watcher/svelte-search.en.svelte @@ -0,0 +1,33 @@ + + + + + +{#if $loading} +
Loading...
+{:else} + +{/if} \ No newline at end of file diff --git a/codesandbox@3/02-client/02-use-watcher/svelte-search.zh.svelte b/codesandbox@3/02-client/02-use-watcher/svelte-search.zh.svelte new file mode 100644 index 000000000..e2ed7b7f0 --- /dev/null +++ b/codesandbox@3/02-client/02-use-watcher/svelte-search.zh.svelte @@ -0,0 +1,33 @@ + + + + + +{#if $loading} +
Loading...
+{:else} + +{/if} \ No newline at end of file diff --git a/codesandbox@3/02-client/02-use-watcher/vueComposition-search.en.vue b/codesandbox@3/02-client/02-use-watcher/vueComposition-search.en.vue new file mode 100644 index 000000000..ff0f42357 --- /dev/null +++ b/codesandbox@3/02-client/02-use-watcher/vueComposition-search.en.vue @@ -0,0 +1,37 @@ + + + \ No newline at end of file diff --git a/codesandbox@3/02-client/02-use-watcher/vueComposition-search.zh.vue b/codesandbox@3/02-client/02-use-watcher/vueComposition-search.zh.vue new file mode 100644 index 000000000..a85d907a4 --- /dev/null +++ b/codesandbox@3/02-client/02-use-watcher/vueComposition-search.zh.vue @@ -0,0 +1,36 @@ + + + \ No newline at end of file diff --git a/codesandbox@3/02-client/02-use-watcher/vueOptions-search.en.vue b/codesandbox@3/02-client/02-use-watcher/vueOptions-search.en.vue new file mode 100644 index 000000000..86931b54c --- /dev/null +++ b/codesandbox@3/02-client/02-use-watcher/vueOptions-search.en.vue @@ -0,0 +1,44 @@ + + + \ No newline at end of file diff --git a/codesandbox@3/02-client/02-use-watcher/vueOptions-search.zh.vue b/codesandbox@3/02-client/02-use-watcher/vueOptions-search.zh.vue new file mode 100644 index 000000000..1af46de14 --- /dev/null +++ b/codesandbox@3/02-client/02-use-watcher/vueOptions-search.zh.vue @@ -0,0 +1,44 @@ + + + \ No newline at end of file diff --git a/docs/tutorial/02-getting-started/02-quick-start.md b/docs/tutorial/02-getting-started/02-quick-start.md index 6cdfadc12..ec4855f93 100644 --- a/docs/tutorial/02-getting-started/02-quick-start.md +++ b/docs/tutorial/02-getting-started/02-quick-start.md @@ -1,115 +1,127 @@ ---- -title: Quick Start -sidebar_position: 20 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; -import EmbedSandpack from "@site/src/components/EmbedSandpack"; - -import quickStartGET from '!!raw-loader!@site/codesandbox/01-getting-started/02-first-request/get.js'; -import quickStartPOST from '!!raw-loader!@site/codesandbox/01-getting-started/02-first-request/post.js'; - -:::tip Example tip - -If you haven’t learned about alova yet, it is recommended that you read [alova overview](/tutorial/getting-started) first. - -::: - -## Install - - - - -```bash -npm install alova --save -``` - - - - -```bash -yarn add alova -``` - - - - -```bash -pnpm add alova -``` - - - - -```bash -bun add alova -``` - - - - -> You can also [use alova through CDN](/tutorial/others/use-in-static) - -## Create alova instance - -In alova, a request needs to be made through an alova instance. Let's create one first. When creating an alova instance, you need to specify a request adapter. It is recommended to use the `GlobalFetch` request adapter here, which is a package based on the `fetch API`. - - - - -```javascript -import { createAlova } from 'alova'; -import GlobalFetch from 'alova/GlobalFetch'; - -const alovaInstance = createAlova({ - requestAdapter: GlobalFetch() -}); -``` - - - - -```javascript -const { createAlova } = require('alova'); -const GlobalFetch = require('alova/GlobalFetch'); - -const alova = createAlova({ - requestAdapter: GlobalFetch(); -}); -``` - -> When using GlobalFetch in nodejs, the nodejs version requires `v17.5`, or you can use [axios request adapter](/tutorial/request-adapter/alova-adapter-axios/). - - - - -```javascript -import { createAlova } from 'npm:alova'; -import GlobalFetch from 'npm:alova/GlobalFetch'; - -const alova = createAlova({ - requestAdapter: GlobalFetch(); -}); -``` - - - - -## GET request - -Sending a request via `alovaInstance.Get` will receive a `Response` instance thanks to the `GlobalFetch` request adapter, which is simple. - - - -In an asynchronous function, you can also use `await alovaInstance.Get` to wait for a response. - -## POST request - -Submitting data via `alovaInstance.Post` is also easy. - - - -## What’s next? - -In fact, this is just the simplest request example. We will learn more about the features in the next chapters, so let's start learning. +--- +title: Quick Start +sidebar_position: 20 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import EmbedSandpack from "@site/src/components/EmbedSandpack"; + +import quickStartGET from '!!raw-loader!@site/codesandbox/01-getting-started/02-first-request/get.js'; +import quickStartPOST from '!!raw-loader!@site/codesandbox/01-getting-started/02-first-request/post.js'; + +:::tip Example Tips + +If you haven't learned about alova yet, it is recommended that you read [alova overview](/tutorial/getting-started) first. + +::: + +## Installation + + + + +```bash +npm install alova --save +``` + + + + +```bash +yarn add alova +``` + + + + +```bash +pnpm add alova +``` + + + + +```bash +bun add alova +``` + + + + +> You can also [use alova through CDN](/tutorial/others/use-in-static) + +## Create an alova instance + +In alova, you need to initiate a request through an alova instance. Let's create one first. When creating an alova instance, you need to specify a request adapter. Here we recommend using the `alova/fetch` request adapter, which is a wrapper based on the `fetch API` and is very concise. + + + + +```javascript +import { createAlova } from 'alova'; +import fetchAdapter from 'alova/fetch'; + +const alovaInstance = createAlova({ + requestAdapter: fetchAdapter() +}); +``` + + + + +```javascript +const { createAlova } = require('alova'); +const fetchAdapter = require('alova/fetch'); + +const alova = createAlova({ +requestAdapter: fetchAdapter(); +}); +``` + +> When using fetchAdapter in nodejs, the nodejs version requires `v17.5`, or you can use [axios request adapter](/tutorial/request-adapter/alova-adapter-axios/). + + + + +```javascript +import { createAlova } from 'npm:alova'; +import fetchAdapter from 'npm:alova/fetch'; + +const alova = createAlova({ +requestAdapter: fetchAdapter(); +}); +``` + + + + +## GET request + +Send a request through `alovaInstance.Get`. Since the `fetchAdapter` request adapter is used, a `Response` instance will be received. This is very simple. + +```js +const response = await alovaInstance + .Get('https://alovajs.dev/user/profile') + .then(response => response.json()); +``` + +In an asynchronous function, you can also use `await alovaInstance.Get` to wait for the response. + +## POST request + +Submit data through `alovaInstance.Post`. This is also very simple. + +```js +const response = alovaInstance + .Post('https://alovajs.dev/posts', { + title: 'foo', + body: 'bar', + userId: 1 + }) + .then(response => response.json()); +``` + +## What to do next? + +In fact, this is just a simple request example. You will learn more functions in the next chapter. Let's start learning. diff --git a/docs/tutorial/02-getting-started/03-method.md b/docs/tutorial/02-getting-started/03-method.md index a188cbd1d..c3d054c89 100644 --- a/docs/tutorial/02-getting-started/03-method.md +++ b/docs/tutorial/02-getting-started/03-method.md @@ -3,7 +3,7 @@ title: Method Instance sidebar_position: 30 --- -In the previous chapter we tried to send a request and obtain the response data. In fact, `alovaInstance.Get(...)` is not a function that initiates a request, but creates a method instance, which is a PromiseLike instance. You can use `then, catch, finally` methods or `await` Send the request just like a Promise object. +In the previous chapter, we tried to send a request and get the response data. In fact, `alovaInstance.Get(...)` is not a function that initiates a request, but creates a method instance, which is a PromiseLike instance. You can send a request through the `then, catch, finally` method or `await`, just like a Promise object. ```javascript const userMethodInstance = alovaInstance.Get('/api/user'); @@ -35,11 +35,11 @@ Simple way to write: const response = await alovaInstance.Get('/api/user'); ``` -Each method instance describes the type of each request, request url, request headers, request parameters, etc. In addition, you can define request behavior on the method instance to control how the method handles the request. +Each method instance describes the type of each request, request url, request header, request parameters, etc. In addition, you can also define request behavior on the method instance to control how the method handles the request. ## Request type -alova provides a total of 7 request types: GET, POST, PUT, DELETE, HEAD, OPTIONS, and PATCH. +alova provides 7 request types: GET, POST, PUT, DELETE, HEAD, OPTIONS, and PATCH. | Instance creation function | Parameters | | -------------------------- | --------------------------------------------- | @@ -51,13 +51,13 @@ alova provides a total of 7 request types: GET, POST, PUT, DELETE, HEAD, OPTIONS | OPTIONS | `alovaInstance.Options(url[, config])` | | PATCH | `alovaInstance.Patch(url[, data[, config]])` | -Parameter Description: +Parameter description: - `url` is the request path; - `data` is the request body data; -- `config` is the request configuration object, which includes configurations such as request headers, params parameters, request behavior parameters, etc.; +- `config` is the request configuration object, which includes the request header, params parameters, request behavior parameters and other configurations; -you can also create a method instance customly. This is useful when you need to dynamically specify the request type. +You can also create your own custom method instance, which is useful when dynamically specifying the request type. ```javascript import { Method } from 'alova'; @@ -69,13 +69,13 @@ const method = new Method('GET', alovaInstance, '/api/users', { }); ``` -Next, let’s take a look at how to define request parameters, which should seem familiar to you. +Next, let's take a look at how to define request parameters, which should be familiar to you. ## Request parameters ### URL parameters -Pass in the URL parameters through params, and the params parameters will be spliced in the form of ? after the url. +Use params to pass in URL parameters, and the params parameters will be concatenated after the url in the form of ?. ```javascript alovaInstance.Get('/todo/list', { @@ -85,7 +85,7 @@ alovaInstance.Get('/todo/list', { }); ``` -Of course, you can also directly splice it behind the url, and the effect will be the same. +Of course, you can also concatenate directly after the url, and the effect is the same. ```javascript alovaInstance.Get('/todo/list?userId=1'); @@ -93,17 +93,17 @@ alovaInstance.Get('/todo/list?userId=1'); ### Request body -When sending **POST, PUT, DELETE, PATCH request**, you can send data through the request body. At this time, the second parameter is passed in the request body. It is worth noting that the POST request can also pass in the params parameter. +When sending **POST, PUT, DELETE, PATCH requests**, data can be sent through the request body. At this time, the second parameter passed in is the request body. It is worth noting that the POST request can also pass in the params parameter. ```javascript alovaInstance.Post( '/todo', - //The second parameter is the request body + // The second parameter is the request body { title: 'test todo', time: '12:00' }, - // The third parameter is configuration + // The third parameter is the configuration { params: { userId: 1 @@ -114,7 +114,7 @@ alovaInstance.Post( ### Request header -Specify request headers via headers. +Specify the request header through headers. ```javascript alovaInstance.Get('/user', { @@ -124,9 +124,9 @@ alovaInstance.Get('/user', { }); ``` -### Other parameters supported by the request adapter +### Parameters supported by other request adapters -In addition to request headers, params parameters, etc., it also supports configuring parameters supported by the corresponding request adapter. When using `GlobalFetch` as the request adapter of alova, you can configure any `fetch API` supported parameters on the `method` instance. These Parameters will be passed to the `fetch` function during request. +In addition to request headers, params parameters, etc., it also supports configuring parameters supported by the corresponding request adapter. When using `alova/fetch` as the request adapter of alova, you can configure any parameters supported by `fetch API` on the method instance, and these parameters will be passed to the `fetch` function when requesting. ```javascript alovaInstance.Get('/todo/list', { @@ -139,7 +139,7 @@ alovaInstance.Get('/todo/list', { }); ``` -When the above `method` instance sends a request through `fetch`, it will be requested with the following parameters. +When the above method instance sends a request through `fetch`, it will request with the following parameters. ```javascript fetch('/todo/list', { @@ -152,17 +152,17 @@ fetch('/todo/list', { }); ``` -> In addition to passing Object, the request body can also pass request body parameters supported by the request adapter. For example, GlobalFetch supports passing `string | FormData | Blob | ArrayBuffer | URLSearchParams | ReadableStream` parameters. +> In addition to passing Object, the request body can also pass request body parameters supported by the request adapter, such as `alova/fetch` supports passing `string | FormData | Blob | ArrayBuffer | URLSearchParams | ReadableStream` parameters. If you use other request adapters, you can also pass the parameters they support. ## Request behavior -In [RSM](/tutorial/others/RSM), request behavior is used to describe how the request will be handled. +In [RSM](/tutorial/others/RSM), request behavior is used to describe how the request will be processed. -### overtime time +### Timeout -Set request timeout. +Set the request timeout. ```javascript // Request timeout at request level @@ -176,23 +176,26 @@ alovaInstance.Get('/todo/list', { ### Request sharing -We will always encounter this situation. When a request is issued but has not yet been responded to, the same request is issued again, resulting in a waste of requests or repeated submission of problems, such as the following three scenarios: +We always encounter this situation. When a request is sent but not responded, the same request is sent again, which causes request waste or repeated submission problems, such as the following three scenarios: -1. A component will obtain initialization data when it is created. When a page renders multiple components at the same time, multiple identical requests will be issued at the same time; -2. The submit button is not disabled and the user clicks the submit button multiple times; -3. When the preloading page is entered before preloading is completed, multiple identical requests will be initiated; -4. Prevent repeated requests from being sent under react’s StrictMode; +1. A component will obtain initialization data when it is created. When a page renders multiple components at the same time, multiple identical requests will be sent at the same time; -Shared requests are used to solve these problems. It can not only improve application fluency, but also reduce server pressure. +2. The submit button is not disabled, and the user clicks the submit button multiple times; + +3. When the preload page is entered before the preload is completed, multiple identical requests will be sent; + +4. Prevent repeated requests in react's StrictMode; + +Shared requests are used to solve these problems. It can not only improve the fluency of the application, but also reduce the pressure on the server. ```mermaid flowchart LR - classDef response fill:#a8bcff - R1[Request 1] --> S1[Send request] --> W1[Waiting for response]:::response --> RE1[Receive data 1] - R2[Same request as request 1] --> W1[Waiting for response]:::response --> RE2[Receive data 1] +classDef response fill:#a8bcff +R1[request 1] --> S1[send request] --> W1[wait for response]:::response --> RE1[receive data 1] +R2[same request as request 1] --> W1[wait for response]:::response --> RE2[receive data 1] ``` -Request sharing is enabled by default. If you wish to turn off request sharing on a specific request, you can do this: +Request sharing is enabled by default. If you want to turn off sharing requests on a specific request, you can do this: ```javascript alovaInst.Get('/todo', { @@ -203,19 +206,19 @@ alovaInst.Get('/todo', { }); ``` -:::warning How to identify identical requests +:::warning How to identify the same request -The request method, request URL, request header, URL parameters, and request body of the method instance are used as a unique identifier. If the identifier is the same, it means the same request, instead of comparing the reference address of the method instance. +The request method, request url, request header, url parameter, and request body of the method instance are used as unique identifiers. Identical identifiers indicate the same request, rather than comparing the reference address of the method instance. ::: -### Convert response data +### Transform response data -Sometimes we need to uniformly transform the response data. We can set the `transformData` function for the method instance to convert the response data into the required structure. +Sometimes we need to uniformly transform response data. We can set the `transformData` function for the method instance to transform the response data into the required structure. ```javascript alovaInstance.Get('/todo/list', { - // The function accepts response data and response header data, and requires the converted data to be returned. + // The function accepts the response data and response header data, and requires the transformed data to be returned. transformData(rawData, headers) { return rawData.list.map(item => { return { @@ -229,11 +232,20 @@ alovaInstance.Get('/todo/list', { ### Response cache -Response caching allows you to better utilize server-side data multiple times without having to send a request to obtain the data every time it is requested. GET requests will set a memory cache time of 5 minutes by default, which we will explain in detail in the [Response Caching](/tutorial/cache/mode) section later. +Response cache allows you to better utilize server-side data multiple times without sending a request to obtain data every time. The GET request will set a memory cache time of 5 minutes by default. If you don't need it, you can turn off the cache for the current request in the following way. + +```ts +alovaInstance.Get('/todo/list', { + // Set to 0 or null to turn off the default response cache + cacheFor: 0 +}); +``` + +For details, please refer to [Response Cache](/tutorial/cache/mode) -## Abort request +## Interrupt request -`[2.6.2+]` calls the `abort` of the method instance to abort the request. +Call the `abort` of the method instance to interrupt the request. ```javascript const userMethod = alovaInstance.Get('/api/user'); @@ -248,9 +260,9 @@ const handleCancel = () => { }; ``` -## Monitor upload and download progress +## Listen for upload and download progress -**[v2.17.0+]** Bind the upload progress event through `onUpload` of the method instance, and bind the download progress event through `onDownload`, which will return the unbinding function. +By binding the upload progress event through the `onUpload` of the method instance, and the download progress event through the `onDownload`, it will return the unbinding function. ```javascript const uploadMethod = alovaInstance.Post('/todo/uploadfile', formData); @@ -286,15 +298,15 @@ const handleOffEvent = () => { }; ``` -:::warning Things to note when using the `GlobalFetch` adapter +:::warning Use `alova/fetch` adapter to pay attention to -Due to fetch api limitations, the **GlobalFetch** adapter provided by alova does not support upload progress. If you need to upload progress, please use [XMLHttpRequest Adapter](/tutorial/request-adapter/alova-adapter-xhr) or [axios Adapter](/tutorial/request-adapter/alova-adapter-axios). +Due to the limitation of fetch api, the one provided by alova `alova/fetch` adapter does not support upload progress. If you need upload progress, please use [XMLHttpRequest adapter](/tutorial/request-adapter/alova-adapter-xhr) or [axios adapter](/tutorial/request-adapter/alova-adapter-axios). -You can also write your own request adapter, see [Writing Request Adapter](/tutorial/custom/custom-http-adapter) for details. +You can also write your own request adapter, see [Writing a request adapter](/tutorial/custom/custom-http-adapter) for details. ::: -**Upload/Download Status Type** +**Upload/download progress type** ```typescript type Progress = { diff --git a/docs/tutorial/02-getting-started/04-alova.md b/docs/tutorial/02-getting-started/04-alova.md index 8a6456f6d..6495b6ed4 100644 --- a/docs/tutorial/02-getting-started/04-alova.md +++ b/docs/tutorial/02-getting-started/04-alova.md @@ -54,7 +54,7 @@ const alovaInstance = createAlova({ ## Request adapter -In the previous chapter we have configured the `GlobalFetch` request adapter, which will be used to send requests initiated by this alova instance. In fact, we also provide various request adapters for different JS environments. +In the previous chapter we have configured the `alova/fetch` request adapter, which will be used to send requests initiated by this alova instance. In fact, we also provide various request adapters for different JS environments. - [Mock request adapter](/tutorial/request-adapter/alova-mock) - [XMLHttpRequest Adapter](/tutorial/request-adapter/alova-adapter-xhr) @@ -64,4 +64,16 @@ In the previous chapter we have configured the `GlobalFetch` request adapter, wh ## Global response cache -You can also set the response cache globally, which we will explain in detail in the [Response Cache](/tutorial/cache/mode) chapter later. +You can also set the response cache globally: + +```ts +const alovaInstance = createAlova({ + // ... + cacheFor: { + GET: 0, // Close all GET caches + POST: 60 * 60 * 1000 // Set all POST caches for 1 hour + } +}); +``` + +For more details, please refer to [Response Cache](/tutorial/cache/mode) diff --git a/docs/tutorial/02-getting-started/05-global-interceptor.md b/docs/tutorial/02-getting-started/05-global-interceptor.md index fbb8d9e6b..bbf902e2f 100644 --- a/docs/tutorial/02-getting-started/05-global-interceptor.md +++ b/docs/tutorial/02-getting-started/05-global-interceptor.md @@ -1,137 +1,136 @@ ---- -title: Global Interceptor -sidebar_position: 50 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -## Global request interceptor - -Usually, we need to use the same configuration for all requests, such as adding token and timestamp to the request header. At this time, we can set a global request interceptor, which will be triggered before all requests. We can set this interceptor Set request parameters uniformly. - -```mermaid -flowchart LR - R1[Request 1] --> beforeRequest - R2[Request 2] --> beforeRequest - R3[Request 3] --> beforeRequest - RN[Request N] --> beforeRequest - beforeRequest --> S1[Send request] -``` - -```javascript -const alovaInstance = createAlova({ - // ... - //The function parameter is a method instance, including request data such as url, params, data, headers, etc. - // You can modify these data freely - // highlight-start - beforeRequest(method) { - // Suppose we need to add token to the request header - method.config.headers.token = 'token'; - } - // highlight-end -}); -``` - -You can also set beforeRequest as an asynchronous function. - -```javascript -const alovaInstance = createAlova({ - // ... - // highlight-start - async beforeRequest(method) { - //Perform some asynchronous tasks - // ... - } - // highlight-end -}); -``` - -## Global response interceptor - -When we want to uniformly parse response data, uniformly handle errors, and uniformly handle request completion, we can specify a global response interceptor when creating an alova instance. The response interceptor includes an interceptor for successful requests and an interceptor for failed requests. and request completion interceptors. - -```mermaid -flowchart LR - classDef error fill:#f96,stroke:#f00,stroke-width:2px; - - R1[Request 1 successful] --> responded.onSuccess - R2[Request 2 successful] --> responded.onSuccess - RN[Request N successful] --> responded.onSuccess - R4[Request 4 failed]:::error --> responded.onError:::error - R5[Request M failed]:::error --> responded.onError:::error - responded.onSuccess --> responded.onComplete - responded.onError --> responded.onComplete -``` - -```javascript -const alovaInstance = createAlova({ - // ... - //Use two items of the array to specify the interceptor for successful request and the interceptor for failed request respectively. - responded: { - // highlight-start - // Interceptor for successful request - // When using the GlobalFetch request adapter, the first parameter receives the Response object - // The second parameter is the method instance of the current request. You can use it to synchronize the configuration information before and after the request. - onSuccess: async (response, method) => { - if (response.status >= 400) { - throw new Error(response.statusText); - } - const json = await response.json(); - if (json.code !== 200) { - // This request will throw an error when an error is thrown or a Promise instance with reject status is returned. - throw new Error(json.message); - } - - //The parsed response data will be passed to the transformData hook function of the method instance. These functions will be explained later. - return json.data; - }, - // highlight-end - - // highlight-start - // Interceptor for request failure - // This interceptor will be entered when a request error occurs. - // The second parameter is the method instance of the current request. You can use it to synchronize the configuration information before and after the request. - onError: (err, method) => { - alert(error.message); - }, - // highlight-end - - // highlight-start - //Interceptor for request completion - // When you need to execute logic whether the request succeeds, fails, or hits the cache, you can specify a global `onComplete` interceptor when creating an alova instance, such as turning off the request loading state. - // Receive the method instance of the current request - onComplete: async method => { - // Process request completion logic - } - // highlight-end - } -}); -``` - -If you do not need to set an interceptor for request failure or completion, you can directly pass in the interceptor function for successful request, instead of setting a callback through an object. - -```javascript -const alovaInstance = createAlova({ - // ... - // highlight-start - async responded(response, method) { - // Interceptor for successful request - } - // highlight-end -}); -``` - -:::info Interceptor triggering instructions - -When you use `GlobalFetch` to request the adapter, due to the characteristics of `window.fetch`, the `onError` interceptor will only be triggered when the connection times out or the connection is aborted. In other cases, the `onSuccess` interceptor will be triggered. [For details, please Check here](https://developer.mozilla.org/docs/Web/API/fetch) - -::: - -:::warning Special attention - -1. `onSuccess`, `onError` and `onComplete` can be set as synchronous functions and asynchronous functions. -2. The `onError` callback is a capture function for request errors. An error thrown in `onSuccess` will not trigger `onError`. When an error is caught but no error is thrown or a Promise instance that returns reject status is used, the request will be considered successful and no response data will be obtained. -3. In 2.0.x and previous versions, `responded` was incorrectly spelled as `responsed`. In 2.1.0, the two have been made compatible. It is recommended to use `responded` instead of `responsed` in subsequent versions. . - -::: +--- +title: Global Interceptor +sidebar_position: 50 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## Global request interceptor + +Usually, we need to use the same configuration for all requests, such as adding token and timestamp to the request header. At this time, we can set a global request interceptor, which will be triggered before all requests. We can set this interceptor Set request parameters uniformly. + +```mermaid +flowchart LR + R1[Request 1] --> beforeRequest + R2[Request 2] --> beforeRequest + R3[Request 3] --> beforeRequest + RN[Request N] --> beforeRequest + beforeRequest --> S1[Send request] +``` + +```javascript +const alovaInstance = createAlova({ + // ... + //The function parameter is a method instance, including request data such as url, params, data, headers, etc. + // You can modify these data freely + // highlight-start + beforeRequest(method) { + // Suppose we need to add token to the request header + method.config.headers.token = 'token'; + } + // highlight-end +}); +``` + +You can also set beforeRequest as an asynchronous function. + +```javascript +const alovaInstance = createAlova({ + // ... + // highlight-start + async beforeRequest(method) { + //Perform some asynchronous tasks + // ... + } + // highlight-end +}); +``` + +## Global response interceptor + +When we want to uniformly parse response data, uniformly handle errors, and uniformly handle request completion, we can specify a global response interceptor when creating an alova instance. The response interceptor includes an interceptor for successful requests and an interceptor for failed requests. and request completion interceptors. + +```mermaid +flowchart LR + classDef error fill:#f96,stroke:#f00,stroke-width:2px; + + R1[Request 1 successful] --> responded.onSuccess + R2[Request 2 successful] --> responded.onSuccess + RN[Request N successful] --> responded.onSuccess + R4[Request 4 failed]:::error --> responded.onError:::error + R5[Request M failed]:::error --> responded.onError:::error + responded.onSuccess --> responded.onComplete + responded.onError --> responded.onComplete +``` + +```javascript +const alovaInstance = createAlova({ + // ... + //Use two items of the array to specify the interceptor for successful request and the interceptor for failed request respectively. + responded: { + // highlight-start + // Interceptor for successful request + // When using the GlobalFetch request adapter, the first parameter receives the Response object + // The second parameter is the method instance of the current request. You can use it to synchronize the configuration information before and after the request. + onSuccess: async (response, method) => { + if (response.status >= 400) { + throw new Error(response.statusText); + } + const json = await response.json(); + if (json.code !== 200) { + // This request will throw an error when an error is thrown or a Promise instance with reject status is returned. + throw new Error(json.message); + } + + //The parsed response data will be passed to the transformData hook function of the method instance. These functions will be explained later. + return json.data; + }, + // highlight-end + + // highlight-start + // Interceptor for request failure + // This interceptor will be entered when a request error occurs. + // The second parameter is the method instance of the current request. You can use it to synchronize the configuration information before and after the request. + onError: (err, method) => { + alert(error.message); + }, + // highlight-end + + // highlight-start + //Interceptor for request completion + // When you need to execute logic whether the request succeeds, fails, or hits the cache, you can specify a global `onComplete` interceptor when creating an alova instance, such as turning off the request loading state. + // Receive the method instance of the current request + onComplete: async method => { + // Process request completion logic + } + // highlight-end + } +}); +``` + +If you do not need to set an interceptor for request failure or completion, you can directly pass in the interceptor function for successful request, instead of setting a callback through an object. + +```javascript +const alovaInstance = createAlova({ + // ... + // highlight-start + async responded(response, method) { + // Interceptor for successful request + } + // highlight-end +}); +``` + +:::info Interceptor triggering instructions + +When you use `alova/fetch` to request the adapter, due to the characteristics of `window.fetch`, the `onError` interceptor will only be triggered when the connection times out or the connection is aborted. In other cases, the `onSuccess` interceptor will be triggered. [For details, please Check here](https://developer.mozilla.org/docs/Web/API/fetch) + +::: + +:::warning Special attention + +1. `onSuccess`, `onError` and `onComplete` can be set as synchronous functions and asynchronous functions. +2. The `onError` callback is a capture function for request errors. An error thrown in `onSuccess` will not trigger `onError`. When an error is caught but no error is thrown or a Promise instance that returns reject status is used, the request will be considered successful and no response data will be obtained. + +::: diff --git a/docs/tutorial/02-getting-started/06-method-metadata.md b/docs/tutorial/02-getting-started/06-method-metadata.md index 995782ccb..27d9074ab 100644 --- a/docs/tutorial/02-getting-started/06-method-metadata.md +++ b/docs/tutorial/02-getting-started/06-method-metadata.md @@ -3,12 +3,6 @@ title: Method Metadata sidebar_position: 60 --- -:::info version requirements - -v2.7.0+ - -::: - Method instances run through the entire request life cycle of alova, and there will be a large number of different method instances in the project. Sometimes we need to add additional information to specific method instances to facilitate their identification or additional information transfer. Wait, at this point, we need to use method metadata. ## Use metadata to identify identities @@ -56,7 +50,7 @@ const loginAPI = (username, password) => { }; ``` -**[2.18.0+]** And you can also directly define metadata in config +And you can also directly define metadata in config ```javascript const loginAPI = (username, password) => { diff --git a/docs/tutorial/02-getting-started/07-combine-framework.md b/docs/tutorial/02-getting-started/07-combine-framework.md new file mode 100644 index 000000000..51d25ac6a --- /dev/null +++ b/docs/tutorial/02-getting-started/07-combine-framework.md @@ -0,0 +1,284 @@ +--- +title: Combine UI framework +sidebar_position: 70 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import CodeBlock from '@theme/CodeBlock'; +import EmbedSandpack from "@site/src/components/EmbedSandpack"; + +import useRequestVue from '!!raw-loader!@site/codesandbox@3/01-getting-started/07-combine-framework/vueComposition-useRequest.en.vue'; +import useRequestReact from '!!raw-loader!@site/codesandbox@3/01-getting-started/07-combine-framework/react-useRequest.en.jsx'; +import useRequestSvelte from '!!raw-loader!@site/codesandbox@3/01-getting-started/07-combine-framework/svelte-useRequest.en.svelte'; + +Next, we will learn how to use it in conjunction with the client UI framework, which can allow alova to exert its true power. When used in the UI framework, not only can alova automatically manage the responsive request status, but also automatically control when the request should be sent through certain rules. + +`alova` provides 10+ client request strategies, which help you implement complex requests in a simple and elegant way. Let's continue to look down! + +## Set statesHook + +Before using the request strategy, we need to set the corresponding statesHook on the alova instance. It must correspond to the UI framework used by the project. This is very important. It will tell alova that it should create responsive states for the corresponding UI framework. Currently, the following frameworks are supported: + + + + + +```js +import { createAlova } from 'alova'; +import VueHook from 'alova/vue'; + +export const alovaInstance = createAlova({ + // ... + // highlight-start + statesHook: VueHook + // highlight-end +}); +``` + + + + + +```js +import { createAlova } from 'alova'; +import ReactHook from 'alova/react'; + +export const alovaInstance = createAlova({ + // ... + // highlight-start + statesHook: ReactHook + // highlight-end +}); +``` + + + + +```js +import { createAlova } from 'alova'; +import SvelteHook from 'alova/svelte'; + +export const alovaInstance = createAlova({ + // ... + // highlight-start + statesHook: SvelteHook + // highlight-end +}); +``` + + + + +## Automatically manage request status + +`useRequest` is our most commonly used request strategy. It can help us create and maintain responsive states of requests, such as `loading/data/error`, etc. You can use these responsive states directly in the view. When they change, the view will also change accordingly. + +useRequest means sending a request. By default, a request will be sent when called. + + + + + + + + + + + + + + +{useRequestSvelte} + + + + +[When to use useRequest and when to send a request via `await alovaInstance.Get`](/tutorial/best-practice/skills). + +:::warning useHook usage specification + +Please note that `useRequest` can only be used to send requests within a component. Outside a component, you can send requests directly through a method instance, and the use of `useRequest` must comply with the use hook usage rules, that is, it can only be called at the outermost level of a function. + +**❌❌❌ It is not recommended to call it in a loop, conditional judgment, or sub-function**. For example, the following example of use in a click callback, when used in a callback function, although the request can be initiated normally, the responsive data returned by the use hook cannot be used in the view, and the same is true for loops and conditional judgments. + +```javascript +// ❌ bad +const handleClick = () => { + const { loading, data } = useRequest(getter); +}; + +// ------- +// ✅ good +const { loading, data, send } = useRequest(getter, { + immediate: false +}); +const handleClick = () => { + send(); +}; +``` + +::: + +## Submit data + +When you need to submit a new todo item, you can first turn off the default send request, switch to manual trigger request, and receive the `send` function in useRequest for manual request sending. The `send` function will return a Promise instance with response data, which will be changed to resolve state after the request response. + +At this time, in order to receive the parameters passed in by the `send` function, you can set the first parameter of `useRequest` to a function, which we call **method handler**. + +```javascript +const { + // ... + // Function for manual sender request, send request after calling + send: addTodo + + // Parameters of send function will be received here +} = useRequest(newTodo => alovaInstance.Post('/todo', newTodo), { + // When immediate is false, it is not sent by default + immediate: false +}); + +// Send request manually +const handleAddTodo = () => { + const newTodo = { + title: 'New todo item', + time: new Date().toLocaleString() + }; + // The send function returns a Promise object, which can receive response data + addTodo(newTodo) + .then(result => { + console.log('Add todo item successfully, response data is:', result); + }) + .catch(error => { + console.log('Add todo item failed, error message is:', error); + }); +}; +``` + +The `send` function allows you to freely repeat requests. + +> In react, the send function uses the `useCallback` package, and it is not restricted by the closure trap. You can use it directly in the event without worrying about performance problems. + +## Process Response + +After the request is completed, the response data will be processed through multiple processes before the final data is obtained at the location where the request was sent. The process is as follows: + +```mermaid +flowchart LR + classDef condition fill:#a8bcff + + R1[Response successfully] --> global.onSuccess + global.onSuccess --> global.onComplete + global.onSuccess --> throw{Is an error thrown? }:::condition + throw -->|No| method.transformData + method.transformData --> useHook.onSuccess + throw -->|Yes| useHook.onError + + method.transformData --> throw2{Is an error thrown? }:::condition + throw2 -->|No| useHook.onSuccess + throw2 -->|Yes| useHook.onError + + useHook.onSuccess --> throw3{Throw an error? }:::condition + throw3 -->|Yes| useHook.onError + + R2[Response Error] --> global.onError + global.onError --> global.onComplete + global.onError --> throw4{Is an error thrown? }:::condition + throw4 -->|Yes| useHook.onError + throw4 -->|No| method.transformData +``` + +When no error is thrown, the next node receives the return value of the previous node. + +### Transform response data + +In [method detailed explanation](/tutorial/getting-started/method), we have already learned about `transformData`, which is also very useful when used in useHook. It allows useHook's data to receive the transformed data without transform again. + +```javascript +const todoListGetter = alovaInstance.Get('/todo/list', { + // The function accepts raw data and response header objects, and requires the transformed data to be returned, which will be assigned to the data state. + // Note: rawData is the data filtered by the global response interceptor (if it is set). For the configuration of the response interceptor, please refer to the [Setting the Global Response Interceptor] chapter. + transformData(rawData, headers) { + return rawData.list.map(item => ({ + ...item, + statusText: item.done ? 'Completed' : 'In progress' + }); + } +}); +``` + +```javascript +const { data } = useRequest(todoListGetter); +const { data } = useWatcher(() => todoListGetter, [userInfo]); +``` + +The data value will receive the transformed data format. + +```typescript +type data = { + // ... + statusText: 'Completed' | 'In progress'; +}[]; +``` + +:::warning note + +When used in usehooks, throwing an error in `transformData` will also trigger `onError`; + +::: + +### Bind response callback + +If you need to set a request callback, you can also receive the callback setting function in the return parameter of useHooks, as follows: + +```javascript +const { + // ... + + //Successful callback binding + onSuccess, + + // Failure callback binding + onError, + + // Complete the callback binding, the callback will be called on success or failure + onComplete +} = useRequest(todoListGetter); // Also applicable to useWatcher +onSuccess(event => { + console.log('The request was successful, the response data is:', event.data); + console.log('The method instance of this request is:', event.method); + console.log('Whether the response data comes from the cache:', event.fromCache); +}); +onError(event => { + console.log('The request failed, the error message is:', event.error); + console.log('The method instance of this request is:', event.method); +}); +onComplete(event => { + // event.status is success when it succeeds and error when it fails. + console.log('The request is completed, the status is: ', event.status); + console.log('The method instance of this request is:', event.method); + console.log('Whether the response data comes from the cache:', event.fromCache); + if (event.data) { + console.log('Request data:', event.data); + } else if (event.error) { + console.log('Error message:', event.error); + } +}); +``` + +:::note Hint + +Throwing an error in `onSuccess` will trigger `onError`. + +::: + +## End + +The above is the basic use of our most commonly used `useRequest`. Other commonly used request strategies include: + +1. useWatcher: monitor data changes and automatically request +2. useForm: form data submission and management +3. useAutoRequest: automatically request according to rules such as timed polling, browser focus, network reconnection, etc. +4. ... + +For complete usage or other client request strategies, please move to [Client Strategy](/xxx) to view all client request strategies provided by alova. diff --git a/docs/tutorial/02-getting-started/08-server.md b/docs/tutorial/02-getting-started/08-server.md new file mode 100644 index 000000000..fe8947d9f --- /dev/null +++ b/docs/tutorial/02-getting-started/08-server.md @@ -0,0 +1,150 @@ +--- +title: Server Usage +sidebar_position: 80 +--- + +As mentioned in the previous [Quick Start](/tutorial/getting-started/quick-start), alova can be used in server-side environments such as `nodejs/deno/bun`. You can use all functions except client request strategies on the server. In addition, alova also provides good support for server-side environments. + +```js +import { createAlova } from 'alova'; +import fetchAdapter from 'alova/fetch'; + +const alovaInstance = createAlova({ +requestAdapter: fetchAdapter() +}); + +alovaInstance.Get(...); +alovaInstance.Post(...); +``` + +## Server hooks + +On the server, we also have different request scenarios, such as request retries, calling APIs to send verification codes, etc., which we call `Server hooks`. They are decorated functions of method instances, and multiple `Server hooks` can be used in combination. + +The following is an example that combines `retry` and `sendCaptcha`, which implements sending verification codes by retrying after failure: + +```js +const { retry, sendCaptcha } = require('alova/server'); + +const email = 'xxx@xxx.com'; +// Create a method instance for sending verification codes +const captchaMethod = alovaInstance.Post('/api/captcha', { + email, + content: 'captcha content' +}); + +// Use retry hook to wrap captchaMethod +const retringMethod = retry(captchaMethod, { + retry: 3, + backoff: { + delay: 2000 + } +}); +// Use sendCaptcha hook to wrap retringMethod, and send the request and get the response result through await +const result = await sendCaptcha(retringMethod, { + key: email +}); +``` + +You can also use multiple `server hooks` to wrap method instances directly. + +```ts +const result = await sendCaptcha( + retry( + alovaInstance.Post('/api/captcha', { + email, + content: 'captcha content' + }), + { + retry: 3, + backoff: { + delay: 2000 + } + } + ), + { + key: email + } +); +``` + +> For more `Server hooks`, please visit [Server Strategy](/xxx). + +## Multi-level Cache + +alova provides a complete and simple cache function. Not only that, it also supports multi-level cache to provide the fastest request experience for your server application. You can freely choose to use single-level cache or multi-level cache. Their operating mechanisms are as follows: + +```mermaid +flowchart LR +A[User request] --> B{Check L1 cache} +B -->|Hit| C[Return data] +B -->|Miss| D{Check L2 cache} +D -->|Hit| E[Update L1 cache] +E --> C +D -->|Miss| F[Request API interface] +F --> G[Update L2 cache] +G --> E +C --> H[End] + +style F stroke-width:8px +``` + +Some application scenarios are as follows: + +1. High access frequency and low latency requirements: such as popular news and product details, which can further reduce network overhead and maintain faster response when the network is unstable. +2. Reduce the pressure on downstream servers, such as services with peak access periods, and the upper-level cache can effectively reduce the pressure on the backend database and microservices. +3. Integrate data merging and processing of multiple downstream servers. Multiple serial requests may lead to longer response time and may consume performance due to complex data conversion. The converted data can be cached. +4. API rate limit and billing. Weather forecast service API updates weather information every hour, geographic location data API, etc. + +The following is an example of using an inter-process memory sharing adapter plus lru cache as the first-level cache and redis as the second-level cache: + +```js +const { createPSCAdapter, NodeSyncAdapter } = require('@alova/psc'); +const { LRUCache } = require('lru-cache'); +const { createRedisAdapter } = require('@alova/redis'); + +function lRUCache(options = {}) { + const cache = new LRUCache(options); + return { + set(key, value) { + return cache.set(key, value); + }, + + get(key) { + return cache.get(key); + }, + + remove(key) { + return cache.delete(key); + }, + + clear() { + return cache.clear(); + } + }; +} + +const alovaInstance = createAlova({ + baseURL: 'https://api.alovajs.dev', + + // Inter-process shared cache adapter + l1Cache: createPSCAdapter( + NodeSyncAdapter(), + lRUCache({ + max: 1000, + ttl: 1000 * 60 * 10 + }) + ), + + // redis cache adapter + l2Cache: createRedisAdapter({ + host: 'localhost', + port: 6379, + username: 'default', + password: 'my-top-secret', + db: 0 + }) +}); +``` + +> For a deeper understanding of response caching, please visit [In-depth Response Cache](/xxx). diff --git a/docs/tutorial/02-getting-started/09-plugin-integration.md b/docs/tutorial/02-getting-started/09-plugin-integration.md new file mode 100644 index 000000000..65da6b1fd --- /dev/null +++ b/docs/tutorial/02-getting-started/09-plugin-integration.md @@ -0,0 +1,93 @@ +--- +title: Extension integration +sidebar_position: 90 +--- + +Integrating Alova's editor extension can make it more powerful. + +1. Automatically generate request code and response data types, and experience smart prompts for interface data in js projects. +2. Embed api documents in the code to experience the effect of checking and using APIs. +3. Update api regularly and actively notify front-end development, no longer relying on server-side developers to notify. + +Install VS Code extension + +> Automatically generate support for swagger-v2 and openapi-v3 specifications. + +## Configuration + +When using the extension, you need to specify the input source and output directory from the openapi file, etc. Create `alova.config.js` in the project root directory. The specific configuration is as follows: + +```js +// alova.config.js +module.exports = { + // API generation setting array, each item represents an automatically generated rule, including the generated input and output directories, standard file addresses, etc. + generator: [ + // Server 1 + { + // Input parameter 1: openapi json file url address + input: 'http://localhost:3000/openapi.json', + + // Input parameter 2: local address with the current project as the relative directory + // input: 'openapi/api.json' + + // Input parameter 3: When there is no direct reference to the openapi file, it is a document address, and the document type must be specified with the platform parameter + // input: 'http://192.168.5.123:8080' + + // (Optional) platform is a platform that supports openapi. Currently only swagger is supported. The default is empty + // When this parameter is specified, the input field only needs to specify the address of the document without specifying the openapi file + platform: 'swagger', + + // Output path of interface file and type file. Multiple generators cannot have the same address, otherwise the generated code will overwrite each other + output: 'src/api', + + // (Optional) Specify the mediaType of the generated response data. Use this data type to generate the response ts format of the 200 status code. The default is application/json + responseMediaType: 'application/json', + + // (Optional) Specify the bodyMediaType of the generated request body data. Use this data type to generate the ts format of the request body. The default is application/json + bodyMediaType: 'application/json', + + /** + * (Optional) The type of generated code. The optional values ​​are auto/ts/typescript/module/commonjs. The default is auto. The type of the current project will be determined by certain rules. If the generation is incorrect, you can also customize the specified type: + * ts/typescript: The same meaning, indicating the generation of ts type files + * module: Generate esModule specification files + * commonjs: Indicates the generation of commonjs specification files + */ + type: 'auto', + + /** + * (Optional) Filter or convert the generated api interface function, return a new apiDescriptor to generate the api call function, if this function is not specified, the apiDescripor object is not converted + */ + handleApi: apiDescriptor => { + // Returning a falsy value means filtering this api + if (!apiDescriptor.path.startWith('/user')) { + return; + } + + apiDescriptor.parameter = apiDescriptor.parameter.filter( + param => param.in === 'header' && param.name === 'token' + ); + delete apiDescriptor.requestBody.id; + apiDescriptor.url = apiDescriptor.url.replace('/user', ''); + return apiDescriptor; + } + }, + + // Server 2 + { + // ... + } + ], + + // (Optional) Whether to automatically update the interface, enabled by default, checked every 5 minutes, disabled when false + autoUpdate: true + + /* You can also configure more detailed parameters + autoUpdate: { + // Update when the editor is opened, false by default + launchEditor: true, + // Automatic update interval, in milliseconds + interval: 5 * 60 * 1000 + } + */ +}; +``` diff --git a/docs/tutorial/02-getting-started/10-congratulations.md b/docs/tutorial/02-getting-started/10-congratulations.md new file mode 100644 index 000000000..b9523c239 --- /dev/null +++ b/docs/tutorial/02-getting-started/10-congratulations.md @@ -0,0 +1,26 @@ +--- +title: Conclusion +sidebar_position: 100 +--- + +import NavCard from '@site/src/components/NavCard'; + +🎉 Congratulations! You have completed the basic use of alova. So far, you can use alova to meet your daily project development. You can freely choose the next learning content according to your own interests. + + diff --git a/docs/tutorial/02-getting-started/README.md b/docs/tutorial/02-getting-started/README.md index 6d845e06c..17ece98ed 100644 --- a/docs/tutorial/02-getting-started/README.md +++ b/docs/tutorial/02-getting-started/README.md @@ -6,21 +6,25 @@ import Link from '@docusaurus/Link'; import NavCard from '@site/src/components/NavCard'; import SupportList from '@site/src/components/SupportList'; -alova is a lightweight request strategy library. It provides a complete set of solutions to deal with complex request scenarios. We call it **Request Strategy**. It only takes one line of code to quickly implement various complex request logics. , not only can help you improve development efficiency, but also help you improve the operating efficiency of the App and reduce the pressure on the server. +alova.js is an innovative next-generation request tool that can help you save most of the work in request, solve the problem of front-end and back-end collaboration, and make network requests very simple. Let's see how alova can help you simplify your work. -Here is the simplest request example: +![](/img/overview_flow_en.png) -```javascript -const response = await alova.Get('/api/user'); -``` +It can simplify API consumption from 7 steps to 1 step. You only need to choose the API to use. + +## How to do it? + +### Request strategy -Ordinary? Let's look at another example of automatically managing request status. **loading, error, and data are responsive data**. In UI frameworks such as react, vue, svelte, etc., they can be bound directly in the view and will be processed according to the request. State automatically maintains this responsive data. +In actual projects, front-end requests always need to consider when to make requests, when not to make requests, how to process response data, etc. according to different scenarios to meet the performance and performance improvement of the project. This will lead to an increase in the developer's time cost and code maintenance cost. In alova, a complete set of solutions for complex request scenarios is provided, which we call **request strategy**. Only one line of code can quickly implement various complex request logics, which can not only help you improve development efficiency, but also help you improve the running efficiency of the App and reduce the pressure on the server. + +For example, `useRequest` can automatically manage the request status, **`loading/error/data` is responsive data**, which can be directly bound in the view in UI frameworks such as react, vue, and svelte, and these responsive data will be automatically maintained according to the request status. ```javascript const { loading, error, data } = useRequest(alova.Get('/api/user')); ``` -The following is an example of a paging request strategy that automatically triggers requests with different parameters when page, pageSize, etc. change. +Another paging request strategy, **when `page/pageSize` changes, it will automatically trigger requests with different parameters**. ```javascript const { loading, error, data, page, pageSize, total } = usePagination((page, size) => @@ -32,55 +36,37 @@ const { loading, error, data, page, pageSize, total } = usePagination((page, siz alova provides 10+ request strategy modules based on the [RSM](/tutorial/others/RSM) specification, which are implemented in the form of useHook. -## Core useHook - -useRequest -useWatcher -useFetcher - -## Scenario-based request strategy +### Alova editor extension -usePagination -useSQRequest -useForm -TokenAuthentication -useAutoRequest -useCaptcha -actionDelegationMiddleware -useSerialRequest -useSerialWatcher -useRetriableRequest -useUploader -useSSE +Using the alova plugin in vscode can help you automatically generate request code with complete API document annotations and response types. In the past, you needed to query the API documentation first and constantly switch between the API documentation and the editor to write request code. After using the alova plugin, you no longer need to leave the editor, and you can directly use the API while checking in the editor to experience a different API usage experience. -## High flexibility +> For a detailed introduction to the alova plugin, please refer to [Integrated IDE plugin](/tutorial/getting-started/plugin-integration). -Thanks to the high flexibility of alova, you can use it with different request libraries in the following different JS environments (the gray part will be gradually supported in the future). +## Is there any difference? - +Unlike other request libraries, alova aims to make requests very simple and maintain more efficient data interaction. -## Is there any difference? +We consider both developers and App users. For developers, alova provides them with the ultimate user experience, and for application users, they can enjoy the smooth experience brought by alova's high-performance data interaction. -Unlike other request libraries, alova's goal is to make requests simpler and maintain more efficient data interaction. +In addition, let's take a look at the specific features: -We consider both developers and App users. For developers, alova provides them with a simple request API and an out-of-the-box high-performance request strategy module. For application users, they can enjoy Alova's high-performance data interaction brings a smooth experience. +- Similar API design to Axios, which makes the user's learning cost lower; +- High-performance client and server request strategies make the application smoother; +- High flexibility, Alova's adapter allows Alova to work with any UI framework in any JS environment, and provides a unified user experience and perfect code migration; +- 2 cache modes and request sharing mechanisms to improve request performance and reduce server pressure; +- High aggregation organization of API code, each API's request parameters, cache behavior, response data conversion, etc. will be gathered in the same code block, which is a great advantage for managing a large number of APIs; -In addition, let’s take a look at the specific features: +:::info comparison -- API design similar to axios, allowing users to learn at a lower cost; -- 10+ out-of-the-box high-performance request strategies to make applications smoother; -- alova is lightweight, only 4kb+, which is 30%+ of axios; -- High flexibility, alova's adapter allows alova to be used in any js environment and with any UI framework (the built-in supported UI framework is `vue/react/svelte`), and provides a unified experience and perfect code migrate; -- 3 caching modes and request sharing mechanism to improve request performance and reduce server pressure; -- Highly aggregated organization of API code. The request parameters, cache behavior, response data conversion, etc. of each API will be gathered in the same code block, which has great advantages for managing a large number of APIs; +You can also check [Comparison with other request libraries](/tutorial/others/comparison) to learn more about the differences of Alova. -In [alova's future](/tutorial/others/future), further request simplification will be implemented. +::: -:::info compared to other request libraries +## Run in any JS environment -You can also check out [Comparison with other request libraries](/tutorial/others/comparison) to learn more about how alova is different. +Not only that, Alova is very flexible, you can use it with different request tools in any of the following JS environments (the gray part will be gradually supported in the future). -::: + ## Online trial diff --git a/docs/tutorial/03-client/01-use-request.md b/docs/tutorial/03-client/01-use-request.md new file mode 100644 index 000000000..d90b8fceb --- /dev/null +++ b/docs/tutorial/03-client/01-use-request.md @@ -0,0 +1,227 @@ +--- +title: Auto Manage States +sidebar_position: 10 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info Strategy type + +use hook + +::: + +useRequest indicates the sending of a request. By default, a request will be sent when it is called. In enterprise-level projects, it is very important to display the data transmission status in the view. When the page obtains initial data or submits data, useRequest is one of the most commonly used use hooks. + +## Usage + +Its basic usage has been introduced in detail in [Combining UI Framework](/tutorial/getting-started/combine-framework). + +### Set initial data + +`data` defaults to `undefined` before the request is successful, but sometimes we need data to have an initial value before the request is successful. For example, when requesting a list, it usually needs to be initialized to `[]`, otherwise it will cause an error when rendering the view because it cannot be looped. + +```javascript +const { data } = useRequest(todoListGetter, { + // highlight-start + // Initial value of data before request response + initialData: [] + // highlight-end +}); +``` + +You can also set `initialData` to a function to dynamically set the initial value. For example, if you don't want the Loading icon to be displayed every time the app is entered and want to use old data instead, you can return the old data as the initial value, which provides a better experience than Loading. + +```js +const { onSuccess } = useRequest(todoListGetter, { + initialData() { + // Set the last response data + const storedData = localStorage.getItem('placeholder-data'); + return JSON.parse(storedData || '{}'); + + // Also use alova's level2 storage adapter + // return alovaInst.l2cache.get('placeholder-data'); + } +}); +onSuccess(({ data, method }) => { + // Save response data + localStorage.setItem('placeholder-data', JSON.stringify(data)); + + // Also use alova's level2 storage adapter + // alovaInst.l2cache.set('placeholder-data', data); +}); +``` + +### Do not send request immediately + +Set `immediate` to `false` to avoid immediate request. + +```javascript +const { data } = useRequest(todoListGetter, { + // highlight-start + immediate: false + // highlight-end +}); +``` + +### Force request + +Force request refers to a mechanism that bypasses cache check to trigger request sending, which is useful when you need to get the latest data under certain conditions. + +```javascript +useRequest(todoListGetter, { + // highlight-start + force: true + // highlight-end +}); +``` + +It can also be set to a function. When the function return value is `true`, the request is forced. + +```javascript +useRequest(todoListGetter, { + // highlight-start + force: ({ method, sendArgs }) => { + return !!sendArgs[0]; + } + // highlight-end +}); +``` + +### Send request manually + +Set the first parameter of `useRequest` to a function. + +```javascript +const { + // ... + // Function for manual sender request, call to send request + // Parameters of send function will be received here +} = useRequest(newTodo => alovaInstance.Post('/todo', newTodo), { + // When immediate is false, it is not sent by default + immediate: false +}); + +send({ + /* todo data */ +}); +``` + +The `send` function allows you to freely repeat the request. + +> In react, the send function is wrapped with `useCallback`, and it is not restricted by closure traps. You can use it directly in events without worrying about performance issues. + +In `useRequest` and `useWatcher`, we can call the `send` function to manually trigger requests. When the send function triggers a request, you can pass in any number of parameters, which can actually be received in the following 3 locations. + +Calling `send` supports passing in any number of parameters, which can be received in the following 3 locations. + +#### Received in method handler + +When their first parameter is set to a callback function, it can be received, which is usually useful when deleting list items, as follows: + +```javascript +const { send } = useRequest(id => + // id will receive 1 + removeTodoPoster(id) +); +send(1); +``` + +#### Received in event callback function + +Received in the event callback function through `event.sendArgs`, which is an array containing all the parameters of the send function. + +```javascript +const { send, onSuccess, onError, onComplete } = useRequest(newTodo => + alovaInstance.Post('/todo', newTodo) +); +onSuccess(event => { + // sendArgs value is [1] + console.log(event.sendArgs); +}); +onError(event => { + // sendArgs value is [1] + console.log(event.sendArgs); +}); +onComplete(event => { + // sendArgs value is [1] + console.log(event.sendArgs); +}); + +// Send request +send(1); +``` + +#### Received in force function + +force is used to specify whether to penetrate the response cache. The content about response cache will be explained in the [cache mode](/tutorial/cache/mode) later. + +```javascript +const { send } = useRequest(alovaInstance.Get('/todo'), { + force: event => { + return event.sendArgs[0]; + } +}); +send(1); +``` + +### Download and upload progress + +Download and upload progress information can be obtained through `downloading` and `uploading`, and you can display the progress information directly in the view. + +```javascript +const { downloading } = useRequest(alovaInstance.Get('/todo/downloadfile')); +const { uploading } = useRequest(alovaInstance.Get('/todo/uploadingfile')); +``` + +The data format of `downloading` and `uploading` is as follows: + +```ts +export type Progress = { + total: number; + loaded: number; +}; +``` + +### Abort request + +Use useHook to receive `abort` for manual abort request. + +```javascript +const { + // ... + // highlight-start + // abort function is used to interrupt the request + abort + // highlight-end +} = useRequest(todoListGetter); + +abort(); +``` + +### Additional managed states + +Set additional managed states, which can be updated across components. For details, please refer to [Additional managed states](/tutorial/managed-state). + +### Middleware + +The `useRequest` middleware can control almost all behaviors of a request, for example, you can use it to block requests. + +```js +let allowRequest = false; +useRequest(todoListGetter, { + middleware(ctx, next) { + if (!allowRequest) { + return; + } + return next(); + } +}); +``` + +For details, please refer to [In-depth Client-Middleware](/tutorial/middleware). + +## API + +Please refer to [API-useRequest](/api/core-hooks#userequest). diff --git a/docs/tutorial/03-client/02-use-watcher.md b/docs/tutorial/03-client/02-use-watcher.md new file mode 100644 index 000000000..1f49972f1 --- /dev/null +++ b/docs/tutorial/03-client/02-use-watcher.md @@ -0,0 +1,148 @@ +--- +title: Watching Request +sidebar_position: 20 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import EmbedSandpack from "@site/src/components/EmbedSandpack"; +import CodeBlock from '@theme/CodeBlock'; +import useWatcherSearchVue from '!!raw-loader!@site/codesandbox@3/02-client/02-use-watcher/vueComposition-search.en.vue'; +import useWatcherSearchReact from '!!raw-loader!@site/codesandbox@3/02-client/02-use-watcher/react-search.en.jsx'; +import useWatcherSearchSvelte from '!!raw-loader!@site/codesandbox@3/02-client/02-use-watcher/svelte-search.en.svelte'; + +:::info Strategy type + +use hook + +::: + +In some scenarios where you need to re-request as data changes, such as paging, data filtering, fuzzy search, tab bar switching, etc., you can use `useWatcher` to listen to the specified state change and send a request immediately. + +## Example + +Next, let's take the search for todo items as an example and try to change the options in the selection box to see how the todo list changes. + + + + + + + + + + + + + +{useWatcherSearchSvelte} + + + + +## Usage + +:::tip Usage Tips + +useWatcher supports all the features of useRequest. For details, please see [useRequest](/tutorial/client/use-request). The following are the unique uses of useWatcher. + +::: + +### Send request immediately + +Unlike `useRequest`, the `immediate` property of `useWatcher` defaults to `false`. + +```javascript +const { send } = useWatcher(() => getTodoList(currentPage), [currentPage], { + // highlight-start + immediate: true + // highlight-end +}); +send(); +``` + +### Request debounce + +Usually we write debounce code at the level of frequently triggered events. This time we implemented the debounce function at the request level, which means you no longer need to implement debounce yourself in the fuzzy search function, and the usage is also very simple. + +:::info What is debounce? + +Debounce means that after an event is triggered, a function can only be executed once within n seconds. If another event is triggered within n seconds after the event is triggered, the function execution delay time will be recalculated (distinguished from throttling here. Throttling means that the event cannot be triggered again within a period of time after the event is triggered) + +::: + +**Set the debounce for all watching states** + +```javascript +const { loading, data, error } = useWatcher( + () => filterTodoList(keyword, date), + [keyword, date], + { + // highlight-start + // Setting debounce to a number means the debounce time for all watching states in milliseconds + // For example, this means that when one or more of the states keyword and date change, the request will be sent after 500ms + debounce: 500 + // highlight-end + } +); +``` + +**Set the debounce for a single watching state** + +In many scenarios, we only need to debounce a few frequently changing watching states, such as the state change triggered by `onInput` of the text box. This can be done as follows: + +```javascript +const { loading, data, error } = useWatcher( + () => filterTodoList(keyword, date), + [keyword, date], + { + // highlight-start + // Set the debounce time in the order of the array of watching states. 0 or no transmission means no debounce + // The order of the watching states here is [keyword, date], and the debounce array is set to [500, 0], which means that only the keyword is set to debounce + debounce: [500, 0] + // You can also set it as follows: + // debounce: [500], + // highlight-end + } +); +``` + +### Request sequence + +Sometimes when the state watched by `useWatcher` changes continuously and causes continuous requests to be initiated, the latter request gets a response before the previous request, but when the previous request gets a response, it will overwrite the response of the latter request, resulting in a response that does not match the state; for example, if a state `state` changes and request `1` is issued, and then the `state` value is changed before request `1` is responded, and request `2` is issued, if request `1` returns after request `2`, the final response data will remain in request `1`. +Therefore, we designed the `abortLast` parameter, which is used to mark whether to interrupt the last unresponsive request when the next request is sent. The default value is `true`, so that only the last request sent by `useWatcher` is valid. + +```mermaid +sequenceDiagram + participant U as user + participant S as server + U ->> U: watch state + U ->> S: state changes and initiates request 1 + U ->> S: state changes and initiates request 2 + S ->> U: request 2 responds first + S ->> U: request 1 responds later + U ->> U: request 2 response is overwritten +``` + +```javascript +useWatcher( + () => getTodoList($currentPage), + // Array of watched states, these state changes will trigger a request + [state], + { + // highlight-start + abortLast: true // Whether to interrupt the last unresponsive request, the default is true + // highlight-end + } +); +``` + +:::warning Notes + +`abortLast` defaults to `true`. Under normal circumstances, you don't need to pay attention to this parameter. If it is changed to `false`, it may cause problems with state and response mismatch. + +::: + +## API + +Please refer to [API-useWatcher](/api/core-hooks#usewatcher). diff --git a/docs/tutorial/03-client/03-use-pagination.md b/docs/tutorial/03-client/03-use-pagination.md new file mode 100644 index 000000000..5b2f812d2 --- /dev/null +++ b/docs/tutorial/03-client/03-use-pagination.md @@ -0,0 +1,699 @@ +--- +title: Pagination request strategy +sidebar_position: 30 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info stragety type + +use hook + +::: + +> Before using extension hooks, make sure you are familiar with basic usage of alova. + +A hook designed for paging scenarios, which can help you automatically manage paging data, preload data, reduce unnecessary data refresh, improve fluency by 300%, and reduce coding difficulty by 50%\*\*. You can use it in the two paging scenarios of pull-down loading and page number flipping. This hook provides a wealth of features to help your application create better performance and more convenient paging functions. + +## Example + +[page list](/tutorial/example/paginated-list) + +[Pull down to load more](/tutorial/example/load-more) + +## Features + +- ✨ Rich and comprehensive paging status; +- ✨ Rich and comprehensive pagination events; +- ✨ Change page, pageSize to automatically get specified paging data; +- ✨Data caching, no need to repeatedly request list data of the same parameters; +- ✨ Front and back pages are preloaded, no waiting for page turning; +- ✨Search condition monitoring automatically reacquires pages; +- ✨ Support adding, editing and deleting list data; +- ✨ Support refreshing the data of the specified page without reset; +- ✨ Request-level search anti-shake, no need to maintain by yourself; + +## Usage + +### Render list data + + + + +```html + + + +``` + + + + +```jsx +import { queryStudents } from './api.js'; +import { usePagination } from 'alova/client'; + +const App = () => { + const { + // loading state + loading, + + // list data + data, + + // is it the last page + // This parameter can be used to determine whether it needs to be loaded during pull-down loading + isLastPage, + + // The current page number, changing this page number will automatically trigger the request + page, + + // Number of data items per page + pageSize, + + // number of paging pages + pageCount, + + // total amount of data + total, + + // update states + update + } = usePagination( + // Method instance acquisition function, it will receive page and pageSize, and return a Method instance + (page, pageSize) => queryStudents(page, pageSize), + { + // Initial data before the request (data format returned by the interface) + initialData: { + total: 0, + data: [] + }, + initialPage: 1, // initial page number, default is 1 + initialPageSize: 10 // The initial number of data items per page, the default is 10 + } + ); + + // Turn to the previous page, the request will be sent automatically after the page value changes + const handlePrevPage = () => { + update({ + page: page - 1 + }); + }; + + // Turn to the next page, the request will be sent automatically after the page value changes + const handleNextPage = () => { + update({ + page: page + 1 + }); + }; + + // Change the number of pages, the request will be sent automatically after the pageSize value is changed + const handleSetPageSize = () => { + update({ + pageSize: 20 + }); + }; + + return ( +
+ {data.map(item => ( +
+ {item.name} +
+ ))} + + + + There are {pageCount} pages + A total of {total} pieces of data +
+ ); +}; +``` + +
+ + +```html + + +{#each $data as item} +
+ {item.name} +
+{/each} + + + +There are {pageCount} pages +A total of {total} pieces of data +``` + +
+
+ +### Specify pagination data + +The data structure returned by each paging data interface is different, so we need to tell `usePagination` the list data and the total number of items separately, so as to help us manage the paging data. + +Suppose the data format returned by your paging interface is as follows: + +```typescript +interface PaginationData { + totalNumber: number; + list: any[]; +} +``` + +At this point, you need to return the list data and the total number of items in the form of a function. + +```javascript +usePagination((page, pageSize) => queryStudents(page, pageSize), { + //... + // highlight-start + total: response => response.totalNumber, + data: response => response.list + // highlight-end +}); +``` + +If you don't specify the total and data callback functions, they will get data in the following ways by default. + +```javascript +const total = response => response.total; +const data = response => response.data; +``` + +:::warning Caution + +The data callback function must return a list of data, indicating the data set used in paging, and total is mainly used to calculate the current page number. If no number is returned in the total callback function, it will pass whether the number of requested lists is less than the pageSize value To determine whether the current page is the last page, which is generally used for pull-down loading. + +::: + +### Enable append mode + +By default, the original list data will be replaced when the page is turned, and the append mode will append the data of the next page to the bottom of the current list when the page is turned. A common usage scenario is to pull down to load more. + +```javascript +usePagination((page, pageSize) => queryStudents(page, pageSize), { + //... + // highlight-start + append: true + // highlight-end +}); +``` + +### Preload adjacent page data + +In order to provide a better experience for pagination, when the previous and next pages of the current page meet the conditions, it will be automatically preloaded, so that when the user turns the page, the data can be displayed directly without waiting. This is the default behavior. If you don't want to preload the data of adjacent pages, you can turn it off in the following way. + +```javascript +usePagination((page, pageSize) => queryStudents(page, pageSize), { + //... + // highlight-start + preloadPreviousPage: false, // turn off preloading previous page data + preloadNextPage: false // turn off preloading next page data + // highlight-end +}); +``` + +:::warning preloading trigger conditions + +When preloading switch is turned on, the next page will not be loaded blindly. The following two conditions need to be met: + +1. Preloading is based on cache of alova. The method instance used for pagination must enable chache. By default, get requests will have 5 minutes of memory cache. If it is a non-get request or the cache is turned off globally, you also need to set the cache in this Method. In the instance, set `localCache` separately to enable cache. +2. Based on the `total` and `pageSize` parameters, it is determined that there is still data on the next page. + +::: + +In addition to `onSuccess, onError, onComplete` request events, when preloading is triggered, you can also know the preloading status through `fetching`, and you can also listen to preloading request events through `onFetchSuccess, onFetchError, onFetchComplete`. + +```javascript +const { + // preload state + fetching, + + // preload success event binding function + onFetchSuccess, + + // preload error event binding function + onFetchError, + + // Preloading complete event binding function + onFetchComplete +} = usePagination((page, pageSize) => queryStudents(page, pageSize), { + //... +}); +``` + +### Listening filter conditions + +In many cases, the list needs to be filtered by conditions. At this time, the re-request can be triggered through the status monitoring of `usePagination`, which is the same as `useWatcher` provided by alova. + +For example, filter by student name, student grade. + + + + +```html + + + +``` + + + + +```jsx +import { queryStudents } from './api.js'; +import { usePagination } from 'alova/client'; + +const App = () => { + // search condition status + const [studentName, setStudentName] = useState(''); + const [clsName, setClsName] = useState(''); + const { + //... + } = usePagination( + (page, pageSize) => queryStudents(page, pageSize, studentName, clsName), + { + //... + // highlight-start + watchingStates: [studentName, clsName] + // highlight-end + } + ); + + return ( + // highlight-start + setStudentName(target.value)} /> + + // highlight-end + //... + ); +}; +``` + + + + +```html + + + + + + + +``` + + + + +Same as `useWatcher`, you can also implement request debounce by specifying `debounce`, for details, please refer to [useWatcher's debounce parameter setting](/api/core-hooks#usewatcher). + +```javascript +usePagination((page, pageSize) => queryStudents(page, pageSize, studentName, clsName), { + //... + // highlight-start + debounce: 300 // Anti-shake parameters, in milliseconds, can also be set as an array to set the anti-shake time separately for watchingStates + // highlight-end +}); +``` + +It should be noted that `debounce` is achieved by request debounce in [**useWatcher**](/api/core-hooks#usewatcher). **At the end of the monitoring state, there are two hidden monitoring states of page and pageSize, which can also be set by debounce. ** + +For example, when `watchingStates` is set to `[studentName, clsName]`, `[studentName, clsName, page, pageSize]` will be monitored internally, so if you need to set anti-shake for page and pageSize, you can specify ` [0, 0, 500, 500]`. + +### Close initialization request + +By default, `usePagination` will initiate a request during initialization, but you can also use `immediate` to turn it off, and then pass the `send` function, or change `page` or `pageSize`, and `watchingStates`, etc. state to initiate the request. + +```javascript +usePagination((page, pageSize) => queryStudents(page, pageSize, studentName, clsName), { + //... + // highlight-start + immediate: false + // highlight-end +}); +``` + +## List action functions + +usePagination provides a fully functional list action function, which can achieve the same effect as the re-request list without re-requesting the list, which greatly improves the interactive experience of the page. The specific function description continues to look down! + +### Insert list item + +You can use it to insert list items to any position in the list, and it will remove the last item after insertion to ensure the same effect as re-requesting the current page data. + +```typescript +/** + * Insert data + * If no index is passed in, it will be inserted at the front by default + * If a list item is passed in, it will be inserted after the list item, and an error will be thrown if the list item is not in the list data + * @param item insert item + * @param indexOrItem insertion position (index) + */ +declare function insert(item: LD[number], indexOrItem?: number | LD[number]): void; +``` + +The following is an example of returning to the first page and then inserting list items in **non-append mode** (page number flipping scenario): + +```javascript +page.value = 1; +nextTick(() => { + insert(newItem, 0); +}); +``` + +The following is an example of scrolling to the top after inserting a list item in **append mode** (drop-down loading scene): + +```javascript +insert(newItem, 0); +nextTick(() => { + window.scrollTo(0, {}); +}); +``` + +You can also specify the second parameter of `insert` as a list item. When the same reference of this list item is found, the insert item will be inserted after this list item. + +```javascript +insert(newItem, afterItem); +``` + +:::warning Caution + +In order to make the data correct, the insert function call will clear all caches. + +::: + +### Remove list item + +In the case that the next page has a cache, it will use the cache of the next page to add to the end of the list item after removing an item, so as to ensure the same effect as re-requesting the data of the current page. In **append mode** and Behave the same in **non-append mode**. + +```typescript +/** + * Remove data + * If a list item is passed in, the list item will be removed, and an error will be thrown if the list item is not in the list data + * @param position index or list item to remove + */ +declare function remove(position: number | LD[number]): void; +``` + +You can also specify the second parameter of `remove` as a list item, and when the same reference of this list item is found, this list item will be removed. + +But in the following two cases, it will re-initiate the request to refresh the data of the corresponding page: + +1. The next page is not cached +2. The data that exceeds the next page cache list item is continuously called synchronously, and the cache data is not enough to supplement the current page list. + +:::warning Caution + +In order to make the data correct, the remove function call will clear all caches. + +::: + +### Update data items + +Use this function when you want to update list items. + +```typescript +/** + * Replace data + * If the position passed in is a list item, this list item will be replaced, if the list item is not in the list data, an error will be thrown + * @param item replacement item + * @param position replacement position (index) or list item + */ +declare function replace( + item: LD extends any[] ? LD[number] : any, + position: number | LD[number] +): void; +``` + +You can also specify the second parameter of `replace` as a list item, which will be replaced when an identical reference to the list item is found. + +### Refresh the data of the specified page + +When you do not want to update the list items locally after the data operation, but re-request the data on the server side, you can use refresh to refresh the data on any page, without resetting the list data and letting the user start browsing from the first page again. + +```typescript +/** + * Refresh the specified page number data, this function will ignore the cache to force the send request + * If no page number is passed in, the current page will be refreshed + * If a list item is passed in, the page where the list item is located will be refreshed + * @param pageOrItemPage Refreshed page number or list item + */ +declare function refresh(pageOrItemPage?: number | LD[number]): void; +``` + +In append mode, you can specify the parameter of `refresh` as a list item. When the same reference of this list item is found, the data of the page number where this list item is located will be refreshed. + +### Manually update list data + +Use the `update` function to update responsive data, which is similar to [useRequest's update](/tutorial/combine-framework/use-request), the only difference is that when calling `update` to update `data`, the list data is updated, while non-response data. This is useful when manually clearing list data without reissuing the request. + +```typescript +// case list data +update({ + data: [] +}); +``` + +### Reset list + +It will clear all caches and reload the first page. + +```typescript +/** + * Reload the list from the first page and clear the cache + */ +declare function reload(): void; +``` + +## API + +### Hook configuration + +Inherit all configurations of [**useWatcher**](/api/core-hooks#usewatcher). + +| Name | Description | Type | Default | Version | +| ------------------- | --------------------------------------------------------------------- | ------------------------- | -------------------------- | ------- | +| initialPage | initial page number | number | 1 | - | +| initialPageSize | Initial number of data items per page | number | 10 | - | +| watchingStates | state monitoring trigger request, implemented using useWatcher | any[] | [page, pageSize] | - | +| debounce | The debounce parameter of state monitoring, implemented by useWatcher | number \| number[] | - | - | +| append | Whether to enable append mode | boolean | false | - | +| data | Array data specifying pagination | (response: any) => any[] | response => response.data | - | +| total | specify the total amount of data | (response: any) => number | response => response.total | - | +| preloadPreviousPage | whether to preload previous page data | boolean | true | - | +| preloadNextPage | whether to preload next page data | boolean | true | - | + +### Responsive data + +Inherit all responsive data from [**useWatcher**](/api/core-hooks#usewatcher). + +| Name | Description | Type | Version | +| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ------- | +| page | Current page number, determined by initialPage | number | - | +| pageSize | The current number of pages, determined by initialPageSize | number | - | +| data | paging list array data, obtained from data configuration | any[] | - | +| total | The total amount of data, obtained from total configuration, can be empty | number | - | +| pageCount | The total number of pages, calculated from total and pageSize | number | - | +| isLastPage | Whether the current page is the last page, if pageCount has a value, it will be obtained by comparing pageCount and page, otherwise it will be obtained by whether the length of the list data is less than pagSize | number | - | +| fetching | whether data is being preloaded | boolean | - | + +### Action function + +Inherit all action functions of [**useWatcher**](/api/core-hooks#usewatcher). + +| name | description | function parameters | return value | version | +| ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | ------- | +| refresh | Refresh the data of the specified page number, this function will ignore the forced sending request of the cache, in the append mode, the list item can be passed in to indicate the page number where the list item is refreshed | pageOrItemPage: Refreshed page number or list item | - | - | +| insert | Insert a piece of data. If no index is passed in, it will be inserted at the front by default. If a list item is passed in, it will be inserted after the list item. If the list item is not in the list data, an error will be thrown | 1. item: insert item
2. indexOrItem: insert position (index) or list item, default is 0 | - | - | +| remove | Remove a piece of data. When a number is passed in, it means the removed index. When the position is passed in a list item, the list item will be removed. If the list item is not in the list data, an error will be thrown | position : remove position (index) or list item | - | - | +| replace | Replace a piece of data. When the second parameter is passed in a number, it means the replacement index. A negative number means counting from the end. When the position passed in is a list item, the list item will be replaced. If the list item is not in the list data An error will be thrown | 1. item: replacement item
2. position: replacement position (index) or list item, when a negative number is passed in, it means counting from the end | - | - | +| reload | Clear the data and re-request the first page of data | - | - | - | +| update | Update state data, same as alova's use hook, but update list data when updating data field | newFrontStates: new state data object | - | - | + +### Event + +Inherit all events from [**useWatcher**](/api/core-hooks#usewatcher). + +| Name | Description | Callback Parameters | Version | +| --------------- | ---------------------------------------------- | ------------------------------------ | ------- | +| onFetchSuccess | fetch success callback binding function | event: alova success event object | - | +| onFetchError | callback binding function for fetch failure | event: alova failure event object | - | +| onFetchComplete | callback binding function for fetch completion | event: alova completion event object | - | diff --git a/docs/tutorial/03-client/04-use-form.md b/docs/tutorial/03-client/04-use-form.md new file mode 100644 index 000000000..adca77a56 --- /dev/null +++ b/docs/tutorial/03-client/04-use-form.md @@ -0,0 +1,488 @@ +--- +title: Form submiting strategy +sidebar_position: 40 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info strategy type + +use hook + +::: + +> Before using extension hooks, make sure you are familiar with the basic usage of alova. + +A hook designed for form submission. Through this hook, you can easily implement form drafts and multi-page (multi-step) forms. In addition, it also provides common functions such as form reset. + +## Example + +[Form Submission Demo](/tutorial/example/form-hook) + +## Features + +- ✨ draft form; +- ✨ Multi-page (multi-step) forms; +- ✨ Form submission automatically resets data; +- ✨Reset form data manually; + +## Usage + +### Basic usage + +Demonstrates basic use of form hooks. + + + + +```html + + + +``` + + + + +```jsx +import { formSubmit } from './api.js'; +import { useForm } from 'alova/client'; + +const App = () => { + const { + // submit status + loading: submitting, + + // Responsive form data, the content is determined by initialForm + form, + + // submit data function + send: submit, + + // update form item + updateForm, + + // Submit successful callback binding + onSuccess, + + // Submit failure callback binding + onError, + + // Submit completed callback binding + onComplete + } = useForm( + formData => { + // Form data can be converted and submitted here + return formSubmit(formData); + }, + { + // Initialize form data + initialForm: { + name: '', + cls: '1' + } + } + ); + + // submit form data + const handleSubmit = () => { + // Validate form data... + submit(); + }; + + return ( +
+ updateForm({ name: target.value })} + /> + + +
+ ); +}; +``` + +
+ + +```html + + + + + +``` + + +
+ +`useForm` will not request by default, and the request will be sent after calling `send`. At the same time, the callback function of `useForm` will pass in the latest form data. If you need to convert the data before submitting, you can convert it here, or Can be converted in the `formSubmit` function. + +:::warning Caution + +1. `initialForm` is to set the initial form data, `initialData` is to set the initial response data, pay attention to the distinction; +2. `updateForm` is to update the form data, and `update` is to update the response data, pay attention to the distinction; + +::: + +The above example only shows a simple form submission function, there is no difference between ordinary form submissions, but `useForm` also implements more practical functions, let us continue to look down. + +### Submit auto reset form + +Many times, we need to reset the form data after the form is submitted. We always need to manually reassign values one by one when implementing it ourselves, and `useForm` can help us do it automatically. + +```javascript +useForm(submitData, { + //... + // highlight-start + // Set this parameter to true to automatically reset the form data after submission + resetAfterSubmitting: true + // highlight-end +}); +``` + +If you need to manually reset the form data, you can also do it by calling the `reset` function. + +```javascript +const { + // highlight-start + // form reset function + reset + // highlight-end +} = useForm(submitData, { + //... +}); + +// highlight-start +const handleReset = () => { + reset(); +}; +// highlight-end +``` + +### Update form data + +When editing a form, we need to display the data of the original form. At this time, we can use `updateForm` to asynchronously update the form data. + +```javascript +const { + // ... + updateForm +} = useForm(submitData, { + // ... + { + //Initialize form data + initialForm: { + name: '', + cls: '1' + } + } +}); + +// Request form data and update it to the form +const { onSuccess } = useRequest(getData); +onSuccess(({ data }) => { + updateForm({ + name: data.name, + cls: data.cls + }); +}); +``` + +### Form draft + +`useForm` also provides a form draft function, even if the page is refreshed before the data is reset, the form data can be restored. The principle is to use the storage adapter on the alova instance to persist the form data. You only need to set `store` to true when using it. + +```javascript +useForm(submitData, { + //... + // highlight-start + // Turn on persistence to save data. After setting to true, uncommitted data will be persisted in real time + store: true + // highlight-end +}); +``` + +Before the data is persisted, `JSON.stringify` will be called to convert it into a JSON string. By default, the form data will be serialized when it is persisted. `useForm` has built-in `Date` and `RegExp` instances , which will be useful when using timepickers. + +In the form data only involves `Date` and `RegEYou don’t need to do more for xp` instances, but if there are other non-JSON data, such as `moment` instances, we need to customize the serializer, but don’t worry, the custom serializer is very simple, the following will show the settings A `moment` serializer. + +```javascript +import moment from 'moment'; +const momentSerializer = { + // forward is called when serializing + // Need to judge whether it is a moment instance, if it is not the target value, return undefined, indicating that it will not be processed + forward: data => moment.isMoment(data) ? data.valueOf() : undefined, + + // backward is called during deserialization, data is the value returned in forward + backward: timestamp => moment(timestamp); +}; + +useForm( + submitData, + { + store: { + enable: true, + serializers: { + moment: momentSerializer + } + } + } +); +``` + +### Multi-page/multi-step forms + +Many times we encounter situations where form items are divided into multiple pages, or filled in multiple steps, and submitted in a unified manner at the end, such as multi-step user registration, questionnaire filling, etc., and forms with multiple steps may have interdependence Relationship, if realized by itself will bring some trouble. And `useForm` realizes form data sharing, you can get the same form data in different pages or components, which solves the problem of multi-step form data dependence, and does not need to summarize form data when submitting, and can submit directly. + +When using, you need to set the id through `useForm`, and you can share the same form data between different pages with the same id. For example, we have a form that needs to go through 3 steps to fill out the form, and they will go through component A, component B, and component C respectively. + +``` +Component A -> Component B -> Component C +``` + +At this point, we can initialize the form data inside component A: + +```javascript title=Component A +const returnStates = useForm(submitData, { + initialForm: { + step1Input: '', + step2Input: '', + step3Input: '' + }, + // highlight-start + id: 'testForm' + // highlight-end +}); +const { form, send } = returnStates; +``` + +In component B and component C, you can get the shared data by specifing the same id in component A. + +```javascript title=Component B, Component C +const returnStates = useForm(submitData, { + id: 'testForm' +}); +const { form, send } = returnStates; +``` + +The `returnStates` returned by id in components B and C are the same reference as the `returnStates` in component A. You can use the same `form`, or you can call `send` in any component to submit the form data uniformly. + +**additional** + +If your multi-step form is not in a certain order, but in random order according to certain conditions, for example: + +```bash +# possible order 1 +Component B -> Component A -> Component C + +# possible order 2 +Component A -> Component C -> Component B + +# possible order 3 +Component C -> Component A -> Component B + +#... +``` + +In this case, you can set the same `useForm` config as component A in component B and C. + +```javascript title=Component B, Component C +const returnStates = useForm(submitData, { + initialForm: { + step1Input: '', + step2Input: '', + step3Input: '' + }, + id: 'testForm' +}); +``` + +In this way, no matter which component is rendered first, the form with the id of testForm can be initialized, and the subsequent components will first use the initialized form data when encountering the id of testForm, and will not initialize again. This way you can initialize form data inside any component. + +> More detailed multi-step forms can also be experienced and viewed in [Form Submission Demo](/tutorial/example/form-hook). + +### Conditional filter + +`useForm` can also be used in the filtering form used in data filtering scenarios, for example, if you want to search city information by city name, you can set `immediate=true`, it will start querying data at initialization, and then In the operation, call `send` to repeatedly query the data. + +```javascript +const { send: searchData } = useForm(queryCity, { + initialForm: { + cityName: '' + }, + immediate: true +}); +``` + +:::warning Conditional Restrictions + +In conditional filtering scenarios, `useForm` is more suitable for non-paginated list conditional queries. If you need to perform conditional queries in paginated lists, it is recommended to use [Pagination Request Strategy (usePagination)](/tutorial/strategy/usePagination). + +::: + +## API + +### Hook configuration + +Inherit all configurations from [**useRequest**](/api/core-hooks#userequest). + +| Name | Description | Type | Default | Version | +| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- | ------- | ------- | +| initialForm | initial form data | any | - | - | +| id | form id, the data data of the same id is the same reference, which can be used to share the same form data in multi-page forms. Single page form does not need to specify id | string \| number | - | - | +| store | Whether to save data persistently, after setting to true, uncommitted data will be persisted in real time | boolean \| [StoreDetailConfig](#storedetailconfig) \| false | - | +| resetAfterSubmiting | reset data after submission | boolean | false | - | + +### Responsive data + +Inherit all responsive data from [**useRequest**](/api/core-hooks#userequest). + +| Name | Description | Type | Version | +| ---- | ----------------------------------- | ---- | ------- | +| form | form data,determined by initialForm | any | - | + +#### StoreDetailConfig + +| Name | Description | Type | Default | Version | +| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | -------- | ------- | +| enable | Whether to enable persistent data | boolean | required | - | +| serializers | A collection of custom serializers, built-in serializers:
1. The date serializer is used to convert dates
2. The regexp serializer is used to convert regular expressions
Yes Override the built-in serializer by setting the serializer with the same name | Record\ | - | - | + +#### DataSerializer + +| Name | Description | Type | Default | Versionthis | +| -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------- | -------- | ----------- | +| forward | Serialization function, when serializing in forward, it needs to judge whether it is the specified data, and return the converted data, otherwise return undefined or not return | (data: any) => any \| undefined \| void | required | - | +| backward | deserialization function, deserialization data directly | (data: any) => any \| undefined \| void | required | - | + +### Action function + +Inherit all action functions of [**useRequest**](/api/core-hooks#userequest). + +| name | description | function parameters | return value | version | +| ---------- | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------ | ------------ | ------- | +| updateForm | Update one or more form data | newForm: Partial\ \| (oldForm: F) => F)
F is `initialForm` type | - | - | +| reset | Reset to initialized data, if there is persistent data, it will also be cleared | - | - | - | + +### Event + +Inherit all events from [**useRequest**](/api/core-hooks#userequest). + +| Name | Description | Callback Parameters | Version | +| --------- | ----------------------------------------------- | ------------------- | ------- | +| onRestore | Triggered after the persistent data is restored | - | - | diff --git a/docs/tutorial/03-client/05-token-authentication.md b/docs/tutorial/03-client/05-token-authentication.md new file mode 100644 index 000000000..f8099aa73 --- /dev/null +++ b/docs/tutorial/03-client/05-token-authentication.md @@ -0,0 +1,507 @@ +--- +title: Token authentication interceptor +sidebar_position: 50 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info + +Policy type: Interceptor + +Version requirements: v1.3.0+ + +::: + +> Before using extension hooks, make sure you are familiar with the basic use of alova. + +Token authentication interceptor provides unified management of token-based login, logout, token assignment, and token refresh, and supports silent token refresh. + +## Features + +- ✨ Unified maintenance of all codes for token identity authentication, including login, logout, token assignment, token refresh, etc.; +- ✨ Supports verification of token expiration on the client and server, and refreshes the token without any warning; +- ✨ Requests that rely on tokens automatically wait for the token refresh to complete before requesting; +- ✨ Set request ID with metadata; +- ✨ Automatically release visitor requests that do not rely on tokens; + +## Bind Token authentication interceptor + +Token identity authentication is completed through global interceptors, which provide `createClientTokenAuthentication` and `createServerTokenAuthentication` for client- and server-based identity authentication respectively. + +- Client-based identity authentication: means judging whether the token has expired from the client, such as the token expiration time obtained during login; +- Server-based identity authentication: It indicates whether the token has expired based on the status returned from the server. For example, when `status` is 401, it means it has expired; + +### Bind client-based authentication interceptor + +```javascript +import { createClientTokenAuthentication } from 'alova/client'; +import { createAlova } from 'alova'; + +const { onAuthRequired, onResponseRefreshToken } = createClientTokenAuthentication({ + // ... +}); + +const alovaInstance = createAlova({ + // ... + beforeRequest: onAuthRequired(method => { + // ...interceptor before original request + }), + responded: onResponseRefreshToken((response, method) => { + //...original response success interceptor + return response.json(); + }) +}); +``` + +In `onResponseRefreshToken`, you can also bind response error and completion interceptors, which is the same as the original usage. + +```javascript +createAlova({ + // ... + // highlight-start + responded: onResponseRefreshToken({ + onSuccess: (response, method) => { + //...original response success interceptor + }, + onError: (error, method) => { + //...original response error interceptor + }, + onComplete: method => { + //...original response completion interceptor + } + }) + // highlight-end +}); +``` + +If you don't need to set an interceptor, you don't need to pass in the interceptor function. + +```javascript +createAlova({ + //... + // highlight-start + beforeRequest: onAuthRequired(), + responded: onResponseRefreshToken() + // highlight-end +}); +``` + +### Bind server-based authentication interceptor + +Same as client-based usage + +```javascript +import { createServerTokenAuthentication } from 'alova/client'; +import { createAlova } from 'alova'; + +const { onAuthRequired, onResponseRefreshToken } = createServerTokenAuthentication({ + // ... +}); + +const alovaInstance = createAlova({ + // ... + beforeRequest: onAuthRequired(method => { + // ...interceptor before original request + }), + responded: onResponseRefreshToken((response, method) => { + //...original response success interceptor + return response.json(); + }) +}); +``` + +:::warning + +When you use the `GlobalFetch` adapter, you may encounter the problem `TypeError: Failed to execute 'json' on 'Response': body stream already read`. This is because the `body stream` of `Response` can only be accessed once, you can `response.clone().json()` to solve it. + +::: + +## Refresh Token silently on the client + +Just set `refreshToken` and specify whether the token expires, and call the function to refresh the token. When the token refresh is completed, all requests that rely on the token will wait for the token refresh to complete. + +```javascript +createClientTokenAuthentication({ + refreshToken: { + // Triggered before the request, the method parameter will be received and a boolean will be returned to indicate whether the token has expired. + isExpired: method => { + return tokenExpireTime < Date.now(); + }, + + // Triggered when the token expires, trigger the refresh token in this function + handler: async method => { + const { token, refresh_token } = await refreshToken(); + localStorage.setItem('token', token); + localStorage.setItem('refresh_token', refresh_token); + } + } +}); +``` + +In order for the `refreshToken` request to pass smoothly, the `authRole` needs to be identified as `refreshToken` through metadata. + +> For more information about metadata, go to [method metadata](/tutorial/getting-started/method-metadata). + +```javascript +export const refreshToken = () => { + const method = alovaInstance.Get('/refresh_token'); + method.meta = { + authRole: 'refreshToken' + }; + return method; +}; +``` + +## Refresh Token silently on the server side + +The same as refreshing the token silently on the client, just specify whether the token expires and call the function to refresh the token. When the token refresh is completed, all requests that rely on the token will wait for the token refresh to complete. + +### Processed in request success interceptor + +When using `GlobalFetch`, as long as the server returns the response data, the response success interceptor will be triggered. At this time, we need to handle the token refresh in the response success interceptor. + +```javascript +createServerTokenAuthentication({ + refreshTokenOnSuccess: { + // Triggered when responding, the response and method can be obtained, and a boolean is returned to indicate whether the token has expired. + // When the server returns 401, it means the token has expired. + isExpired: (response, method) => { + return response.status === 401; + }, + + // Triggered when the token expires, trigger the refresh token in this function + handler: async (response, method) => { + const { token, refresh_token } = await refreshToken(); + localStorage.setItem('token', token); + localStorage.setItem('refresh_token', refresh_token); + } + } +}); +``` + +### Handled in request error interceptor + +When using the `axios` interceptor, if the server returns a status code other than `200/300`, the response error interceptor will be triggered. At this time, we need to handle the token refresh in the response error interceptor. + +```javascript +createServerTokenAuthentication({ + refreshTokenOnError: { + // Triggered when responding, error and method can be obtained, and boolean is returned to indicate whether the token has expired. + // When the server returns 401, it means the token has expired. + isExpired: (error, method) => { + return error.response.status === 401; + }, + + // Triggered when the token expires, trigger the refresh token in this function + handler: async (error, method) => { + const { token, refresh_token } = await refreshToken(); + localStorage.setItem('token', token); + localStorage.setItem('refresh_token', refresh_token); + } + } +}); +``` + +In order for the `refreshToken` request to pass smoothly, the `authRole` needs to be identified as `refreshToken` through metadata. + +> For more information about metadata, please go to [method metadata](/tutorial/getting-started/method-metadata). + +```javascript +export const refreshToken = () => { + const method = alovaInstance.Get('/refresh_token'); + method.meta = { + authRole: 'refreshToken' + }; + return method; +}; +``` + +## Release visitor request + +Some interfaces do not need to rely on token authentication. We call them "guest requests". At this time, we can set their metadata to `authRole: null` to bypass the front-end interception and allow them to send requests and receive responses smoothly. + +```javascript +export const requestTokenNotRequired = () => { + const method = alovaInstance.Get('/token_not_required'); + method.meta = { + authRole: null + }; + return method; +}; +``` + +## Login interception + +In the identity authentication interceptor, you can also intercept login requests and save login information in the interceptor to achieve the purpose of unified maintenance of identity authentication codes. + +First identify the metadata of the login request as `authRole: 'login'`. + +```javascript +export const login = () => { + const method = alovaInstance.Get('/login'); + method.meta = { + authRole: 'login' + }; + return method; +}; +``` + +Then save the login information in the login interceptor. + +```javascript +createClientTokenAuthentication({ + login(response, method) { + localStorage.setItem('token', response.token); + localStorage.setItem('refresh_token', response.refresh_token); + } +}); +``` + +> The login interceptor usage of `createServerTokenAuthentication` is the same. + +## Assign token + +Usually, we will append token to the request information in `beforeRequest`. The `assignToken` callback function is provided in the Token authentication interceptor for assigning tokens. It will filter guest requests and login requests and trigger them before the requests. It can also achieve the purpose of unified maintenance of identity authentication codes. + +```javascript +createClientTokenAuthentication({ + assignToken: method => { + method.config.headers.Authorization = localStorage.getItem('token')}; + } +}); +``` + +> The usage of assignToken callback function of `createServerTokenAuthentication` is the same. + +## Logout interception + +When your logout also requires calling the interface, you can also intercept the logout request and clear the login information. + +First identify the logout request metadata as `authRole: 'logout'`. + +```javascript +export const logout = () => { + const method = alovaInstance.Get('/logout'); + method.meta = { + authRole: 'logout' + }; + return method; +}; +``` + +Then clear the login information in the logout interceptor. + +```javascript +createClientTokenAuthentication({ + logout(response, method) { + localStorage.removeItem('token'); + localStorage.removeItem('refresh_token'); + } +}); +``` + +> The login interceptor usage of `createServerTokenAuthentication` is the same. + +## Custom identification identity + +The above metadata identities are actually the default identities. If you need to customize the identity, you can set it as follows. + +### token refresh identity + +```javascript +createClientTokenAuthentication({ + refreshToken: { + // highlight-start + metaMatches: { + refreshToken: true + } + // highlight-end + // ... + } +}); +``` + +```javascript +createServerTokenAuthentication({ + refreshTokenOnSuccess: { + // highlight-start + metaMatches: { + refreshToken: true + } + // highlight-end + // ... + }, + refreshTokenOnError: { + // highlight-start + metaMatches: { + refreshToken: true + } + // highlight-end + // ... + } +}); +``` + +Then, requests with `refreshToken: true` in the metadata will be identified as `refreshToken`. + +```javascript +export const refreshToken = () => { + const method = alovaInstance.Get('/refresh_token'); + method.meta = { + refreshToken: true + }; + return method; +}; +``` + +### Visitor identify + +```javascript +createClientTokenAuthentication({ + visitorMeta: { + isVisitor: true + } +}); +``` + +Then, requests with `isVisitor: true` in the metadata will be identified as visitors. + +```javascript +export const requestTokenNotRequired = () => { + const method = alovaInstance.Get('/token_not_required'); + method.meta = { + isVisitor: true + }; + return method; +}; +``` + +### Login identity + +```javascript +createClientTokenAuthentication({ + login: { + // highlight-start + metaMatches: { + login: true + }, + // highlight-end + handler(response, method) { + //Login interceptor + } + } +}); +``` + +Then, requests with `login: true` in the metadata will be identified as `login` identity. + +```javascript +export const login = () => { + const method = alovaInstance.Get('/login'); + method.meta = { + login: true + }; + return method; +}; +``` + +### Log out identity + +```javascript +createClientTokenAuthentication({ + logout: { + // highlight-start + metaMatches: { + logout: true + }, + // highlight-end + handler(response, method) { + //Logout interceptor + } + } +}); +``` + +Then, requests with `logout: true` in the metadata will be identified as `logout`. + +```javascript +export const logout = () => { + const method = alovaInstance.Get('/logout'); + method.meta = { + logout: true + }; + return method; +}; +``` + +> The login interceptor usage of `createServerTokenAuthentication` is the same. + +## Typescript + +By default, `createClientServerTokenAuthentication` and `createServerTokenAuthentication` are adapted to the `GlobalFetch` request adapter, you can only specify the type of `StatesHook`, as follows: + +```typescript +// highlight-start +const { onAuthRequired, onResponseRefreshToken } = createClientTokenAuthentication< + typeof VueHook +>({ + // highlight-end + //... +}); + +const alovaInstance = createAlova({ + // ... + beforeRequest: onAuthRequired(method => { + // The type of method is Method + }), + responded: onResponseRefreshToken((response, method) => { + //The response type is Response + return response.json(); + }) +}); +``` + +If you are not using the `GlobalFetch` request adapter, You also need to specify the type of request adapter, which is also simple. + +The following is an example of the axios request adapter. Specify the request adapter type in `createClientTokenAuthentication`. + +```typescript +import { axiosRequestAdapter } from '@alova/adapter-axios'; + +// highlight-start +const { onAuthRequired, onResponseRefreshToken } = createClientTokenAuthentication< + typeof ReactHook, + typeof axiosRequestAdapter +>({ + // highlight-end + //... +}); +const alovaInstance = createAlova({ + //... + // highlight-start + beforeRequest: onAuthRequired(method => { + // The type of method is Method + // highlight-end + }), + // highlight-start + responded: onResponseRefreshToken((response, method) => { + //The response type is AxiosResponse + // highlight-end + return response.data; + }) +}); +``` + +The server-based Token authentication interceptor is used in the same way. + +```typescript +import { axiosRequestAdapter } from '@alova/adapter-axios'; + +// highlight-start +createServerTokenAuthentication({ + // highlight-end + //... +}); +``` diff --git a/docs/tutorial/03-client/06-use-auto-request.md b/docs/tutorial/03-client/06-use-auto-request.md new file mode 100644 index 000000000..fedd67de7 --- /dev/null +++ b/docs/tutorial/03-client/06-use-auto-request.md @@ -0,0 +1,132 @@ +--- +title: Automatically refetch data +sidebar_position: 60 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info strategy type + +use hook + +::: + +> Before using extension hooks, make sure you are familiar with the basic use of alova. + +Automatically fetch data through browser events or polling, allowing the interface to display the newest data. + +## Features + +- ✨ Supports refetching the newest data in scenarios such as browser focus, tab switching, network reconnection, polling requests, etc; +- ✨ Supports request throttling, only one request will be sent if triggered multiple times in a short period of time; +- ✨ Support custom event listening functions to adapt to usage scenarios in non-browser environments; + +## Basic usage + +By default, useHook`useAutoRequest` that automatically fetches data will automatically fetch the newest data when browser is visible, hidden, focused, and the network is reconnected, and will automatically cancel the listening event when the component is uninstalled. + +```javascript +import { useAutoRequest } from 'alova/client'; + +const { loading, data, error } = useAutoRequest(() => method()); +``` + +The return value of `useAutoRequest` is the same as [useRequest](/api/core-hooks#userequest). + +In addition to supporting all configuration parameters of [useRequest](/api/core-hooks#userequest), it also supports automatically fetched configuration parameters. You can turn on or off some events through the following configuration, or modify request throttling events. + +```javascript +const { loading, data, error, onSuccess, onError, onComplete } = useAutoRequest( + () => method(), + { + /** + * Browser display hide trigger + * @default true + */ + enableVisibility: true, + + /** + * Triggered by browser focus + * @default true + */ + enableFocus: true, + + /** + * Triggered by network reconnection + * @default true + */ + enableNetwork: true, + + /** + *Throttling time, only one request will be sent if triggered multiple times within a certain period, unit ms + * @default 1000 + */ + throttle: 1000, + + /** + * The time of polling request, effective when set greater than 0, unit ms + * @default 0 + */ + pollingTime: 2000 + + //Other parameters are the same as useRequest... + } +); +``` + +:::warning caching advice + +It is recommended to turn off the cache of the corresponding request when using `useAutoRequest`, because when the cache is set, the cache will be hit when the automatic request is triggered and the newest data cannot be obtained. Please read [Cache Mode](/tutorial/cache/mode) for details. + +::: + +## Custom listening functions + +The above 4 methods of automatically fetching data are implemented by listening browser's events by default. When users use it in a non-browser environment, you may need to customize the listening function. This function receives the notification request function and useHook config as parameters, and returns a cancel listening function. +. + +The following is an example of custom listening function in `react-native`: + +### Network reconnection custom function + +```javascript +import NetInfo from '@react-native-community/netinfo'; +useAutoRequest.onNetwork = (notify, config) => { + const unsubscribe = NetInfo.addEventListener(({ isConnected }) => { + isConnected && notify(); + }); + return unsubscribe; +}; +``` + +### Polling custom function + +```javascript +useAutoRequest.onPolling = (notify, config) => { + const timer = setInterval(notify, config.pollingTime); + return () => clearInterval(timer); +}; +``` + +### App switching custom function + +```javascript +import { AppState, Text } from 'react-native'; +useAutoRequest.onVisibility = (notify, config) => { + const subscription = AppState.addEventListener('change', state => { + state === 'active' && notify(); + }); + return () => subscription.remove(); +}; +``` + +### App focus custom function + +Since the App doesn't have a focus event, it can be set to an empty function to avoid throwing error. + +```javascript +useAutoRequest.onFocus = (notify, config) => { + return () => {}; +}; +``` diff --git a/docs/tutorial/03-client/07-action-delegation-middleware.md b/docs/tutorial/03-client/07-action-delegation-middleware.md new file mode 100644 index 000000000..94b4b02e6 --- /dev/null +++ b/docs/tutorial/03-client/07-action-delegation-middleware.md @@ -0,0 +1,205 @@ +--- +title: Cross components to trigger request +sidebar_position: 70 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info strategy type + +middleware + +::: + +> Before using extension hooks, make sure you are familiar with the basic usage of alova. + +In the past, if you want to trigger a request in another component in one component, you need to save the data in the Store and complete it by dispatching Action. Now, you can use this middleware to **remove the limitation of component hierarchy**, and quickly trigger any request action function in any component. + +For example, after updating the menu data in a component, you can re-trigger the re-request of the side menu bar to refresh the data. When the list data is manipulated, the list update is triggered. + +## Example + +[Cross-component trigger request Demo](/tutorial/example/action-delegation-middleware) + +## Features + +- ✨Delegate the action function of any use hook in alova; +- ✨ Trigger the delegated action function anywhere; + +## Usage + +### Basic usage + +> Take vue3 as an example, the usage is the same in react and svelte. + +Use `actionDelegationMiddleware` in component A to delegate the action function of `useRequest`. + +```javascript title=Component A +import { actionDelegationMiddleware } from 'alova/client'; + +useRequest(queryTodo, { + //... + middleware: actionDelegationMiddleware('actionName') +}); +``` + +In any component (such as component B), pass in the specified delegate name through `accessAction` to trigger the action function of `useRequest` in component A. + +```javascript title=Component B +import { accessAction } from 'alova/client'; + +accessAction('actionName', delegatedActions => { + // Call the send function in component A + delegatedActions.send(); + + // Call the abort function in component A + delegatedActions.abort(); +}); +``` + +:::info note + +1. All use hooks in alova support action function delegation, but the functions delegated by different use hooks are different. +2. When using `actionDelegationMiddleware`, the delegate name can be passed in strings, numbers, and symbol values. + +::: + +### Batch trigger action function + +In the above example, we use `accessAction` to trigger the action function of a use hook, but in fact, delegates with the same name will not override each other, but will be stored in a group, and we can use this name to trigger them at the same time The delegated function. + +```javascript title=Component C +import { actionDelegationMiddleware } from 'alova/client'; + +useRequest(queryTodo, { + //... + middleware: actionDelegationMiddleware('actionName1') +}); +``` + +```javascript title=Component D +import { actionDelegationMiddleware } from 'alova/client'; + +useRequest(queryTodo, { + //... + middleware: actionDelegationMiddleware('actionName1') +}); +``` + +```javascript title=Component E +import { accessAction } from 'alova/client'; + +// Because the delegate hooks of component C and component D will be matched, the callback function will be executed twice +accessAction('actionName1', delegatedActions => { + // Call the send function in component C and component D + delegatedActions.send(); + + // Call the abort function in component C and component D + delegatedActions.abort(); +}); +``` + +At the same time, regular expressions can also be used in `accessAction` to trigger batches of action functions whose delegate names meet the conditions + +```javascript title=Component F +import { actionDelegationMiddleware } from 'alova/client'; + +useRequest(queryTodo, { + //... + middleware: actionDelegationMiddleware('prefix_name1') +}); +``` + +```javascript title=Component G +import { actionDelegationMiddleware } from 'alova/client'; + +useRequest(queryTodo, { + //... + middleware: actionDelegationMiddleware('prefix_name2') +}); +``` + +```javascript title=Component H +import { accessAction } from 'alova/client'; + +// Because the delegate hooks of component F and component G will be matched, the callback function will be executed twice +accessAction(/^prefix_/, delegatedActions => { + // Call the send function in component F and component G + delegatedActions.send(); + + // Call the abort function in component F and component G + delegatedActions.abort(); +}); +``` + +## Action function delegation list + +Although the action functions delegated by most hooks are the same as the action functions themselves, this is not absolute. The following is the action function delegation list of each hook. + +### useRequest + +| name | description | function parameters | return value | version | +| ------ | ------------------------------------------------------- | ------------------- | ------------ | ------- | +| send | Same as [useRequset](/api/core-hooks#userequest).send | | | - | +| abort | Same as [useRequset](/api/core-hooks#userequest).abort | | | - | +| update | Same as [useRequset](/api/core-hooks#userequest).update | | | - | + +### useWatcher + +Same as [useRequest delegate list](#userequest). + +### useFetcher + +| name | description | function parameters | return value | version | +| ------ | ------------------------------------------------------- | ------------------- | ------------ | ------- | +| fetch | Same as [useFetcher](/api/core-hooks#usefetcher).fetch | | | - | +| abort | Same as [useFetcher](/api/core-hooks#usefetcher).abort | | | - | +| update | Same as [useFetcher](/api/core-hooks#usefetcher).update | | | - | + +### usePagination + +| name | description | function parameters | return value | version | +| -------- | -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | ------------------------------------------ | ------- | +| refresh | For details, see [usePagination action function](/tutorial/strategy/usePagination#api) | | | - | +| insert | For details, see [usePagination action function](/tutorial/strategy/usePagination#api) | | | - | +| remove | For details, see [usePagination action function](/tutorial/strategy/usePagination#api) | | | - | +| replace | For details, see [usePagination action function](/tutorial/strategy/usePagination#api) | | | - | +| reload | For details, see [usePagination action function](/tutorial/strategy/usePagination#api) | | | - | +| update | For details, see [usePagination action function](/tutorial/strategy/usePagination#api) | | | - | +| getState | Get paging related data by name | stateKey: 'page' \| 'pageSize' \| 'data' \| 'pageCount' \| 'total' \| 'isLastPage' | Corresponding to the value of the statekey | - | + +### useSQRequest + +Same as [useRequest delegate list](#userequest). + +### useForm + +| name | description | function parameters | return value | version | +| ---------- | -------------------------------------------------------------------------- | ------------------- | ------------ | ------- | +| updateForm | For details, see [useForm action function](/tutorial/strategy/useForm#api) | | | - | +| reset | For details, see [useForm action function](/tutorial/strategy/useForm#api) | | | - | +| send | Same as [useRequset](/api/core-hooks#userequest).send | | | - | +| abort | Same as [useRequset](/api/core-hooks#userequest).abort | | | - | +| update | Same as [useRequset](/api/core-hooks#userequest).update | | | - | + +### useCaptcha + +Same as [useRequest delegate list](#userequest). + +### useRetriableRequest + +| name | description | function parameters | return value | version | +| ------ | ------------------------------------------------------------------------------------------------- | ------------------- | ------------ | ------- | +| stop | See [useRetriableRequest action function](/tutorial/strategy/useRetriableRequest#api) for details | | | - | +| send | Same as [useRequset](/api/core-hooks#userequest).send | | | - | +| abort | Same as [useRequset](/api/core-hooks#userequest).abort | | | - | +| update | Same as [useRequset](/api/core-hooks#userequest).update | | | - | + +### useSerialRequest + +Same as [useRequest delegate list](#userequest). + +### useSerialWatcher + +Same as [useRequest delegate list](#userequest). diff --git a/docs/tutorial/03-client/08-sensorless-data-interaction/02-virtual-data.md b/docs/tutorial/03-client/08-sensorless-data-interaction/02-virtual-data.md new file mode 100644 index 000000000..6c2663db4 --- /dev/null +++ b/docs/tutorial/03-client/08-sensorless-data-interaction/02-virtual-data.md @@ -0,0 +1,177 @@ +--- +title: Virtual data +sidebar_position: 20 +--- + +In fact, virtual data is a reference object with a unique id, and its tracking mechanism is realized by first generating a mapping between virtual data id and response data, and then finding and replacing it with the actual value through virtual data id. + +When the original value is a reference type, the performance is the same as the original value, but the virtual data of the basic type uses `Number, String, Boolean` encapsulation classes, as well as custom `Undefined, Null` encapsulation classes, and their expressions are the same as There are some deviations from the original value. The characteristics of the virtual data and the use of auxiliary functions for the virtual data are listed below. The details of the auxiliary functions will be introduced at the end of the chapter. + +## string concatenation + +When virtual data is concatenated, it will be converted to virtual data id for splicing. + +```javascript +const virtualData = createVirtualData(1); +'a' + virtualData; // a[vd:xxxxxx] +1 + virtualData; // 1[vd:xxxxxx] +``` + +## Data Comparison + +Virtual data cannot be directly used for comparison, but virtual data and actual data are often mixed and compared in actual scenarios. In this case, `equals` can be used for comparison. + +```javascript +import { equals } from 'alova/client'; + +equals('a', 'a'); // true + +const virtualData1 = createVirtualData(1); +const virtualData2 = virtualData1.clone(); // clone virtual data +equals(virtualData1, virtualData2); // true when the ids of virtualData1 and virtualData2 are the same +equals(virtualData1, '[vd:xxxxxx]'); // true when the id of virtualData1 is also [vd:xxxxxx] +``` + +## Participate in operations + +When participating in operations such as `+-*/%`, numerical comparison, and bit operations, it cannot be automatically converted to the original value. It can be converted to the original value through `dehydrateVData` and then calculated. + +```javascript +import { dehydrateVData } from 'alova/client'; + +const virtualData = createVirtualData(1); +dehydrateVData(virtualData) + 1; // 2 +dehydrateVData(virtualData) > 0; // true +``` + +## type operator + +Because the virtual data is implemented using the encapsulation class on the basic data type, `object` will always be returned when using `typeof` to get the type, and it can also be converted to the original value by `dehydrateVData` to get the type + +```javascript +const vNum = createVirtualResponse(1); +typeof vNum === 'object'; // true +const vUndef = createVirtualResponse(undefined); +typeof vUndef === 'object'; // true + +typeof dehydrateVData(vNum) === 'number'; // true +typeof dehydrateVData(vUndef) === 'undefined'; // true +``` + +To solve this problem, you can use the virtual data helper function **dehydrateVData**, which can get the original value of a virtual data, and return it unchanged when encountering non-virtual data + +```javascript +const vNum = createVirtualResponse(1); +typeof dehydrateVData(vNum) === 'number'; // true +dehydrateVData(vNum) === 1; // true +dehydrateVData('string') === 'string'; // true +``` + +## View display + +By default, `toString` will be called implicitly when the virtual data is displayed in the view, but sometimes you will encounter problems with display confusion, and rendering an object in **react** will report the following error: + +``` +Uncaught Error: Objects are not valid as a React child (found: object with keys {}). If you meant to render a collection of children, use an array instead. +``` + +Therefore, it is recommended to use `dehydrateVData` to convert to raw data for display. + +## virtual data helper functions + +### dehydrateVData + +Dehydrate virtual data and return its original value, if target is non-virtual data, return it as it is. + +```typescript +// type +function dehydrateVData(target: any): any; + +// example +dehydrateVData(1); // 1 +const virtualData = createVirtualData(1); +dehydrateVData(virtualData); // 1 +``` + +### stringifyVData + +Stringify virtual data, return virtual data id, when returnOriginalIfNotVData is set to false, non-virtual data will be returned as-is. + +```typescript +// type +function stringifyVData(target: any, returnOriginalIfNotVData?: boolean): any; + +// example +stringifyVData(1); // 1 +stringifyVData(1, false); // undefined + +const virtualData = createVirtualData(1); +stringifyVData(virtualData); // [vd:xxxxxx] +``` + +### equals + +Judge whether two values are equal in a way that is compatible with virtual data. When there is no virtual data to participate in the comparison, it will be strictly compared. Otherwise, it will be compared whether the virtual data id is the same. If there may be virtual data involved in the comparison data, it is recommended to use this function for comparison. + +```typescript +// type +function equals(prevValue: any, nextValue: any): boolean; + +// example +equals('a', 'a'); // true +const virtualData1 = createVirtualData(1); +const virtualData2 = virtualData1.clone(); // clone virtual data +equals(virtualData1, virtualData2); // true when the ids of virtualData1 and virtualData2 are the same +equals(virtualData1, '[vd:xxxxxx]'); // true when the id of virtualData1 is also [vd:xxxxxx] +``` + +###isVData + +Determine whether the target data is virtual data + +```typescript +// type +function isVData(target: any): boolean; + +// example +isVData(1); // false +const virtualData = createVirtualData(1); +isVData(virtualData); // true +isVData('[vd:xxxxxx]'); //true +``` + +## Replacement restrictions for virtual data + +The tracing mechanism of virtual data can only deeply traverse relevant data, and then replace the data with virtual data identifiers with actual data. If some data is generated by virtual data, it will not be recalculated after virtual data is replaced with actual data. + +In the following cases, even if the virtualId is replaced with actual data, the id of the request will not be recalculated. Therefore, if replacement is required, the virtualId must be directly used as a request parameter. The example is as follows: + +```javascript +const deleteTodo = virtualId => { + return alova.Delete('/deleteTodo', { + id: dehydrateVData(virtualId) === null ? 1 : 2 + }); +}; +``` + +But if virtual data is concatenated as string, it will be automatically converted to virtual data id to participate in string concatenation, which will work. In the following cases, the value of the request id at the beginning is `id_[vd:xxxxxx]`, and when virtualId is replaced with the response value (assuming it is replaced with 1), it will be automatically updated to `id_1`. + +```javascript +const deleteTodo = virtualId => { + return alova.Delete('/deleteTodo', { + id: 'id'_virtualId + }); +}; +``` + +## Next + +Although it is simple enough to realize non-inductive interaction, there are still some additional processing compared with conservative requests. The specific implementation is roughly divided into the following steps. + +1. Implement functions in a conservative request manner; +2. Manually update the list data to realize localized data compensation; +3. Record data operations so that you can manually add to the latest records when there are unsubmitted modifications; +4. Manually compensate the unsubmitted data to the list, so that the latest status can be displayed even if the data is not submitted; +5. When modifying unsubmitted data, intercept requests with virtual data; + +Next, we will demonstrate with a simple example. diff --git a/docs/tutorial/03-client/08-sensorless-data-interaction/03-start-silent-factory.md b/docs/tutorial/03-client/08-sensorless-data-interaction/03-start-silent-factory.md new file mode 100644 index 000000000..0a140768c --- /dev/null +++ b/docs/tutorial/03-client/08-sensorless-data-interaction/03-start-silent-factory.md @@ -0,0 +1,68 @@ +--- +title: Boot silent factory +sidebar_position: 30 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +The silent queue is not started by default, and we need to specify the startup parameters for initialization. In general, call `bootSilentFactory` in the entry file to initialize the silent factory, which will read unexecuted requests to the corresponding silent through the specified configuration items queues and start those queues. + + + + +```javascript +import { bootSilentFactory } from 'alova/client'; +import { alovaInst } from '@/api'; + +bootSilentFactory({ + // Specify the alova instance at startup to request information storage and request sending + alova: alovaInst, + + // Delay start time, in milliseconds, the default is 2000ms, see the follow-up instructions for details + delay: 1000 +}); +``` + + + + + +```javascript +import { bootSilentFactory } from 'alova/client'; +import { alovaInst } from '@/api'; + +bootSilentFactory({ + // Specify the alova instance at startup to request information storage and request sending + alova: alovaInst, + + // Delay start time, in milliseconds, the default is 2000ms, see the follow-up instructions for details + delay: 1000 +}); +``` + + + + + +```javascript +import { bootSilentFactory } from 'alova/client'; +import { alovaInst } from '@/api'; + +bootSilentFactory({ + // Specify the alova instance at startup to request information storage and request sending + alova: alovaInst, + + // Delay start time, in milliseconds, the default is 2000ms, see the follow-up instructions for details + delay: 1000 +}); +``` + + + + +:::warning `delay` parameter description + +In actual scenarios, when entering the current page, a request is also sent to load the page data. In order to ensure that the user can see the page data faster, the request to load the data needs to be forwarded to the beginning of the queue, otherwise it may cause the loading data to fail. The request is placed at the end of the queue. At this time, it is necessary to wait until all the previous requests are completed before loading the page data. This is obviously inappropriate. Therefore, by delaying initialization for a period of time, the request for loading data enters the queue first to achieve "queue jumping" effect, the specific delay time depends on the time required for page rendering. + +::: diff --git a/docs/tutorial/03-client/08-sensorless-data-interaction/04-conservative-request.md b/docs/tutorial/03-client/08-sensorless-data-interaction/04-conservative-request.md new file mode 100644 index 000000000..4842ff79e --- /dev/null +++ b/docs/tutorial/03-client/08-sensorless-data-interaction/04-conservative-request.md @@ -0,0 +1,136 @@ +--- +title: Step 1 - Implement features with conservative requests +sidebar_position: 40 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +Take Todo management as an example to realize the creation, editing, deletion and other functions of Todo in the non-sense interaction mode, and the key code related to the request will be provided in the following chapters. + +> The [simple list page example](/tutorial/example/silent-submit-simple-list) here contains the complete code, you can enter the experience. + +**useSQRequest** will be used to replace **useRequest** provided by alova, and then the most common conservative request mode will be implemented first, and then the process will be done step by step Interactive mode compatibility. + +## Create an alova instance and related methods + +```javascript title="api.js" +import { createAlova } from 'alova'; + +export const alovaInst = createAlova({ + /*...*/ +}); + +/** Load todo list */ +const todoList = () => alovaInst.Get('/todo'); + +/** Load todo details */ +const todoDetail = id => + alovaInst.Get('/todo', { + params: { id } + }); + +/** Create and edit todo items */ +const createOrEditTodo = (data, id) => + alovaInst.Post('/todo', { + data, + id + }); + +/** Delete the todo item */ +const deleteTodo = id => alovaInst.Delete('/todo', { id }); +``` + +## Start the silent factory + +```javascript title="main.js" +import { bootSilentFactory } from 'alova/client'; +import { alovaInst } from './api.js'; + +bootSilentFactory({ + alova: alovaInst +}); +``` + +## Load the Todo list + +Load and display page data in the simplest way + +```javascript +import { useSQRequest } from 'alova/client'; +import { todoList } from './api.js'; +const { data, loading, error } = useSQRequest(todoList, { + initialData: [] +}); +``` + +## Enter the Todo creation/editing page + +When creating a todo item, the id is empty, and no request for obtaining details is sent. When editing a todo item, if the id has a value, the detailed data will be obtained. + +```javascript +import { useSQRequest } from 'alova/client'; +import { todoDetail } from './api.js'; + +const id = /* todo id */; +const { loading, data } = useSQRequest(() => todoDetail(id), { + initialData: { + title: '', + time: new Date() + }, + immediate: !!id +}); +``` + +## Create/Edit Todo Items + +By submitting an event trigger request, after the submission is successful, call fetch to re-fetch the latest list data, and the interface will automatically display the latest data. + +```javascript +import { useFetcher } from 'alova'; +import { useSQRequest } from 'alova/client'; +import { createOrEditTodo, todoList } from './api.js'; + +const id = /* todo id */; +const { loading, data, send, onSuccess } = useSQRequest(createOrEditTodo, { + immediate: false, +}); + +const { fetch } = useFetcher(); +onSuccess(() => { + // Re-fetch list data + fetch(todoList); +}) + +// submit event callback function +const handleSubmit = newData => { + send(newData, id); +}; + +``` + +## Delete Todo item + +Delete the corresponding todo item by id + +```javascript +import { useSQRequest } from 'alova/client'; +import { deleteTodo, todoList } from './api.js'; + +const { loading, data, send, onSuccess } = useSQRequest(deleteTodo, { + immediate: false +}); + +const { fetch } = useFetcher(); +onSuccess(() => { + // Re-fetch list data + fetch(todoList); +}); + +// Event callback triggers delete request +const handleDelete = deletingId => { + send(deletingId); +}; +``` + +So far, a simple Todo list management related request function has been completed, and then we will start to transform it to be compatible with the non-sense interaction mode. diff --git a/docs/tutorial/03-client/08-sensorless-data-interaction/05-modify-response.md b/docs/tutorial/03-client/08-sensorless-data-interaction/05-modify-response.md new file mode 100644 index 000000000..eddef1b85 --- /dev/null +++ b/docs/tutorial/03-client/08-sensorless-data-interaction/05-modify-response.md @@ -0,0 +1,260 @@ +--- +title: Step 2 - Adjust Response Handling +sidebar_position: 50 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +In the conservative request example in the previous section, we called `fetch` to re-fetch the refreshed page after creating, editing, and deleting requests for Todo items. In order to display the results immediately after the operation, we need to make the following adjustments: + +1. Set the behavior mode of create, edit and delete requests to `silent`, they will trigger the success callback immediately when the request is made; +2. Manually update the list, instead of pulling data, use virtual data to occupy the response data of the server; +3. Save operation records for data compensation when refreshing the page; + +## Set behavior mode + +Set by configuring the parameter `behavior`, the optional parameters are `queue`, `silent`, `static`, or a function that returns behavior data to dynamically set the behavior mode, the default is `queue`. + +The following sets the behavior parameters statically. + +```javascript +useSQRequest(createOrEditTodo, { + // highlight-start + behavior: 'silent', + // highlight-end + immediate: false +}); +``` + +The following is to dynamically set the behavior parameter. + +```javascript +const { send } = useSQRequest(createOrEditTodo, { + // highlight-start + // The arg parameter can be passed in through the send function + behavior: arg => { + if (arg === 0) return 'silent'; + return 'queue'; + }, + // highlight-end + immediate: false +}); +``` + +> When behavior is set as a function, it will be called every time a request is initiated to determine which behavior to process this request. + +## Silent queue description + +After setting the behavior parameter to `queue` or `silent`, the request will enter the silent queue and wait for the request to be initiated. By default, they will enter the queue named `default`. You can also specify other queues to save silentMethod instances. without interfering with each other. + +```javascript +useSQRequest(createOrEditTodo, { + // highlight-start + // The specified request information enters the queue named queue-2 + queue: 'queue-2', + // highlight-end + behavior: 'silent', + immediate: false +}); +``` + +## Manually update the list in the callback + +### Update the list after adding/editing + + + + +When the list page is not destroyed, such as using the modal box operation on the current page, or using `` (Vue) to keep the page components, the data will still exist. At this time, we use **updateStateEffect** to Update the list data. Compared with the **updateState** exported by alova, it has the function of tracking virtual data. When the response data is obtained, it will automatically track the virtual data in the list data and replace it with the actual data. + +```javascript +import { useSQRequest, updateStateEffect } from 'alova/client'; +import { createOrEditTodo, todoList } from './api.js'; + +const { onSuccess } = useSQRequest(createOrEditTodo, { + behavior: 'silent', + immediate: false, + + // highlight-start + // Before processing list updates, it is necessary to construct virtual response data of the same structure according to the structure of the response data + // For example, when creating a Todo item, the id of this piece of data will be returned. + silentDefaultResponse: () => { + return { + id: '--' + }; + } + // highlight-end +}); + +// highlight-start +onSuccess(({ data, silentMethod }) => { + // Construct list data items + const editingItem = { + ...detail, + + // When editing, use the original id, otherwise use the id in the response data + // When submitting silently, data.id is virtual data, and when in static behavior mode, data.id is the actual id value + id: id || data.id + }; + + // use updateStateEffect instead of updateState + updateStateEffect(todoList(), todoListRaw => { + if (id) { + todoListRaw = todoListRaw.map(item => (item.id === id ? editingItem : item)); + } else { + todoListRaw.unshift(editingItem); + } + return todoListRaw; + }); +}); +// highlight-end +``` + +> updateStateEffect is used in the same way as [updateState](/tutorial/advanced/update-across-components) + + + + +When the list page has been destroyed and the data has been released, such as jumping to a new page, use **setCache** to update the cache data. When the list page is returned, the request will be re-initiated and the updated cache will be hit. + +```javascript +import { useSQRequest, setCache, equals } from 'alova/client'; +import { createOrEditTodo, todoList } from './api.js'; + +const urlParams = new URLSearchParams(window.location.search); +const id = urlParams.get('id') || ''; +const { onSuccess } = useSQRequest(createOrEditTodo, { + behavior: 'silent', + immediate: false, + + // highlight-start + // Before processing list updates, it is necessary to construct virtual response data of the same structure according to the structure of the response data + // For example, when creating a Todo item, the id of this piece of data will be returned. + silentDefaultResponse: () => { + return { + id: '--' + }; + } + // highlight-end +}); +// highlight-start +onSuccess(({ data, silentMethod }) => { + // Construct list data items + const editingItem = { + ...detail, + + // When editing, use the original id, otherwise use the id in the response data + // When submitting silently, data.id is virtual data, and when in static behavior mode, data.id is the actual id value + id: id || data.id + }; + + const method TodoList = todoList(); + setCache(methodTodoList, todoListRaw => { + if (id) { + todoListRaw = todoListRaw.map(item => (equals(item.id, id) ? editingItem : item)); + } else { + todoListRaw.unshift(editingItem); + } + return todoListRaw; + }); + // Call setUpdateState to set response data tracking, so as to achieve the same delayed update effect as updateStateEffect + if (silentMethod) { + silentMethod.setUpdateState(methodTodoList); + silentMethod.save(); + } +}); +// highlight-end +``` + + + + +### Update list after removal + +```javascript +import { useSQRequest, updateStateEffect } from 'alova/client'; +import { deleteTodo, todoList } from './api.js'; + +const { loading, data, send, onSuccess } = useSQRequest(deleteTodo, { + immediate: false, + // highlight-start + behavior: 'silent' + // highlight-end +}); + +onSuccess(({ sendArgs: [deletingId] }) => { + updateStateEffect(todoList(), todoListRaw => + todoListRaw.filter(item => item.id !== deletingId) + ); +}); + +// Event callback triggers delete request +const handleDelete = deletingId => { + send(deletingId); +}; +``` + +## Save the operation record + +It is not enough to just update the list manually. We also need to consider that when the network is restored and there are still waiting requests in the request queue, the list data loaded at this time does not include the part of the unsubmitted request, which will cause certain problems for the user. Puzzled: + +> "I have clearly added multiple pieces of data, why is it not in the list?" + +Therefore, we need to record the operation and related data in the success callback, so that when the list data is loaded again, the uncommitted data will be manually compensated to the list, so that the list data will always be kept up-to-date. + +Saving operation records is also very simple, you only need to mount the relevant data to the silentMethod instance, and it will be persisted along with the instance. + +### create/edit success callback + +```javascript +//... +onSuccess(({ silentMethod }) => { + // Construct list data items + const editingItem = { + ...detail, + id: id || data.id + }; + //... + // highlight-start + if (silentMethod) { + // Set the name for subsequent queries + // If editingItem.id is virtual data will be automatically converted to its id + silentMethod.entity.setName('edit' + editingItem.id); + silentMethod.reviewData = { + operate: id ? 'edit' : 'add', + data: editingItem + }; + silentMethod.save(); + } + // highlight-end +}); +``` + +### delete success callback + +```javascript +//... +onSuccess(({ sendArgs: [deletingId], silentMethod }) => { + //... + // highlight-start + if (silentMethod) { + silentMethod.reviewData = { + operate: 'delete', + data: { + id: deletingId + } + }; + silentMethod.save(); + } + // highlight-end +}); +``` + +### Precautions + +1. In the onSuccess callback function, silentMethod has a value only in the `queue` and `silent` behavior modes; +2. Generally speaking, you can use `silentMethod.a = ...` or `silentMethod.b = ...` to save operation records, but it will report an error in typescript, so _reviewData_ is specially provided as a silent Submit the save attribute of the operation record; +3. After modifying the silentMethod data, you need to save the modification through `silentMethod.save()`; + +The next step is to set retry parameters on silent submit requests. diff --git a/docs/tutorial/05-strategy/01-sensorless-data-interaction/06-request-retry.md b/docs/tutorial/03-client/08-sensorless-data-interaction/06-request-retry.md similarity index 100% rename from docs/tutorial/05-strategy/01-sensorless-data-interaction/06-request-retry.md rename to docs/tutorial/03-client/08-sensorless-data-interaction/06-request-retry.md diff --git a/docs/tutorial/03-client/08-sensorless-data-interaction/07-data-compensation.md b/docs/tutorial/03-client/08-sensorless-data-interaction/07-data-compensation.md new file mode 100644 index 000000000..429b9f0ad --- /dev/null +++ b/docs/tutorial/03-client/08-sensorless-data-interaction/07-data-compensation.md @@ -0,0 +1,56 @@ +--- +title: Step 4 - Data Compensation +sidebar_position: 70 +--- + +The user may perform some data operations in a disconnected environment. At this time, the silent queue will be full of unsubmitted requests. When the network is restored, due to the limitation of the timing mechanism, it will take a little time to complete these requests. The list loaded at this time The data does not include unsubmitted requests, which can cause some confusion for users: + +> "I have clearly added multiple pieces of data, why is it not in the list?" + +Therefore, we need to manually compensate the unsubmitted data to the list, so that the list data is always kept up-to-date. In this step, the saved operation records will be used to compensate the list data. It is actually very simple. We It is only necessary to traverse the silentMethod instance of the relevant queue after the list request is successful, and update the operation records recorded in the previous step to the list data. + +```javascript +import { useSQRequest, filterSilentMethods, equals } from 'alova/client'; +import { todoList } from './api.js'; +const { data, loading, onSuccess } = useSQRequest(todoList, { + initialData: [] +}); + +onSuccess(() => { + // Get all silentMethod instances under the default queue + const silentMethods = filterSilentMethods(); + silentMethods.forEach(({ reviewData }) => { + if (!reviewData) { + return; + } + const { operate, data } = reviewData; + const index = todoListData.findIndex(({ id }) => equals(id, data.id)); + if ((operate === 'edit' || operate === 'remove') && index >= 0) { + operate === 'edit' ? todoListData.splice(index, 1, data) : todoListData.splice(index, 1); + } else if (operate === 'add' && index < 0) { + // There will be added uncommitted items when re-requesting and hitting the cache, these need to be filtered + todoListData.unshift(data); + } + }); +}); +``` + +## Explanation of related functions + +### filterSilentMethods + +Filter out the specified silentMethod instance by method name or regular expression, which is defined as follows: + +```typescript +function filterSilentMethods( + methodNameMatcher?: string | number | RegExp, + queueName?: string, + filterActive?: boolean +): SilentMethod[]; +``` + +**methodNameMatcher: **method name matcher, if it is a number or string, it will filter out the results that completely match the name, if it is a regular expression, it will filter out the matching results, if it is not passed, it will filter out all the results; + +**queueName**: Specify the queue to search for, if not uploaded, the _default_ queue will be searched by default; + +**filterActive**: whether to filter out the active state instance diff --git a/docs/tutorial/03-client/08-sensorless-data-interaction/08-edit-item.md b/docs/tutorial/03-client/08-sensorless-data-interaction/08-edit-item.md new file mode 100644 index 000000000..6dfcf86af --- /dev/null +++ b/docs/tutorial/03-client/08-sensorless-data-interaction/08-edit-item.md @@ -0,0 +1,111 @@ +--- +title: Step 5 - Edit Data +sidebar_position: 80 +--- + +> What should I do when the user needs to edit data when the network is disconnected? + +At this point, two situations need to be explained: + +1. The list data can meet the data echo of the edit page. At this time, the list data can be passed to the edit page without requesting. At this time, all list data supports editing in the silent submission mode; +2. The echo data on the edit page needs to be obtained through the api, and only the locally cached list items can echo the data normally, for example: + 1. For the list items that have been accessed before the network is disconnected, the request can hit the cache again; + 2. Created through the silent submission mode, but the list item has not been successfully submitted, and the submitted data still exists in the silentMethod instance; + +And here we will focus on the case of **2-2**. + +## Edit silent submit items + +In the previous chapters, we know that when the newly created data item has not been successfully submitted, the virtual data will be used as the placeholder for the id. Usually, we also get the data item through the id. At this time, we are in `useSQRequeset` Virtual data interception is implemented on the above, if a request is accompanied by virtual data information, it will be intercepted before sending and the data can be specified to replace the response data, and the request will be abandoned. + +Remember the **silentMethod.reviewData** saved in [Step 2 - Adjust Response Handling](/tutorial/strategy/sensorless-data-interaction/modify-response)? + +```javascript +onSuccess(({ silentMethod }) => { + // Construct list data items + const editingItem = { + ...detail, + id: id || data.id + }; + //... + if (silentMethod) { + // highlight-start + // Setting the name is to find the corresponding silentMethod instance when intercepting + silentMethod.entity.setName('edit' + editingItem.id); + silentMethod.reviewData = { + operate: id ? 'edit' : 'add', + data: editingItem + }; + // Don't forget to call save + silentMethod.save(); + // highlight-end + } +}); +``` + +It can be used not only for data compensation, but also for echoing data in edit pages. + +```javascript +const { loading, data } = useSQRequest(id => todoDetail(id), { + initialData: { + title: '', + time: new Date() + }, + immediate: false, + + // highlight-start + // Set the interception function, the function will be called when there is virtual data in this request + // If reviewData is returned, it will replace the response data and give up this request, otherwise the request will still be initiated + vDataCaptured: () => { + const targetSM = filterSilentMethods('edit' + todoId).pop(); + if (targetSM?.reviewData) { + return { ...targetSM.reviewData.data }; + } + } + // highlight-end +}); +``` + +:::warning Caution + +You can save enough data in **silentMethod.reviewData** to satisfy both list data compensation and edit page data echo. + +::: + +So far, data items created through silent submit mode also support editing! What's the problem, um... and one last one. + +## When the data item being edited is submitted successfully + +When the user is editing a data item that has not been successfully submitted, it suddenly submits successfully! At this time, we need to replace the virtual data used in the edit page with actual data, for example, replace the virtual id with the actual id, and use the actual id to submit in the next edit. This is also very simple, we only need to monitor This is done by silently submitting the success event, which will receive a data collection consisting of virtual data and real data. + +```javascript +import { onSilentSubmitSuccess, stringifyVData } from 'alova/client'; + +//... +// id is virtual data during initialization +let id = /* todo virtual id */; + +// highlight-start +// Binding listener silently submits the successful event to update the id, and returns the unbind function, don't forget to call the unbind function when the component is destroyed +const unbindEvent = onSilentSubmitSuccess(event => { + const vDataId = stringifyVData(id); + if (event.vDataResponse[vDataId]) { + id = event.vDataResponse[vDataId]; + + // The following is to change the virtual id in the url to the actual id + history.replaceState(null, '', '?id=' + currentId); + } +}); +// highlight-end +``` + +Here, the `event.vDataResponse` value is a collection of virtual data id and actual data, and its format is as follows: + +```javascript +{ + '[vd:aaaaaa]': { id: 1 }, + '[vd:bbbbbb]': 1 +} +``` + +So far, we have completed all the content of a simple list of non-inductive interaction, but in other application scenarios such as editing applications, complex list management, etc., we may encounter more different needs. What else does alova have at this time? What are the features we can use? Please read the next chapter! diff --git a/docs/tutorial/03-client/08-sensorless-data-interaction/09-what-more.md b/docs/tutorial/03-client/08-sensorless-data-interaction/09-what-more.md new file mode 100644 index 000000000..71d85cf3b --- /dev/null +++ b/docs/tutorial/03-client/08-sensorless-data-interaction/09-what-more.md @@ -0,0 +1,353 @@ +--- +title: What more? +sidebar_position: 90 +--- + +## Description of the role of virtual data + +In the previous chapters we used virtual data as the id placeholder, but its function is more than that, it can occupy any response data, for example, in a complex list, when creating a data item, the server needs to calculate additional Data, at this time, these additional data can also be occupied by virtual data, but this requires that the additional data needs to be returned together when the data item is created. See the following example: + +```javascript +const { onSuccess, send } = useSQRequest(createOrEditData, { + behavior: 'silent', + immediate: false, + + // Construct the same data structure as the response data + silentDefaultResponse: () => { + return { + id: '--', + extra1: '', + extra2: '' + }; + } +}); +onSuccess(event => { + event.data.id; // virtual data + event.data.extra1; //virtual data + event.data.extra2; //virtual data +}); +``` + +## New events in useSQRequest + +In order to better monitor the behavior of requests in the queue, `useSQRequset` also provides the following 3 additional event monitoring functions, you can obtain the binding functions in the following ways. + +```javascript +const { onBeforePushQueue, onPushedQueue, onFallback } = useSQRequest(/* ... */); +``` + +###onBeforePushQueue + +silentMethod is an event before entering the request queue. It is valid when the behavior mode is `queue` or `silent`. You can return `false` in this event callback to prevent the current silentMethod from entering the queue. For example, you may want to replace the current silentMethod with another one. It can be done like this: + +```javascript +//... +onBeforePushQueue(event => { + // Replace the old silentMethod with the specified id each time, reducing the number of requests + const prevSumbmitMethod = getSilentMethod('temp' + id); + if (event.silentMethod && prevSumbmitMethod) { + prevSumbmitMethod.replace(event.silentMethod); + return false; + } +}); +``` + +###onPushedQueue + +silentMethod The event after entering the queue. It is valid when the behavior mode is `queue` or `silent`. If the queue is blocked in the **onBeforePushQueue** event, this function will not trigger. + +### onFallback + +Similar to the traditional optimistic ui solution, we also provide a request rollback event, which will be triggered when the request reaches the maximum number of retries or the retry judgment fails. You can use it to handle some rollback operations. + +:::warning Warning + +When the fallback event is bound, even if the behavior mode is `silent`, the request will no longer be persisted, and it will be lost after refreshing the page. This is because the persistent silentMethod usually needs to ensure completion, not Rollbacks allow the user to re-process, in which case the rollback function should not be used. + +::: + +## Save additional operation data + +When creating or editing a data item, the previous chapters only saved the echo data to `silentMethod.reviewData`, if there are some additional data that need to be recorded, such as the menu options of the edit page, etc., we also need to record them to ensure They can also be selected when the network is disconnected. At this time, these data are mounted on the silentMethod instance and persisted together. + +Generally speaking, you can save persistent data with any property name, but an error will be reported in typescript, so the `silentMethod.extraData` attribute is specified for you as the storage field for extra data, remember to use `silentMethod.save()` for persistence data. + +## Custom serializer + +By default, alova uses localStorage for silentMethod data persistence, so it will call `JSON.stringify` to convert to a string when persisting, but json data only supports basic data types, pure objects and arrays, if you want Serialize special data structures such as Date instances, RegExp instances, functions, and custom class instances. Alova supports custom serializers to handle them. How to convert it to a data structure supported by json when storing it? How to convert to the original object structure. + +```javascript +const regExpSerializer = { + // forward is called when serializing + // Need to judge whether the data is the target value, if not, return undefined, indicating that it will not be processed + forward: data => data instanceof RegExp ? data.source : undefined, + + // backward is called during deserialization, data is the value returned in forward + backward: source => new RegExp(source); +}; + +bootSilentFactory({ + //... + // use this serializer via serializers + serializers: { + customRegExp: regExpSerializer + } +}) +``` + +SilentFactory provides Date and RegExp serializers by default, and you can also use the same key to override the default serializers + +```javascript +const defaultSerializers = { + Date: dateSerializer, + RegExp: regExpSerializer +}; +``` + +[Read Date serializer source code](https://github.com/alovajs/scene/blob/main/src/hooks/silent/serializer/date.ts) + +## Manipulate the silent queue + +Silent queues are used to ensure the timing of requests. We can create queues arbitrarily, and all requests entering the queue will be stored in the queue in the form of **SilentMethod** instances. Each **SilentMethod** not only contains request information, but also Contains relevant configuration for silent submission. Any number of silent queues can be generated, and it supports searching, modifying, and deleting silentMethod instances in the queue. + +### Using multiple silent queues + +Requests whose behavior mode is set to `queue` and `silent` will enter the silent queue. By default, the silentMethod instance will be assigned to the **default** queue. When it needs to be assigned to other queues, it can be assigned in _useSQRequest_ Specify the `queue` parameter. + +```javascript +useSQRequest(createOrEditTodo, { + //... + // Specify the silentMethod instance to enter the queue named customQueue + queue: 'customQueue', + behavior: 'silent' +}); +``` + +You can also specify `queue` as a function to return the name of the queue. This function will be called every time a request is made, and the function parameters come from the send function. + +```javascript +const { send } = useSQRequest(createOrEditTodo, { + //... + // Determine whether to enter the customQueue queue according to useCustomQueue + queue: useCustomQueue => (useCustomQueue ? 'customQueue' : 'default'), + behavior: 'silent', + immediate: false +}); +const handleClick = () => { + send(true); +}; +``` + +### Find silentMethod + +In the previous [data compensation](/tutorial/strategy/sensorless-data-interaction/data-compensation), we used [filterSilentMethods](/tutorial/strategy/sensorless-data-interaction/data-compensation#filtersilentmethods) to find the silentMethod of the specified queue instance, it will return all matching silentMethod instances, here are two more ways to find the queue: + +#### Find a silentMethod instance + +Use `getSilentMethod` to query the first matching silentMethod instance, the usage is the same as [filterSilentMethods](/tutorial/strategy/sensorless-data-interaction/data-compensation#filtersilentmethods). + +```typescript +function filterSilentMethods( + methodNameMatcher?: string | number | RegExp, + queueName?: string, + filterActive?: boolean +): SilentMethod | undefined; +``` + +#### Custom Lookup + +Customize the lookup through the exported `silentQueueMap` queue collection, the data structure of `silentQueueMap` is: + +```javascript +const silentQueueMap = { + default: [silentMethod1, silentMethod2 /* ... */], + queueName1: [silentMethod3, silentMethod4 /* ... */], + queueName2: [silentMethod5, silentMethod6 /* ... */] + //... +}; +``` + +### Change the silentMethod in the queue + +After finding the silentMethod instance you want, you can manipulate these waiting silentMethod instances. + +#### update silentMethod + +Call `silentMethod.save` to update this silentMethod data to cache. + +```javascript +silentMethod.extraData = { ... }; +await silentMethod.save(); +``` + +#### replace silentMethod + +Call `silentMethod.replace` to replace a silentMethod with another silentMethod in the queue. + +```javascript +oldSilentMethod.replace(newSilentMethod); +``` + +#### remove silentMethod + +Call `silentMethod.remove` to remove the current silentMethod from the queue. + +```javascript +oldSilentMethod.remove(); +``` + +#### Use silentQueueMap to change silentMethod + +You can also access `silentQueueMap` to custom change any data of any queue. + +```javascript +import { silentQueueMap } from 'alova/client'; + +// Modify all silentMethods in the default queue +silentQueueMap.default.forEach(silentMethodItem => { + //... +}); +``` + +## Queue request delay + +Some applications need to submit data frequently, such as editor-type applications, which are saved in real time during the editing process without aborting the user's use. When using silent submission in this type of application, more request information will be generated, not only It will fill up the front-end cache and make the server receive too many requests. At this time, we may no longer need to synchronize all save operations, but send an operation within a period of time. There will be the following two solutions: + +1. Throttle the editing operation, and only initiate one submission within n seconds. This solution may lose the operation records during the delay period, resulting in only the status of the last submission being obtained when refreshing; +2. Delay the save request in the queue, and only keep the latest request information during the delay time, so that you can reduce the request while retaining the latest editing status; + +By default, the silentMethod in the queue will send the request immediately after the last response. We can set the delay sending time of the silentMethod through `requestWait` at startup, and keep the latest silentMethod by manipulating the queue during this time. + +### Set request delay + +You can set the delay time for the specified queue, or you can set multiple queues at once. + +```javascript +bootSilentFactory({ + alova: alovaInst, + // highlight-start + requestWait: [ + // Each silentMethod in the customQueue queue will delay 5000ms to initiate a request + { queue: 'customQueue', wait: 5000 }, + + //Use a regular expression to uniformly set a 3000ms delay request time for the queue whose name is prefixed with delay + { queue: /^delay/, wait: 3000 } + ] + // highlight-end +}); +``` + +When `requestWait` is directly set to a number, it is valid for the default queue by default. + +```javascript +bootSilentFactory({ + alova: alovaInst, + // highlight-start + // Each silentMethod in the default queue will delay 5000ms to initiate a request + requestWait: 5000 + // highlight-end +}); +``` + +### Dynamically set the request delay + +Many times we only want a specific silentMethod in the queue to set a delay request. At this time, a function can be used to dynamically set the request delay. Each silentMethod of the specified queue will call this function before initiating a request to determine the delay time. + +```javascript +bootSilentFactory({ + alova: alovaInst, + // highlight-start + requestWait: [ + { + queue: /^delay, + // Only delay 5000ms for post requests with url /edit + wait: (silentMethod, queueName) => { + const { type, url, data } = silentMethod.entity; + if (type === 'POST' && url === '/edit') { + return 5000; + } + } + }, + ] + // highlight-end +}); +``` + +Similarly, when `requestWait` is set directly to a function, it defaults to the default queue. + +```javascript +bootSilentFactory({ + alova: alovaInst, + // highlight-start + requestWait: (silentMethod, queueName) => { + const { type, url, data } = silentMethod.entity; + if (type === 'POST' && url === '/edit') { + return 5000; + } + } + // highlight-end +}); +``` + +## Dynamically set the method name + +For easy search, if you need to dynamically set the name of the method in silentMethod, you can call setName. + +```javascript +// Reset the name of silentMethod on successful request +onSuccess(({ data, silentMethod }) => { + silentMethod.entity.setName('name' + data.id); +}); +``` + +## Global silent submit event + +In the previous chapter, we touched `onSilentSubmitSuccess`, we provided a total of 5 global events. + +### onSilentSubmitBoot + +Silent factory start event, triggered after the silent factory is started. + +```typescript +function onSilentSubmitBoot(handler: () => void): OffEventCallback; +``` + +###onBeforeSilentSubmit + +Fired before a silentMethod request with `behavior=silent`. + +```typescript +function onBeforeSilentSubmit(handler: (event: GlobalSQEvent)): OffEventCallback; +``` + +###onSilentSubmitSuccess + +Fired when a silentMethod request with `behavior=silent` succeeds. + +```typescript +function onSilentSubmitSuccess( + handler: (event: GlobalSQSuccessEvent) => void +): OffEventCallback; +``` + +### onSilentSubmitError + +Fired when the silentMethod request with `behavior=silent` fails, but the maximum number of retries has not been reached. + +```typescript +function onSilentSubmitError(handler: (event: GlobalSQErrorEvent) => void): OffEventCallback; +``` + +### onSilentSubmitFail + +When the silentMethod of `behavior=silent` encounters a request failure, the following 3 situations will trigger this event: + +1. Triggered when the request reaches the maximum number of retries; +2. When the number of retries is not set, the first request failure will trigger; +3. Triggered when the request has not reached the maximum number of retries, but the retry is judged to be no more retries; + +```typescript +function onSilentSubmitFail(handler: (event: GlobalSQFailEvent) => void): OffEventCallback; +``` + +The above event binding functions will return the unbinding function, and you can unbind the event before the component is unmounted. diff --git a/docs/tutorial/05-strategy/01-sensorless-data-interaction/README.md b/docs/tutorial/03-client/08-sensorless-data-interaction/README.md similarity index 100% rename from docs/tutorial/05-strategy/01-sensorless-data-interaction/README.md rename to docs/tutorial/03-client/08-sensorless-data-interaction/README.md diff --git a/docs/tutorial/05-strategy/01-sensorless-data-interaction/_category_.json b/docs/tutorial/03-client/08-sensorless-data-interaction/_category_.json similarity index 100% rename from docs/tutorial/05-strategy/01-sensorless-data-interaction/_category_.json rename to docs/tutorial/03-client/08-sensorless-data-interaction/_category_.json diff --git a/docs/tutorial/03-client/09-use-captcha.md b/docs/tutorial/03-client/09-use-captcha.md new file mode 100644 index 000000000..8213d8401 --- /dev/null +++ b/docs/tutorial/03-client/09-use-captcha.md @@ -0,0 +1,180 @@ +--- +title: send captcha +sidebar_position: 90 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info strategy type + +use hook + +::: + +> Before using extension hooks, make sure you are familiar with the basic usage of alova. + +The verification code sending hook saves you the trouble of developing the verification code sending function. + +## Example + +[Send Captcha Demo](/tutorial/example/captcha-send) + +## Features + +- ✨ The countdown will start automatically after the verification code is sent; +- ✨ Custom countdown seconds; +- ✨ Verification code sending limit; + +## Usage + +## Basic usage + +Demonstrates basic use of form hooks. + + + + +```html + + + +``` + + + + +```jsx +import { useState } from 'react'; +import { apiSendCaptcha } from './api.js'; +import { useCaptcha } from 'alova/client'; + +const App = () => { + const [mobile, setMobile] = ref(''); + const { + // send status + loading: sending, + + // Call sendCaptcha to request the interface to send the verification code + send: sendCaptcha + } = useCaptcha(() => apiSendCaptcha(mobile)); + + return ( +
+ setMobile(target.value)} + /> + +
+ ); +}; +``` + +
+ + +```html + + + + +``` + + +
+ +By default, after the verification code is successfully sent, it will count down for 60 seconds, and calling `send` when the countdown is not over will throw an error. + +### Custom countdown seconds + +You can also customize the countdown seconds + +```javascript +useCaptcha(() => apiSendCaptcha(mobile.value), { + //... + // highlight-start + // Set the countdown to 20 seconds + initialCountdown: 20 + // highlight-end +}); +``` + +## API + +### Hook configuration + +Inherit all configurations of [**useRequest**](/api/core-hooks#userequest) except `immediate`, `immediate` in `useCaptcha` has been hard-coded to false. + +| Name | Description | Type | Default | Version | +| ---------------- | ---------------------------------------------------------------------------------------------------------- | ------ | ------- | ------- | +| initialCountdown | Initial countdown, when the verification code is sent successfully, it will start countdown with this data | number | 60 | - | + +### Responsive data + +Inherit all responsive data from [**useRequest**](/api/core-hooks#userequest). + +| Name | Description | Type | Version | +| --------- | ----------------------------------------------------------------------------------------------------------- | ------ | ------- | +| countdown | The current countdown, -1 per second, the verification code can only be sent again after the countdown ends | number | - | + +### Action function + +Inherit all action functions of [**useRequest**](/api/core-hooks#userequest). + +| name | description | function parameters | return value | version | +| ---- | ---------------------------------------------------------------------------- | ------------------------------- | ------------------- | ------- | +| send | Send a request, when the countdown is not over, the call will throw an error | Consistent with useRequest.send | Promise\ | - | + +### Event + +Inherit all events from [**useRequest**](/api/core-hooks#userequest). diff --git a/docs/tutorial/03-client/10-use-serial-request.md b/docs/tutorial/03-client/10-use-serial-request.md new file mode 100644 index 000000000..6950827b6 --- /dev/null +++ b/docs/tutorial/03-client/10-use-serial-request.md @@ -0,0 +1,107 @@ +--- +title: useRequest with serial +sidebar_position: 100 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info strategy type + +use hook + +::: + +> Before using extension hooks, make sure you are familiar with the basic usage of alova. + +This use hook is more concise and easy to use than [serial request in best practice](/tutorial/best-practice/skills), with unified loading status, error, and callback functions. + +## Features + +- ✨ A more concise and easy-to-use serial method; +- ✨Unified request status and callback function; +- ✨ send function can trigger serial execution of multiple requests; + +## Example + +[serial request](/tutorial/example/serial-request) + +## Usage + +### Basic usage + +Same usage as `useRequest`, except that the first parameter is changed to an array of handlers executed serially, and each handler will receive the response data of the previous request. + +```javascript +import { useSerialRequest } from 'alova/client'; + +const { + // Serial loading status, all requests will be changed to false + loading, + + // The response data of the last request + data, + + // Any request error will record the error message here + error, + + // Manually send a serial request + send, + + // serial request success callback binding function + onSuccess, + + // Serial request error callback binding function, any request error will trigger it + onError, + + // Serial request completion callback binding function + onComplete +} = useSerialRequest( + [ + // args is the parameter passed in by the send function + (...args) => request1(args), + + // Starting from the second handler, the first parameter is the response data of the previous request, and args is received from the second + (response1, ...args) => request2(response1, args), + (response2, ...args) => request3(response2, args) + ], + { + immediate: false + } +); + +// emit request manually and pass arguments +send(1, 2, 3); +``` + +It is worth noting that the first item in the handler array can also be specified as a method instance, and the second item must be a function. + +```javascript +useSerialRequest([ + methodInstance, + (response1, ...args) => request2(response1, args), + (response2, ...args) => request3(response2, args) +]); +``` + +### Request error + +When any of the serial requests is wrong, `onError` will be triggered, and its `event.method` will point to the method instance of the request error. + +## API + +### Hook configuration + +Inherit all configurations from [**useRequest**](/api/core-hooks#userequest). + +### Responsive data + +Inherit all responsive data from [**useRequest**](/api/core-hooks#userequest). + +### Action function + +Inherit all action functions of [**useRequest**](/api/core-hooks#userequest). + +### Event + +Inherit all events from [**useRequest**](/api/core-hooks#userequest). diff --git a/docs/tutorial/03-client/11-use-serial-watcher.md b/docs/tutorial/03-client/11-use-serial-watcher.md new file mode 100644 index 000000000..3141d55b0 --- /dev/null +++ b/docs/tutorial/03-client/11-use-serial-watcher.md @@ -0,0 +1,108 @@ +--- +title: useWatcher with serial +sidebar_position: 110 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info policy type + +use hook + +::: + +> Before using extension hooks, make sure you are familiar with the basic usage of alova. + +Status update triggers a set of serial requests, which is more concise and easy to use than [serial request in best practice](/tutorial/best-practice/skills), with unified loading status, error, and callback functions. + +## Features + +- ✨ A more concise and easy-to-use serial method; +- ✨Unified request status and callback function; +- ✨Status update triggers serial execution of multiple requests; + +## Example + +[serial request](/tutorial/example/serial-request) + +## Usage + +### Basic usage + +Same usage as `useWatcher`, except that the first parameter is changed to an array of handlers executed serially, and each handler will receive the response data of the previous request. + +```javascript +import { useSerialWatcher } from 'alova/client'; + +const { + // Serial loading status, all requests will be changed to false + loading, + + // The response data of the last request + data, + + // Any request error will record the error message here + error, + + // Manually send a serial request + send, + + // serial request success callback binding function + onSuccess, + + // Serial request error callback binding function, any request error will trigger it + onError, + + // Serial request completion callback binding function + onComplete +} = useSerialWatcher( + [ + // args is the parameter passed in by the send function + (...args) => request1(args), + + // Starting from the second handler, the first parameter is the response data of the previous request, and args is received from the second + (response1, ...args) => request2(response1, args), + (response2, ...args) => request3(response2, args) + ], + [watchedState1, watchedState2], + { + immediate: true + } +); + +// Manually trigger the request and pass parameters +send(1, 2, 3); +``` + +It is worth noting that the first item in the handler array can also be specified as a method instance, and the second item must be a function. + +```javascript +useSerialRequest([ + methodInstance, + (response1, ...args) => request2(response1, args), + (response2, ...args) => request3(response2, args) +]); +``` + +### Request error + +When any of the serial requests is wrong, `onError` will be triggered, and its `event.method` will point to the method instance of the request error. + +## API + +### Hook configuration + +Inherit all configurations of [**useWatcher**](/api/core-hooks#usewatcher). + +### Responsive data + +Inherit all responsive data from [**useWatcher**](/api/core-hooks#usewatcher). + +### Action function + +Inherit all action functions of [**useWatcher**](/api/core-hooks#usewatcher). + +### Event + +Inherit all events from [**useWatcher**](/api/core-hooks#usewatcher). diff --git a/docs/tutorial/03-client/12-use-retriable-request.md b/docs/tutorial/03-client/12-use-retriable-request.md new file mode 100644 index 000000000..a2b7fa10b --- /dev/null +++ b/docs/tutorial/03-client/12-use-retriable-request.md @@ -0,0 +1,232 @@ +--- +title: retriable request +sidebar_position: 120 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info strategy type + +use hook + +::: + +> Before using extension hooks, make sure you are familiar with the basic usage of alova. + +A use hook that can automatically retry a request failure, you can use it for important requests. + +## Example + +[Request Retry Demo](/tutorial/example/retriable-hook) + +## Features + +- ✨Customize the number of retries or judge whether retry is required according to the conditions; +- ✨ Retry delay mechanism; +- ✨ Manually stop retrying; + +## Usage + +### Basic usage + +```javascript +import { useRetriableRequest } from 'alova/client'; + +const { + // Loading status, always true during retry, until retry succeeds or fails + loading, + + // response data + data, + + // Request error information, every time a request or retry fails, there will be an error instance + // The last error instance will be overwritten + error, + + // Every time a request or retry fails, the onError event will be triggered + onError, + + // Request retry event, triggered immediately after each retry request is issued + onRetry, + + // request retry failure event + // The request is not successful after reaching the maximum number of retries, or manually stopping retries will trigger + onFail, + + // request or retry success event + onSuccess, + + // Every request or retry, regardless of success or failure, will trigger the completion event + onComplete +} = useRetriableRequest(request); +``` + +The maximum number of request retries for `useRetriableRequest` defaults to 3, and each retry will be delayed by 1 second. It will also make a request by default, you can change the behavior by setting `immediate` to false. + +### Set the static maximum number of retries + +The maximum number of retries indicates the maximum number of times to retry the request after the first request fails. During this period, if the request succeeds, it will stop continuing to retry. The default maximum number of retries is 3, and you can customize the settings in the following ways. + +When the request reaches the maximum number of retries and still fails, the `onFail` event will be triggered and the request retry will stop. If you want to continue to retry after the failure, you can call the `send` function, and it will perform a new round request and retry. + +```javascript +const { send } = useRetriableRequest(request, { + //... + // highlight-start + // Set the maximum number of retries to 5 + retry: 5 + // highlight-end +}); +``` + +### Dynamically set the maximum number of retries + +Maybe sometimes you want to use a certain condition to determine whether to continue to retry. At this time, you can set `retry` as a function that returns a boolean value to dynamically determine whether to continue to retry. + +```javascript +useRetriableRequest(request, { + //... + // highlight-start + // The first parameter is the last error instance, and the parameters passed in from the second parameter to send + retry(error, ...args) { + // If the request times out, continue to retry + return /network timeout/i.test(error.message); + } + // highlight-end +}); +``` + +### Set delay time + +The default retry delay time is 1 second, you can customize the setting in the following ways. + +```javascript +useRetriableRequest(request, { + //... + backoff: { + // highlight-start + // Set the delay time to 2 seconds + delay: 2000 + // highlight-end + } +}); +``` + +### Set an unfixed retry delay time + +Sometimes you want that the delay time of each request is not fixed, you can set the delay growth multiple in the following way, and the delay time will increase exponentially according to the number of retries. + +```javascript +useRetriableRequest(request, { + //... + backoff: { + delay: 2000, + // highlight-start + // When the multiplier is set to 2, the first retry delay is 2 seconds, the second is 4 seconds, the third is 8 seconds, and so on. + multiplier: 2 + // highlight-end + } +}); +``` + +not enough? You can even add a random jitter value to each delay to make it look less regular. + +```javascript +useRetriableRequest(request, { + //... + backoff: { + delay: 2000, + multiplier: 2, + // highlight-start + /** + * The initial jitter percentage value of the delay request, the range is 0-1 + * When only startQuiver is set, endQuiver defaults to 1 + * For example set to 0.5, it will add 50% to 100% random time on the current delay time + * If endQuiver has a value, the delay time will be increased by a random value in the range of startQuiver and endQuiver + */ + startQuiver: 0.5, + + /** + * The jitter end percentage value of the delayed request, the range is 0-1 + * When only endQuiver is set, startQuiver defaults to 0 + * For example set to 0.8, it will add a random time from 0% to 80% on the current delay time + * If startQuiver has a value, the delay time will increase the random value in the range of startQuiver and endQuiver + */ + endQuiver: 0.8; + // highlight-end + } +}); +``` + +### Manually stop retrying + +In some cases, you need to manually stop the retry, whether you are currently requesting or waiting for the next retry, you can use `stop` to stop it. + +```javascript +const { stop } = useRetriableRequest(request, { + //... +}); + +const handleStop = () => { + stop(); +}; +``` + +## API + +### Hook configuration + +Inherit all configurations from [**useRequest**](/api/core-hooks#userequest). + +| Name | Description | Type | Default | Version | +| ------- | -------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- | ----------------------------------------- | ------- | --- | +| retry | The maximum number of retries can also be set as a function returning a boolean value to dynamically determine whether to continue to retry. | number | (error: Error, ...args: any[]) => boolean | 3 | - | +| backoff | Backoff policy, set retry delay time, etc. | [BackoffPolicy](#backoffpolicy) | - | - | + +### BackoffPolicy + +| Name | Description | Type | Default | Version | +| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | ------- | ------- | +| delay | Delay time for another request, in milliseconds | number | 1000 | - | +| multiplier | Specify the delay multiplier, for example, when multiplier is set to 2 and delay is 1 second, the first retry is 1 second, the second is 2 seconds, the third is 4 seconds, and so on | number | 0 | - | +| startQuiver | The initial jitter percentage value of the delay request, ranging from 0-1. When only startQuiver is set, endQuiver defaults to 1. For example, if it is set to 0.5, it will increase the current delay time by 50% to 100% randomly Time, if endQuiver has a value, the delay time will be increased by a random value in the range of startQuiver and endQuiver | number | 0 | - | +| endQuiver | The jitter end percentage value of the delayed request, the range is 0-1, when onlyWhen endQuiver is set, startQuiver defaults to 0. For example, if it is set to 0.5, it will add a random time from 0% to 50% to the current delay time. If startQuiver has a value, the delay time will increase the random value in the range of startQuiver and endQuiver | number | 0 | - | + +### Responsive data + +Inherit all responsive data from [**useRequest**](/api/core-hooks#userequest). + +### Action function + +Inherit all action functions of [**useRequest**](/api/core-hooks#userequest). + +| name | description | function parameters | return value | version | +| ---- | ------------------------------------------------------------------------------------------------------------------ | ------------------- | ------------ | ------- | +| stop | Stop retrying, it is only valid during retrying, and the onFail event will be triggered immediately after stopping | - | - | - | + +### Event + +Inherit all events from [**useRequest**](/api/core-hooks#userequest). + +| Name | Description | Callback Parameters | Version | +| ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | ------- | +| onRetry | Retry event bindings, they will fire after a retry is initiated | Retry event instance [RetriableRetryEvent](#retriableretryevent) | - | +| onFail | Triggered when the request fails. It will be triggered when no more retries are made. For example, when the maximum number of retries is reached, when the retry callback returns false, manually call stop to stop retrying
Note:
1 The .onError event will be triggered every time an error is reported.
2. If there are no retries, onError, onComplete and onFail will be triggered at the same time | Retry event instance [RetriableFailEvent](#retriablefailevent) | - | + +#### RetriableRetryEvent + +Event event instance inherited from alova. + +| Name | Description | Type | Default | Version | +| ---------- | ----------------------------------- | ------ | -------- | ------- | +| retryTimes | current retry times | number | required | - | +| retryDelay | The delay time of this retry, in ms | number | required | - | + +#### RetriableFailEvent + +Event event instance inherited from alova. + +| Name | Description | Type | Default | Version | +| ---------- | ------------------- | ------ | -------- | ------- | +| retryTimes | current retry times | number | required | - | diff --git a/docs/tutorial/03-client/13-use-sse.md b/docs/tutorial/03-client/13-use-sse.md new file mode 100644 index 000000000..12053d850 --- /dev/null +++ b/docs/tutorial/03-client/13-use-sse.md @@ -0,0 +1,248 @@ +--- +title: request by server-send events +sidebar_position: 130 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info strategy type + +use hook + +::: + +> Before using extension hooks, make sure you are familiar with the basic usage of alova. + +A use hook that can automatically retry a request failure, you can use it for important requests. + +## Features + +- ✨ Simpler and easier-to-use usage. +- ✨ Automatic connection management. + +## Usage + + + + +```typescript +import { useSSE } from 'alova/client'; + +const method = (value: string) => alova.Get('/api/source', { param: { key: value } }); +const { data, eventSource, readyState, onMessage, onError, on, send, close } = useSSE(method, { + initialData: 'initial-data' // Data initially in `data` +}); + +// Connect +send('value'); + +console.log(data.value); // Data is updated after receiving an event, by default it is `initialData` + +// Corresponds to the `message` event of `eventsource` +const unbindMessage = onMessage(({ data }) => { + console.log(data); +}); + +const unbindError = onError(({ error }) => { + console.error('sse error', error); + close(); +}); + +// Unbind when needed +unbindMessage(); +unbindError(); +``` + + + + +```typescript +import { useSSE } from 'alova/client'; + +const method = (value: string) => alova.Get('/api/source', { param: { key: value } }); +const { data, eventSource, readyState, onMessage, onError, on, send, close } = useSSE(method, { + initialData: 'initial-data' // Data initially in `data` +}); + +// Connect +send('value'); + +console.log(data); // Data is updated after receiving an event, by default it is `initialData` + +// Corresponds to the `message` event of `eventsource` +const unbindMessage = onMessage(({ data }) => { + console.log(data); +}); + +const unbindError = onError(({ error }) => { + console.error('sse error', error); + close(); +}); + +// Unbind when needed +unbindMessage(); +unbindError(); +``` + + + + +```typescript +import { useSSE } from 'alova/client'; + +const method = (value: string) => alova.Get('/api/source', { param: { key: value } }); +const { data, eventSource, readyState, onMessage, onError, on, send, close } = useSSE(method, { + initialData: 'initial-data' // Data initially in `data` +}); + +// Connect +send('value'); + +console.log(data); // Data is updated after receiving an event, by default it is `initialData` + +// Corresponds to the `message` event of `eventsource` +const unbindMessage = onMessage(({ data }) => { + console.log(data); +}); + +const unbindError = onError(({ error }) => { + console.error('sse error', error); + close(); +}); + +// Unbind when needed +unbindMessage(); +unbindError(); +``` + + + + +:::warning + +Currently, `useSSE` can only connect to one source. This means that when attempting to connect to multiple targets, the previous connection will always be terminated. + +::: + +```typescript +const { data, eventSource, readyState, onMessage, onError, on, send, close } = useSSE(method); + +send('value1'); +// highlight-start +send('value2'); // This will terminate the previous connection +send('value3'); // This will also terminate the previous connection +// highlight-end +``` + +By default, no request is sent. However, by setting immediate = true, you can skip the manual send step. + +```typescript +const { data, eventSource, readyState, onMessage, onError, on, send, close } = useSSE(method, { + // highlight-start + immediate: true + // highlight-end +}); + +// codes here... +``` + +### Binding Custom Events + +```typescript +const { data, readyState, onMessage, on } = useSSE(method); + +on('event-name', ({ data }) => { + console.log(data); +}); +``` + +### Global Response Interception + +By default, the response data is captured by [the global response interceptors](/tutorial/combine-framework/response). If this is not the desired behavior, you can manually disable it. + +```typescript +const { data, readyState, onMessage, on } = useSSE(method, { + // highlight-start + interceptByGlobalResponded: false // Now the data will not be intercepted by the response interceptor + // highlight-end +}); +``` + +## Type Declaration + +```typescript +const enum SSEHookReadyState { + CONNECTING = 0, + OPEN = 1, + CLOSED = 2 +}; + +type SSEHookConfig = { + /** + * Passed to new EventSource + */ + withCredentials?: boolean; + + /** + * Whether to be intercepted by the global responded interceptor of Alova instance + * @default true + */ + interceptByGlobalResponded?: boolean; + + /** + * Initial data + */ + initialData?: any; + + /** + * Whether to initiate the request immediately + * @default false + */ + immediate?: boolean; +}; + +type SSEReturnType = { + readyState: ExportedType; + data: ExportedType; + eventSource: ExportedType; + /** + * Manually initiate the request. When `immediate: true` is used, this method is triggered automatically. + * @param sendArgs Request parameters passed to the method + */ + send: (...sendArgs: any[]) => Promise; + /** + * Close the connection + */ + close: () => void; + /** + * Register a callback function for the EventSource 'open' event + * @param callback Callback function + * @returns Function to unregister the callback + */ + onOpen(callback: SSEOnOpenTrigger): () => void; + + /** + * Register a callback function for the EventSource 'message' event + * @param callback Callback function + * @returns Function to unregister the callback + */ + onMessage(callback: SSEOnMessageTrigger): () => void; + + /** + * Register a callback function for the EventSource 'error' event + * @param callback Callback function + * @returns Function to unregister the callback + */ + onError(callback: SSEOnErrorTrigger): () => void; + + /** + * @param eventName Event name, defaults to 'open' | 'error' | 'message' + * @param handler Event handler + */ + on( + eventName: string, + handler: (event: AlovaSSEMessageEvent) => void + ) => () => void; +}; +``` diff --git a/docs/tutorial/03-client/14-use-breakpoint-uploader.md b/docs/tutorial/03-client/14-use-breakpoint-uploader.md new file mode 100644 index 000000000..3bd88eda8 --- /dev/null +++ b/docs/tutorial/03-client/14-use-breakpoint-uploader.md @@ -0,0 +1,6 @@ +--- +title: Breakpoint upload +sidebar_position: 140 +--- + +coming soon... diff --git a/docs/tutorial/03-client/15-use-uploader.md b/docs/tutorial/03-client/15-use-uploader.md new file mode 100644 index 000000000..3d646cd51 --- /dev/null +++ b/docs/tutorial/03-client/15-use-uploader.md @@ -0,0 +1,6 @@ +--- +title: Universal upload strategy +sidebar_position: 150 +--- + +coming soon... diff --git a/docs/tutorial/03-client/16-rate-limit-middleware.md b/docs/tutorial/03-client/16-rate-limit-middleware.md new file mode 100644 index 000000000..dbe5bea83 --- /dev/null +++ b/docs/tutorial/03-client/16-rate-limit-middleware.md @@ -0,0 +1,8 @@ +--- +title: Request rate limit +sidebar_position: 160 +--- + +Set the number of requests that should be executed immediately for each interval, and other requests will be automatically delayed. + +coming soon... diff --git a/docs/tutorial/03-combine-framework/10-typescript.md b/docs/tutorial/03-client/20-typescript.md similarity index 100% rename from docs/tutorial/03-combine-framework/10-typescript.md rename to docs/tutorial/03-client/20-typescript.md diff --git a/docs/tutorial/03-client/README.md b/docs/tutorial/03-client/README.md new file mode 100644 index 000000000..84f84a380 --- /dev/null +++ b/docs/tutorial/03-client/README.md @@ -0,0 +1,19 @@ +--- +title: Client Strategy +--- + +import DocCardList from '@theme/DocCardList'; + +Like using a component library, just learn it when you need a request strategy! + +All client use hooks have the following in common: + +1. They all rely on statesHook, please [set statesHook](/tutorial/getting-started/combine-framework) before using. + +2. Their return values ​​all contain the `update` function, which is used to actively update the exported state value. + +3. For performance under react, all operation functions such as `send`, `update`, `abort`, etc. are wrapped with `useCallback`. + +## Table of contents + + diff --git a/docs/tutorial/03-client/_category_.json b/docs/tutorial/03-client/_category_.json new file mode 100644 index 000000000..2df63af18 --- /dev/null +++ b/docs/tutorial/03-client/_category_.json @@ -0,0 +1,3 @@ +{ + "label": "Client Strategy" +} diff --git a/docs/tutorial/04-server/01-retry.md b/docs/tutorial/04-server/01-retry.md new file mode 100644 index 000000000..221edfbd9 --- /dev/null +++ b/docs/tutorial/04-server/01-retry.md @@ -0,0 +1,129 @@ +--- +title: Request retry strategy +sidebar_position: 10 +--- + +:::info type + +server hook + +::: + +Request retry strategy, you can use it for important requests. + +## Features + +- ✨ Customize the number of retries or determine whether to retry based on conditions; + +- ✨ Retry delay mechanism; + +## Usage + +### Basic usage + +```javascript +const { retry } = require('alova/server'); +const { alovaInstance } = require('./api'); + +const request = alovaInstance.Get('/api/user'); +const hookedMethod = retry(request); +``` + +`retry`'s maximum request retry count defaults to 3, and each retry will be delayed by 1 second. + +### Set the maximum number of static retries + +The maximum number of retries indicates the maximum number of times a request is retried after the first request fails. If the request succeeds during this period, it will stop retrying. The default maximum number of retries is 3 times, and you can customize the setting in the following way. + +```javascript +const hookedMethod = retry(request, { + // ... + // highlight-start + // Set the maximum number of retries to 5 + retry: 5 + // highlight-end +}); +``` + +### Dynamically set the maximum number of retries + +Sometimes you may want to determine whether to continue retrying based on a certain condition. In this case, you can set `retry` to a function that returns a boolean value to dynamically determine whether to continue retrying. + +```javascript +const hookedMethod = retry(request, { + // ... + // highlight-start + // The first parameter is the last error instance, and the second parameter is the parameter passed in by send + retry(error) { + // If the request times out, continue to retry + return /network timeout/i.test(error.message); + } + // highlight-end +}); +``` + +### Set the delay time + +The default retry delay time is 1 second, and you can customize it in the following way. + +```javascript +retry(request, { + // ... + backoff: { + // highlight-start + // Set the delay time to 2 seconds + delay: 2000 + // highlight-end + } +}); +``` + +### Set an unfixed retry delay time + +Sometimes you want the delay time of each request to be not fixed. You can set the delay growth multiple as follows. The delay time will increase exponentially according to the number of retries. + +```javascript +retry(request, { + // ... + backoff: { + delay: 2000, + // highlight-start + // When multiplier is set to 2, the first retry delay is 2 seconds, the second is 4 seconds, the third is 8 seconds, and so on. + multiplier: 2 + // highlight-end + } +}); +``` + +Not enough? You can even add a random jitter value to each delay time to make it look less regular. + +```javascript +retry(request, { +// ... +backoff: { +delay: 2000, +multiplier: 2, +// highlight-start +/** +* The starting percentage value of the delay request jitter, ranging from 0-1 +* When only startQuiver is set, endQuiver defaults to 1 +* For example, if it is set to 0.5, it will add a random time of 50% to 100% to the current delay time +* If endQuiver has a value, the delay time will increase by a random value in the range of startQuiver and endQuiver +*/ +startQuiver: 0.5, + +/** +* The ending percentage value of the delay request jitter, ranging from 0-1 +* When only endQuiver is set, startQuiver defaults to 0 +* For example, if it is set to 0.8, it will add a random time of 0% to 80% to the current delay time +* If startQuiver has a value, the delay time will increase by a random value in the range of startQuiver and endQuiver +*/ +endQuiver: 0.8; +// highlight-end +} +}); +``` + +## API + +Please refer to [API-retry](/api/core-hooks#usewatcher). diff --git a/docs/tutorial/04-server/02-send-captcha.md b/docs/tutorial/04-server/02-send-captcha.md new file mode 100644 index 000000000..e25cf25d6 --- /dev/null +++ b/docs/tutorial/04-server/02-send-captcha.md @@ -0,0 +1,8 @@ +--- +title: Send Captcha +sidebar_position: 20 +--- + +Send the captcha via SMS or email, and record the countdown time according to the key. If you send the captcha again within the countdown, an error will be reported. + +coming soon... diff --git a/docs/tutorial/04-server/03-rate-limit-middleware.md b/docs/tutorial/04-server/03-rate-limit-middleware.md new file mode 100644 index 000000000..50eaf516c --- /dev/null +++ b/docs/tutorial/04-server/03-rate-limit-middleware.md @@ -0,0 +1,8 @@ +--- +title: Request Rate Limit +sidebar_position: 30 +--- + +Set the number of requests that should be executed immediately per interval, other requests will be automatically delayed. + +Coming soon... diff --git a/docs/tutorial/04-server/README.md b/docs/tutorial/04-server/README.md new file mode 100644 index 000000000..8ea006bf3 --- /dev/null +++ b/docs/tutorial/04-server/README.md @@ -0,0 +1,24 @@ +--- +title: Server Strategy +--- + +import DocCardList from '@theme/DocCardList'; + +Like using a component library, you can learn it when you need a specific request strategy! + +The following is the specification of server hooks, which receives a method instance and returns a new method instance, so it is very convenient to combine multiple server hooks. + +```ts +/** + * Server hook model, representing the types of all server hooks. + * Pass a method or hooked method instance, set options, and return a hooked method instance. + * You can continue to modify this method to achieve the effect of combining multiple server hooks. + */ +export interface AlovaServerHook> { + (method: Method, options: Options): Method; +} +``` + +## Table of contents + + diff --git a/docs/tutorial/04-server/_category_.json b/docs/tutorial/04-server/_category_.json new file mode 100644 index 000000000..90ea63292 --- /dev/null +++ b/docs/tutorial/04-server/_category_.json @@ -0,0 +1,3 @@ +{ + "label": "Server Strategy" +} diff --git a/docs/tutorial/07-best-practice/01-manage-apis.md b/docs/tutorial/05-best-practice/01-manage-apis.md similarity index 100% rename from docs/tutorial/07-best-practice/01-manage-apis.md rename to docs/tutorial/05-best-practice/01-manage-apis.md diff --git a/docs/tutorial/07-best-practice/02-skills.md b/docs/tutorial/05-best-practice/02-skills.md similarity index 100% rename from docs/tutorial/07-best-practice/02-skills.md rename to docs/tutorial/05-best-practice/02-skills.md diff --git a/docs/tutorial/07-best-practice/03-manage-cache-by-indexeddb.md b/docs/tutorial/05-best-practice/03-manage-cache-by-indexeddb.md similarity index 100% rename from docs/tutorial/07-best-practice/03-manage-cache-by-indexeddb.md rename to docs/tutorial/05-best-practice/03-manage-cache-by-indexeddb.md diff --git a/docs/tutorial/07-best-practice/04-multiple-servers.md b/docs/tutorial/05-best-practice/04-multiple-servers.md similarity index 100% rename from docs/tutorial/07-best-practice/04-multiple-servers.md rename to docs/tutorial/05-best-practice/04-multiple-servers.md diff --git a/docs/tutorial/07-best-practice/05-middleware.md b/docs/tutorial/05-best-practice/05-middleware.md similarity index 100% rename from docs/tutorial/07-best-practice/05-middleware.md rename to docs/tutorial/05-best-practice/05-middleware.md diff --git a/docs/tutorial/07-best-practice/_category_.json b/docs/tutorial/05-best-practice/_category_.json similarity index 100% rename from docs/tutorial/07-best-practice/_category_.json rename to docs/tutorial/05-best-practice/_category_.json diff --git a/docs/tutorial/04-cache/01-mode.md b/docs/tutorial/07-cache/01-mode.md similarity index 100% rename from docs/tutorial/04-cache/01-mode.md rename to docs/tutorial/07-cache/01-mode.md diff --git a/docs/tutorial/04-cache/02-auto-invalidate.md b/docs/tutorial/07-cache/02-auto-invalidate.md similarity index 100% rename from docs/tutorial/04-cache/02-auto-invalidate.md rename to docs/tutorial/07-cache/02-auto-invalidate.md diff --git a/docs/tutorial/04-cache/03-manually-invalidate.md b/docs/tutorial/07-cache/03-manually-invalidate.md similarity index 100% rename from docs/tutorial/04-cache/03-manually-invalidate.md rename to docs/tutorial/07-cache/03-manually-invalidate.md diff --git a/docs/tutorial/04-cache/04-force-request.md b/docs/tutorial/07-cache/04-force-request.md similarity index 100% rename from docs/tutorial/04-cache/04-force-request.md rename to docs/tutorial/07-cache/04-force-request.md diff --git a/docs/tutorial/04-cache/05-set-and-query.md b/docs/tutorial/07-cache/05-set-and-query.md similarity index 100% rename from docs/tutorial/04-cache/05-set-and-query.md rename to docs/tutorial/07-cache/05-set-and-query.md diff --git a/docs/tutorial/04-cache/06-controlled-cache.md b/docs/tutorial/07-cache/06-controlled-cache.md similarity index 100% rename from docs/tutorial/04-cache/06-controlled-cache.md rename to docs/tutorial/07-cache/06-controlled-cache.md diff --git a/docs/tutorial/07-cache/07-server-cache.md b/docs/tutorial/07-cache/07-server-cache.md new file mode 100644 index 000000000..2d809326f --- /dev/null +++ b/docs/tutorial/07-cache/07-server-cache.md @@ -0,0 +1,8 @@ +--- +title: 服务端缓存 +sidebar_position: 70 +--- + +## alova id 设置 + +可以设置 alova id,用于固定请求,当没有设置时将按 alova 创建顺序递增。 diff --git a/docs/tutorial/04-cache/README.md b/docs/tutorial/07-cache/README.md similarity index 100% rename from docs/tutorial/04-cache/README.md rename to docs/tutorial/07-cache/README.md diff --git a/docs/tutorial/04-cache/_category_.json b/docs/tutorial/07-cache/_category_.json similarity index 100% rename from docs/tutorial/04-cache/_category_.json rename to docs/tutorial/07-cache/_category_.json diff --git a/docusaurus.config.ts b/docusaurus.config.ts index 9020d097d..2d228267d 100644 --- a/docusaurus.config.ts +++ b/docusaurus.config.ts @@ -42,7 +42,12 @@ const config: Config = { sidebarPath: require.resolve('./sidebars.js'), // Please change this to your repo. // Remove this to remove the "edit this page" links. - editUrl: 'https://github.com/alovajs/alovajs.github.io/blob/main/' + editUrl: 'https://github.com/alovajs/alovajs.github.io/blob/main/', + versions: { + current: { + label: '3.0-beta' + } + } }, // disable blog blog: false, @@ -105,6 +110,11 @@ const config: Config = { } ] }, + { + type: 'docsVersionDropdown', + position: 'right', + dropdownActiveClassDisabled: true + }, { type: 'localeDropdown', position: 'right' diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current.json b/i18n/zh-CN/docusaurus-plugin-content-docs/current.json index 804e2ab26..18bd79697 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current.json +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current.json @@ -1,70 +1,78 @@ -{ - "version.label": { - "message": "最新版本", - "description": "The label for version current" - }, - "sidebar.tutorialSidebar.category.Overview": { - "message": "概览", - "description": "The label for category Overview in sidebar tutorialSidebar" - }, - "sidebar.tutorialSidebar.category.Examples": { - "message": "示例", - "description": "The label for category Examples in sidebar tutorialSidebar" - }, - "sidebar.tutorialSidebar.category.Examples.link.generated-index.description": { - "message": "这里有丰富的示例,来展示alova在不同请求场景下的表现。", - "description": "The generated-index page description for category Examples in sidebar tutorialSidebar" - }, - "sidebar.tutorialSidebar.category.Getting Started": { - "message": "开始", - "description": "The label for category Get Started in sidebar tutorialSidebar" - }, - "sidebar.tutorialSidebar.category.Response Cache": { - "message": "响应缓存", - "description": "The label for category Learn in sidebar tutorialSidebar" - }, - "sidebar.tutorialSidebar.category.Combine Framework": { - "message": "结合框架", - "description": "The label for category Combine Framework in sidebar tutorialSidebar" - }, - "sidebar.tutorialSidebar.category.Strategy": { - "message": "请求策略", - "description": "The label for category Strategy in sidebar tutorialSidebar" - }, - "sidebar.tutorialSidebar.category.Sensorless data interaction": { - "message": "无感数据交互", - "description": "The label for category Sensorless data interaction in sidebar tutorialSidebar" - }, - "sidebar.tutorialSidebar.category.Basics completed": { - "message": "🎉你已完成基础", - "description": "The label for category Basics completed in sidebar tutorialSidebar" - }, - "sidebar.tutorialSidebar.category.Advanced": { - "message": "进阶", - "description": "The label for category Advanced in sidebar tutorialSidebar" - }, - "sidebar.tutorialSidebar.category.Custom": { - "message": "自定义", - "description": "The label for category custom in sidebar tutorialSidebar" - }, - "sidebar.tutorialSidebar.category.Request adapter": { - "message": "请求适配器", - "description": "The label for category Request adapter in sidebar tutorialSidebar" - }, - "sidebar.tutorialSidebar.category.extend hooks": { - "message": "扩展hooks", - "description": "The label for category extend hooks in sidebar tutorialSidebar" - }, - "sidebar.tutorialSidebar.category.Best practice": { - "message": "最佳实践", - "description": "The label for category best practice in sidebar tutorialSidebar" - }, - "sidebar.tutorialSidebar.category.Framework": { - "message": "UI框架", - "description": "The label for category Framework in sidebar tutorialSidebar" - }, - "sidebar.tutorialSidebar.category.Others": { - "message": "其他", - "description": "The label for category Others in sidebar tutorialSidebar" - } -} +{ + "version.label": { + "message": "3.0-beta", + "description": "The label for version current" + }, + "sidebar.tutorialSidebar.category.Overview": { + "message": "概览", + "description": "The label for category Overview in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.Examples": { + "message": "示例", + "description": "The label for category Examples in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.Examples.link.generated-index.description": { + "message": "这里有丰富的示例,来展示alova在不同请求场景下的表现。", + "description": "The generated-index page description for category Examples in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.Getting Started": { + "message": "开始", + "description": "The label for category Get Started in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.Response Cache": { + "message": "响应缓存", + "description": "The label for category Learn in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.Combine Framework": { + "message": "结合框架", + "description": "The label for category Combine Framework in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.Client Strategy": { + "message": "客户端策略", + "description": "The label for category Client Strategy in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.Server Strategy": { + "message": "服务端策略", + "description": "The label for category Server Strategy in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.Strategy": { + "message": "请求策略", + "description": "The label for category Strategy in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.Sensorless data interaction": { + "message": "无感数据交互", + "description": "The label for category Sensorless data interaction in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.Basics completed": { + "message": "🎉你已完成基础", + "description": "The label for category Basics completed in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.Advanced": { + "message": "进阶", + "description": "The label for category Advanced in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.Custom": { + "message": "自定义", + "description": "The label for category custom in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.Request adapter": { + "message": "请求适配器", + "description": "The label for category Request adapter in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.extend hooks": { + "message": "扩展hooks", + "description": "The label for category extend hooks in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.Best practice": { + "message": "最佳实践", + "description": "The label for category best practice in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.Framework": { + "message": "UI框架", + "description": "The label for category Framework in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.Others": { + "message": "其他", + "description": "The label for category Others in sidebar tutorialSidebar" + } +} diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/02-quick-start.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/02-quick-start.md index 3f13c341d..cd1deeacc 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/02-quick-start.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/02-quick-start.md @@ -1,115 +1,127 @@ ---- -title: 快速开始 -sidebar_position: 20 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; -import EmbedSandpack from "@site/src/components/EmbedSandpack"; - -import quickStartGET from '!!raw-loader!@site/codesandbox/01-getting-started/02-first-request/get.js'; -import quickStartPOST from '!!raw-loader!@site/codesandbox/01-getting-started/02-first-request/post.js'; - -:::tip 示例提示 - -如果你还未了解 alova,推荐你先阅读 [alova 概述](/tutorial/getting-started)。 - -::: - -## 安装 - - - - -```bash -npm install alova --save -``` - - - - -```bash -yarn add alova -``` - - - - -```bash -pnpm add alova -``` - - - - -```bash -bun add alova -``` - - - - -> 你也可以[通过 CDN 使用 alova](/tutorial/others/use-in-static) - -## 创建 alova 实例 - -在 alova 中需要通过 alova 实例发起请求,我们先创建一个。在创建 alova 实例时需要指定请求适配器,在这里推荐使用`GlobalFetch`请求适配器, 它是基于`fetch API`的封装。 - - - - -```javascript -import { createAlova } from 'alova'; -import GlobalFetch from 'alova/GlobalFetch'; - -const alovaInstance = createAlova({ - requestAdapter: GlobalFetch() -}); -``` - - - - -```javascript -const { createAlova } = require('alova'); -const GlobalFetch = require('alova/GlobalFetch'); - -const alova = createAlova({ - requestAdapter: GlobalFetch(); -}); -``` - -> 在 nodejs 中使用 GlobalFetch 时,nodejs 版本要求`v17.5`,或者你可以使用[axios 请求适配器](/tutorial/request-adapter/alova-adapter-axios/)。 - - - - -```javascript -import { createAlova } from 'npm:alova'; -import GlobalFetch from 'npm:alova/GlobalFetch'; - -const alova = createAlova({ - requestAdapter: GlobalFetch(); -}); -``` - - - - -## GET 请求 - -通过 `alovaInstance.Get` 发送一个请求,由于使用了`GlobalFetch`请求适配器,将会接收到一个`Response`实例,这很简单。 - - - -在异步函数中,你也可以使用`await alovaInstance.Get`等待响应。 - -## POST 请求 - -通过 `alovaInstance.Post`提交数据,这同样很简单。 - - - -## 接下来要做什么? - -实际上,这只是一个最简单的请求示例,在接下来的章节中将会了解更多功能,让我们开始学习吧。 +--- +title: 快速开始 +sidebar_position: 20 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import EmbedSandpack from "@site/src/components/EmbedSandpack"; + +import quickStartGET from '!!raw-loader!@site/codesandbox/01-getting-started/02-first-request/get.js'; +import quickStartPOST from '!!raw-loader!@site/codesandbox/01-getting-started/02-first-request/post.js'; + +:::tip 示例提示 + +如果你还未了解 alova,推荐你先阅读 [alova 概述](/tutorial/getting-started)。 + +::: + +## 安装 + + + + +```bash +npm install alova --save +``` + + + + +```bash +yarn add alova +``` + + + + +```bash +pnpm add alova +``` + + + + +```bash +bun add alova +``` + + + + +> 你也可以[通过 CDN 使用 alova](/tutorial/others/use-in-static) + +## 创建 alova 实例 + +在 alova 中需要通过 alova 实例发起请求,我们先创建一个。在创建 alova 实例时需要指定请求适配器,在这里推荐使用`alova/fetch`请求适配器,它是基于`fetch API`的封装,非常简洁。 + + + + +```javascript +import { createAlova } from 'alova'; +import fetchAdapter from 'alova/fetch'; + +const alovaInstance = createAlova({ + requestAdapter: fetchAdapter() +}); +``` + + + + +```javascript +const { createAlova } = require('alova'); +const fetchAdapter = require('alova/fetch'); + +const alova = createAlova({ + requestAdapter: fetchAdapter(); +}); +``` + +> 在 nodejs 中使用 fetchAdapter 时,nodejs 版本要求`v17.5`,或者你可以使用[axios 请求适配器](/tutorial/request-adapter/alova-adapter-axios/)。 + + + + +```javascript +import { createAlova } from 'npm:alova'; +import fetchAdapter from 'npm:alova/fetch'; + +const alova = createAlova({ + requestAdapter: fetchAdapter(); +}); +``` + + + + +## GET 请求 + +通过 `alovaInstance.Get` 发送一个请求,由于使用了`fetchAdapter`请求适配器,将会接收到一个`Response`实例,这很简单。 + +```js +const response = await alovaInstance + .Get('https://alovajs.dev/user/profile') + .then(response => response.json()); +``` + +在异步函数中,你也可以使用`await alovaInstance.Get`等待响应。 + +## POST 请求 + +通过 `alovaInstance.Post`提交数据,这同样很简单。 + +```js +const response = alovaInstance + .Post('https://alovajs.dev/posts', { + title: 'foo', + body: 'bar', + userId: 1 + }) + .then(response => response.json()); +``` + +## 接下来要做什么? + +实际上,这只是一个最简单的请求示例,在接下来的章节中将会了解更多功能,让我们开始学习吧。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/03-method.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/03-method.md index c189526d3..746cc8158 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/03-method.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/03-method.md @@ -126,7 +126,7 @@ alovaInstance.Get('/user', { ### 其他请求适配器支持的参数 -除了请求头、params 参数等外,还支持配置对应请求适配器支持的参数,当使用`GlobalFetch`作为 alova 的请求适配器时,就可以在 method 实例上配置任何`fetch API`支持的参数,这些参数会在请求时传给`fetch`函数。 +除了请求头、params 参数等外,还支持配置对应请求适配器支持的参数,当使用`alova/fetch`作为 alova 的请求适配器时,就可以在 method 实例上配置任何`fetch API`支持的参数,这些参数会在请求时传给`fetch`函数。 ```javascript alovaInstance.Get('/todo/list', { @@ -152,7 +152,7 @@ fetch('/todo/list', { }); ``` -> 请求体除了可以传递 Object,还能传递请求适配器支持的请求体参数,例如 GlobalFetch 支持传递`string | FormData | Blob | ArrayBuffer | URLSearchParams | ReadableStream`参数。 +> 请求体除了可以传递 Object,还能传递请求适配器支持的请求体参数,例如 `alova/fetch` 支持传递`string | FormData | Blob | ArrayBuffer | URLSearchParams | ReadableStream`参数。 如果你使用了其他的请求适配器,也可以传递它们支持的参数。 @@ -229,11 +229,20 @@ alovaInstance.Get('/todo/list', { ### 响应缓存 -响应缓存让你可以更好地多次利用服务端数据,而不需要每次请求时都发送请求获取数据。GET 请求将默认设置 5 分钟的内存缓存时间,我们将在后面的[响应缓存](/tutorial/cache/mode)章节中详细说明。 +响应缓存让你可以更好地多次利用服务端数据,而不需要每次请求时都发送请求获取数据。GET 请求将默认设置 5 分钟的内存缓存时间,如果你不需要可以通过以下方式关闭当前请求的缓存。 + +```ts +alovaInstance.Get('/todo/list', { + // 设置为0或者null关闭默认的响应缓存 + cacheFor: 0 +}); +``` + +详细内容可参考[响应缓存](/tutorial/cache/mode) ## 中断请求 -`[2.6.2+]` 调用 method 实例的`abort`中断请求。 +调用 method 实例的`abort`中断请求。 ```javascript const userMethod = alovaInstance.Get('/api/user'); @@ -250,7 +259,7 @@ const handleCancel = () => { ## 监听上传下载进度 -**[v2.17.0+]** 通过 method 实例的`onUpload`绑定上传进度事件,`onDownload`绑定下载进度事件,它将返回解绑函数。 +通过 method 实例的`onUpload`绑定上传进度事件,`onDownload`绑定下载进度事件,它将返回解绑函数。 ```javascript const uploadMethod = alovaInstance.Post('/todo/uploadfile', formData); @@ -286,9 +295,9 @@ const handleOffEvent = () => { }; ``` -:::warning 使用`GlobalFetch`适配器需注意 +:::warning 使用`alova/fetch`适配器需注意 -因 fetch api 限制,alova 提供的 **GlobalFetch** 适配器不支持上传进度,如果需要上传进度,请使用[XMLHttpRequest 适配器](/tutorial/request-adapter/alova-adapter-xhr)或[axios 适配器](/tutorial/request-adapter/alova-adapter-axios)。 +因 fetch api 限制,alova 提供的 `alova/fetch` 适配器不支持上传进度,如果需要上传进度,请使用[XMLHttpRequest 适配器](/tutorial/request-adapter/alova-adapter-xhr)或[axios 适配器](/tutorial/request-adapter/alova-adapter-axios)。 也可以自行编写请求适配器,详见 [编写请求适配器](/tutorial/custom/custom-http-adapter)。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/04-alova.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/04-alova.md index ae5e8c4ae..b3cc5285b 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/04-alova.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/04-alova.md @@ -54,7 +54,7 @@ const alovaInstance = createAlova({ ## 请求适配器 -在之前的章节中我们已经配置了`GlobalFetch`请求适配器,由这个 alova 实例发起的请求都将使用它发送请求。实际上,我们针对不同的 JS 环境,还提供了各种请求适配器。 +在之前的章节中我们已经配置了`alova/fetch`请求适配器,由这个 alova 实例发起的请求都将使用它发送请求。实际上,我们针对不同的 JS 环境,还提供了各种请求适配器。 - [模拟请求适配器](/tutorial/request-adapter/alova-mock) - [XMLHttpRequest 适配器](/tutorial/request-adapter/alova-adapter-xhr) @@ -64,4 +64,16 @@ const alovaInstance = createAlova({ ## 全局的响应缓存 -你还可以全局设置响应缓存,我们将在后面的[响应缓存](/tutorial/cache/mode)章节中详细说明。 +你还可以全局设置响应缓存: + +```ts +const alovaInstance = createAlova({ + // ... + cacheFor: { + GET: 0, // 关闭所有GET缓存 + POST: 60 * 60 * 1000 // 设置所有POST缓存1小时 + } +}); +``` + +详细内容可参考[响应缓存](/tutorial/cache/mode) diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/05-global-interceptor.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/05-global-interceptor.md index 72a617f58..98462ae54 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/05-global-interceptor.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/05-global-interceptor.md @@ -1,137 +1,136 @@ ---- -title: 全局拦截器 -sidebar_position: 50 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -## 全局的请求拦截器 - -通常,我们需要让所有请求都用上相同的配置,例如添加 token、timestamp 到请求头,此时我们可以设置一个全局的请求拦截器,它将在所有请求前被触发,我们可以在此拦截器中统一设置请求参数。 - -```mermaid -flowchart LR - R1[请求1] --> beforeRequest - R2[请求2] --> beforeRequest - R3[请求3] --> beforeRequest - RN[请求N] --> beforeRequest - beforeRequest --> S1[发送请求] -``` - -```javascript -const alovaInstance = createAlova({ - // ... - // 函数参数为一个method实例,包含如url、params、data、headers等请求数据 - // 你可以自由修改这些数据 - // highlight-start - beforeRequest(method) { - // 假设我们需要添加token到请求头 - method.config.headers.token = 'token'; - } - // highlight-end -}); -``` - -你也可以将 beforeRequest 设置为异步函数。 - -```javascript -const alovaInstance = createAlova({ - // ... - // highlight-start - async beforeRequest(method) { - // 执行一些异步任务 - // ... - } - // highlight-end -}); -``` - -## 全局的响应拦截器 - -当我们希望统一解析响应数据、统一处理错误,以及统一处理请求完成时,此时可以在创建 alova 实例时指定全局的响应拦截器,响应拦截器包括请求成功的拦截器、请求失败的拦截器,和请求完成的拦截器。 - -```mermaid -flowchart LR - classDef error fill:#f96,stroke:#f00,stroke-width:2px; - - R1[请求1成功] --> responded.onSuccess - R2[请求2成功] --> responded.onSuccess - RN[请求N成功] --> responded.onSuccess - R4[请求4失败]:::error --> responded.onError:::error - R5[请求M失败]:::error --> responded.onError:::error - responded.onSuccess --> responded.onComplete - responded.onError --> responded.onComplete -``` - -```javascript -const alovaInstance = createAlova({ - // ... - // 使用数组的两个项,分别指定请求成功的拦截器和请求失败的拦截器 - responded: { - // highlight-start - // 请求成功的拦截器 - // 当使用GlobalFetch请求适配器时,第一个参数接收Response对象 - // 第二个参数为当前请求的method实例,你可以用它同步请求前后的配置信息 - onSuccess: async (response, method) => { - if (response.status >= 400) { - throw new Error(response.statusText); - } - const json = await response.json(); - if (json.code !== 200) { - // 抛出错误或返回reject状态的Promise实例时,此请求将抛出错误 - throw new Error(json.message); - } - - // 解析的响应数据将传给method实例的transformData钩子函数,这些函数将在后续讲解 - return json.data; - }, - // highlight-end - - // highlight-start - // 请求失败的拦截器 - // 请求错误时将会进入该拦截器。 - // 第二个参数为当前请求的method实例,你可以用它同步请求前后的配置信息 - onError: (err, method) => { - alert(error.message); - }, - // highlight-end - - // highlight-start - // 请求完成的拦截器 - // 当你需要在请求不论是成功、失败、还是命中缓存都需要执行的逻辑时,可以在创建alova实例时指定全局的`onComplete`拦截器,例如关闭请求 loading 状态。 - // 接收当前请求的method实例 - onComplete: async method => { - // 处理请求完成逻辑 - } - // highlight-end - } -}); -``` - -如果不需要设置请求失败或完成的拦截器,可以直接传入请求成功的拦截器函数,而不再需要通过对象来设置回调。 - -```javascript -const alovaInstance = createAlova({ - // ... - // highlight-start - async responded(response, method) { - // 请求成功的拦截器 - } - // highlight-end -}); -``` - -:::info 拦截器触发说明 - -当你使用`GlobalFetch`请求适配器时,由于`window.fetch`的特点,只有在连接超时或连接中断时才会触发`onError`拦截器,其他情况均会触发`onSuccess`拦截器,[详情请查看这边](https://developer.mozilla.org/docs/Web/API/fetch) - -::: - -:::warning 特别注意 - -1. `onSuccess`、`onError`和`onComplete`均可以设为同步函数和异步函数。 -2. `onError` 回调是请求错误的捕获函数,`onSuccess` 中抛出错误不会触发 `onError`。当捕获错误但没有抛出错误或返回 reject 状态的 Promise 实例,将认为请求是成功的,且不会获得响应数据。 -3. 在 2.0.x 及以前的版本中将`responded`错误地拼写为了`responsed`,在 2.1.0 中已将两者做了兼容处理,建议在后续版本中使用`responded`代替`responsed`。 - -::: +--- +title: 全局拦截器 +sidebar_position: 50 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## 全局的请求拦截器 + +通常,我们需要让所有请求都用上相同的配置,例如添加 token、timestamp 到请求头,此时我们可以设置一个全局的请求拦截器,它将在所有请求前被触发,我们可以在此拦截器中统一设置请求参数。 + +```mermaid +flowchart LR + R1[请求1] --> beforeRequest + R2[请求2] --> beforeRequest + R3[请求3] --> beforeRequest + RN[请求N] --> beforeRequest + beforeRequest --> S1[发送请求] +``` + +```javascript +const alovaInstance = createAlova({ + // ... + // 函数参数为一个method实例,包含如url、params、data、headers等请求数据 + // 你可以自由修改这些数据 + // highlight-start + beforeRequest(method) { + // 假设我们需要添加token到请求头 + method.config.headers.token = 'token'; + } + // highlight-end +}); +``` + +你也可以将 beforeRequest 设置为异步函数。 + +```javascript +const alovaInstance = createAlova({ + // ... + // highlight-start + async beforeRequest(method) { + // 执行一些异步任务 + // ... + } + // highlight-end +}); +``` + +## 全局的响应拦截器 + +当我们希望统一解析响应数据、统一处理错误,以及统一处理请求完成时,此时可以在创建 alova 实例时指定全局的响应拦截器,响应拦截器包括请求成功的拦截器、请求失败的拦截器,和请求完成的拦截器。 + +```mermaid +flowchart LR + classDef error fill:#f96,stroke:#f00,stroke-width:2px; + + R1[请求1成功] --> responded.onSuccess + R2[请求2成功] --> responded.onSuccess + RN[请求N成功] --> responded.onSuccess + R4[请求4失败]:::error --> responded.onError:::error + R5[请求M失败]:::error --> responded.onError:::error + responded.onSuccess --> responded.onComplete + responded.onError --> responded.onComplete +``` + +```javascript +const alovaInstance = createAlova({ + // ... + // 使用数组的两个项,分别指定请求成功的拦截器和请求失败的拦截器 + responded: { + // highlight-start + // 请求成功的拦截器 + // 当使用GlobalFetch请求适配器时,第一个参数接收Response对象 + // 第二个参数为当前请求的method实例,你可以用它同步请求前后的配置信息 + onSuccess: async (response, method) => { + if (response.status >= 400) { + throw new Error(response.statusText); + } + const json = await response.json(); + if (json.code !== 200) { + // 抛出错误或返回reject状态的Promise实例时,此请求将抛出错误 + throw new Error(json.message); + } + + // 解析的响应数据将传给method实例的transformData钩子函数,这些函数将在后续讲解 + return json.data; + }, + // highlight-end + + // highlight-start + // 请求失败的拦截器 + // 请求错误时将会进入该拦截器。 + // 第二个参数为当前请求的method实例,你可以用它同步请求前后的配置信息 + onError: (err, method) => { + alert(error.message); + }, + // highlight-end + + // highlight-start + // 请求完成的拦截器 + // 当你需要在请求不论是成功、失败、还是命中缓存都需要执行的逻辑时,可以在创建alova实例时指定全局的`onComplete`拦截器,例如关闭请求 loading 状态。 + // 接收当前请求的method实例 + onComplete: async method => { + // 处理请求完成逻辑 + } + // highlight-end + } +}); +``` + +如果不需要设置请求失败或完成的拦截器,可以直接传入请求成功的拦截器函数,而不再需要通过对象来设置回调。 + +```javascript +const alovaInstance = createAlova({ + // ... + // highlight-start + async responded(response, method) { + // 请求成功的拦截器 + } + // highlight-end +}); +``` + +:::info 拦截器触发说明 + +当你使用`alova/fetch`请求适配器时,由于`window.fetch`的特点,只有在连接超时或连接中断时才会触发`onError`拦截器,其他情况均会触发`onSuccess`拦截器,[详情请查看这边](https://developer.mozilla.org/docs/Web/API/fetch) + +::: + +:::warning 特别注意 + +1. `onSuccess`、`onError`和`onComplete`均可以设为同步函数和异步函数。 +2. `onError` 回调是请求错误的捕获函数,`onSuccess` 中抛出错误不会触发 `onError`。当捕获错误但没有抛出错误或返回 reject 状态的 Promise 实例,将认为请求是成功的,且不会获得响应数据。 + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/06-method-metadata.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/06-method-metadata.md index a493ee4aa..e2249288a 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/06-method-metadata.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/06-method-metadata.md @@ -3,12 +3,6 @@ title: method元数据 sidebar_position: 60 --- -:::info 版本要求 - -v2.7.0+ - -::: - method 实例是贯穿 alova 的整个请求生命周期的,并且,在项目中会大量存在不同的 method 实例,有时候我们需要对特定的 method 实例添加附加信息,以便于对它们进行身份标识或额外的信息传递等,此时,我们就需要使用 method 元数据。 ## 使用元数据标识身份 @@ -56,7 +50,7 @@ const loginAPI = (username, password) => { }; ``` -**[2.18.0+]** 你也可以直接在 config 中传入 meta 数据 +你也可以直接在 config 中传入 meta 数据 ```javascript const loginAPI = (username, password) => { diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/07-combine-framework.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/07-combine-framework.md new file mode 100644 index 000000000..e7e7252c8 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/07-combine-framework.md @@ -0,0 +1,280 @@ +--- +title: 结合UI框架 +sidebar_position: 70 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import CodeBlock from '@theme/CodeBlock'; +import EmbedSandpack from "@site/src/components/EmbedSandpack"; + +import useRequestVue from '!!raw-loader!@site/codesandbox@3/01-getting-started/07-combine-framework/vueComposition-useRequest.zh.vue'; +import useRequestReact from '!!raw-loader!@site/codesandbox@3/01-getting-started/07-combine-framework/react-useRequest.zh.jsx'; +import useRequestSvelte from '!!raw-loader!@site/codesandbox@3/01-getting-started/07-combine-framework/svelte-useRequest.zh.svelte'; + +接下来,我们将学习如何与客户端的 UI 框架结合使用,这可以让 alova 发挥出真正的力量,在 UI 框架中使用时,不仅可以让 alova 自动管理响应式的请求状态,还能通过一定规则自动控制什么时候应该发送请求。 + +`alova`中提供了 10+个客户端的请求策略,它们帮助你以简单优雅的用法实现复杂的请求,让我们继续往下看吧! + +## 设置 statesHook + +在使用请求策略前,我们需要在 alova 实例上设置对应的 statesHook,它必须和项目使用的 UI 框架对应,这非常重要,它将告诉 alova 应该创建对应 UI 框架的响应式状态,目前支持以下框架: + + + + +```js +import { createAlova } from 'alova'; +import VueHook from 'alova/vue'; + +export const alovaInstance = createAlova({ + // ... + // highlight-start + statesHook: VueHook + // highlight-end +}); +``` + + + + +```js +import { createAlova } from 'alova'; +import ReactHook from 'alova/react'; + +export const alovaInstance = createAlova({ + // ... + // highlight-start + statesHook: ReactHook + // highlight-end +}); +``` + + + + +```js +import { createAlova } from 'alova'; +import SvelteHook from 'alova/svelte'; + +export const alovaInstance = createAlova({ + // ... + // highlight-start + statesHook: SvelteHook + // highlight-end +}); +``` + + + + +## 自动管理请求状态 + +`useRequest`是我们最常用的请求策略,它可以帮我们创建和维护请求的响应式状态,如`loading/data/error`等,你可以直接在视图中使用这些响应式状态,当它们更改时,视图也将随之变化。 + +useRequest 表示一次请求的发送,调用时默认将发送一次请求。 + + + + + + + + + + + + + + +{useRequestSvelte} + + + + +[何时使用 useRequest ,何时通过`await alovaInstance.Get` 发送请求](/tutorial/best-practice/skills)。 + +:::warning useHook 使用规范 + +请注意,`useRequest`只能用于组件内发送请求,在组件外,你可以通过 method 实例直接发送请求,并且 `useRequest` 的使用需要符合 use hook 使用规则,即只能在函数最外层调用。 + +**❌❌❌ 不推荐在在循环、条件判断或者子函数中调用**,例如以下在 click 回调中的使用示例,在回调函数中使用时,虽然可以正常发起请求,但 use hook 返回的响应式数据无法在视图中使用,循环和条件判断中使用也是如此。 + +```javascript +// ❌ bad +const handleClick = () => { + const { loading, data } = useRequest(getter); +}; + +// ------- +// ✅ good +const { loading, data, send } = useRequest(getter, { + immediate: false +}); +const handleClick = () => { + send(); +}; +``` + +::: + +## 提交数据 + +当你需要提交一条新的 todo 项时,可以先关闭默认发送请求,转为手动触发请求,并在 useRequest 中接收`send`函数用于手动发送请求,`send`函数将返回带响应数据的 Promise 实例,它将在请求响应后改为 resolve 状态。 + +此时为了接收`send`函数传入参数,可以将`useRequest`的第一个参数设置为函数,我们称这个函数为 **method handler**。 + +```javascript +const { + // ... + // 手动发送器请求的函数,调用后发送请求 + send: addTodo + + // 在这边将会接收到 send 函数的参数 +} = useRequest(newTodo => alovaInstance.Post('/todo', newTodo), { + // 当immediate为false时,默认不发出 + immediate: false +}); + +// 手动发送请求 +const handleAddTodo = () => { + const newTodo = { + title: '新的todo项', + time: new Date().toLocaleString() + }; + // send函数返回一个Promise对象,可接收响应数据 + addTodo(newTodo) + .then(result => { + console.log('新增todo项成功,响应数据为:', result); + }) + .catch(error => { + console.log('新增todo项失败,错误信息为:', error); + }); +}; +``` + +`send`函数可以让你自由地重复发起请求。 + +> 在 react 中,send 函数使用了`useCallback`包裹,同时它也不受闭包陷阱限制,你可以直接在事件中使用它,不用担心引起性能问题。 + +## 处理响应 + +请求完成后,响应数据会经过多个流程的处理,最终才会在发送请求的位置获得最终数据,流程如下: + +```mermaid +flowchart LR + classDef condition fill:#a8bcff + + R1[响应成功] --> global.onSuccess + global.onSuccess --> global.onComplete + global.onSuccess --> throw{是否抛出错误?}:::condition + throw -->|否| method.transformData + method.transformData --> useHook.onSuccess + throw -->|是| useHook.onError + + method.transformData --> throw2{是否抛出错误?}:::condition + throw2 -->|否| useHook.onSuccess + throw2 -->|是| useHook.onError + + useHook.onSuccess --> throw3{是否抛出错误?}:::condition + throw3 -->|是| useHook.onError + + R2[响应错误] --> global.onError + global.onError --> global.onComplete + global.onError --> throw4{是否抛出错误?}:::condition + throw4 -->|是| useHook.onError + throw4 -->|否| method.transformData +``` + +当没有抛出错误时,下一个节点会接收到上一个节点的返回值。 + +### 转换响应数据 + +在[method 详解](/tutorial/getting-started/method)中,我们已经了解过`transformData`了,这在 useHook 中使用也非常有用,它可以让 useHook 的 data 接收到转换后的数据,而不用再转换。 + +```javascript +const todoListGetter = alovaInstance.Get('/todo/list', { + // 函数接受未加工的数据和响应头对象,并要求将转换后的数据返回,它将会被赋值给data状态。 + // 注意:rawData是全局响应拦截器(如果有设置)过滤后的数据,响应拦截器的配置可以参考[设置全局响应拦截器]章节。 + transformData(rawData, headers) { + return rawData.list.map(item => ({ + ...item, + statusText: item.done ? '已完成' : '进行中' + }); + } +}); +``` + +```javascript +const { data } = useRequest(todoListGetter); +``` + +data 值将接收到转换后的数据格式。 + +```typescript +type data = { + // ... + statusText: '已完成' | '进行中'; +}[]; +``` + +:::warning 注意 + +在 usehooks 中使用时,在`transformData`中抛出错误也会触发`onError`; + +::: + +### 绑定响应回调 + +如需设置请求回调,你还可以在 useHooks 的返回参数中接收回调的设置函数,如下: + +```javascript +const { + // ... + + // 成功回调绑定 + onSuccess, + + // 失败回调绑定 + onError, + + // 完成回调绑定,回调在成功或失败都会调用 + onComplete +} = useRequest(todoListGetter); // 也适用useWatcher +onSuccess(event => { + console.log('请求成功,响应数据为:', event.data); + console.log('本次请求的method实例为:', event.method); + console.log('响应数据是否来自缓存:', event.fromCache); +}); +onError(event => { + console.log('请求失败,错误信息为:', event.error); + console.log('本次请求的method实例为:', event.method); +}); +onComplete(event => { + // event.status在成功时为success,失败时为error + console.log('请求完成,状态为:', event.status); + console.log('本次请求的method实例为:', event.method); + console.log('响应数据是否来自缓存:', event.fromCache); + if (event.data) { + console.log('请求数据:',event.data) + } else if (event.error) { + console.log('错误信息:',event.error) + } +}); +``` + +:::warning 注意 +在`onSuccess`中抛出错误将会触发`onError`。 + +::: + +## 结尾 + +以上是我们最常用的`useRequest`的基础使用,其他常用的请求策略还有: + +1. useWatcher: 监听数据变化并自动请求 +2. useForm: 表单数据数据提交与管理 +3. useAutoRequest: 按定时轮询、浏览器聚焦、网络重连等规则自动请求 +4. ... + +了解完整使用或其他客户端请求策略,请移步[客户端策略](/xxx)查看 alova 提供的所有客户端请求策略。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/08-server.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/08-server.md new file mode 100644 index 000000000..ed462008e --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/08-server.md @@ -0,0 +1,150 @@ +--- +title: 在服务端使用 +sidebar_position: 80 +--- + +在前面的[快速开始](/tutorial/getting-started/quick-start)中已经介绍过,alova 可以在`nodejs/deno/bun`等服务端环境中使用,你可以在服务端使用除客户端请求策略外的所有功能,此外,alova 还针对服务端环境提供了很好的支持。 + +```js +import { createAlova } from 'alova'; +import fetchAdapter from 'alova/fetch'; + +const alovaInstance = createAlova({ + requestAdapter: fetchAdapter() +}); + +alovaInstance.Get(...); +alovaInstance.Post(...); +``` + +## Server hooks + +在服务端中,我们也有不同的请求场景,例如请求重试、调用 API 发送验证码等,我们称为`Server hooks`,它们是 method 实例的装饰函数,并且可以多个`Server hooks`组合使用。 + +以下是组合了`retry`和`sendCaptcha`的示例,它实现了以失败重试的方式发送验证码: + +```js +const { retry, sendCaptcha } = require('alova/server'); + +const email = 'xxx@xxx.com'; +// 创建一个发送验证码的method实例 +const captchaMethod = alovaInstance.Post('/api/captcha', { + email, + content: 'captcha content' +}); + +// 使用retry hook包装captchaMethod +const retringMethod = retry(captchaMethod, { + retry: 3, + backoff: { + delay: 2000 + } +}); +// 再使用sendCaptcha hook包装retringMethod,并通过await发送请求并获取响应结果 +const result = await sendCaptcha(retringMethod, { + key: email +}); +``` + +你也可以直接使用多个`server hooks`包装 method 实例。 + +```ts +const result = await sendCaptcha( + retry( + alovaInstance.Post('/api/captcha', { + email, + content: 'captcha content' + }), + { + retry: 3, + backoff: { + delay: 2000 + } + } + ), + { + key: email + } +); +``` + +> 了解更多`Server hooks`请移步[服务端策略](/xxx)。 + +## 多级缓存 + +alova 提供了完整而简单的缓存功能,不仅如此,它还支持多级缓存来为你的服务端应用提供最快的请求体验。你可以自由选择单级缓存还是多级缓存使用,它们运行机制如下: + +```mermaid +flowchart LR + A[用户请求] --> B{检查L1缓存} + B -->|命中| C[返回数据] + B -->|未命中| D{检查L2缓存} + D -->|命中| E[更新L1缓存] + E --> C + D -->|未命中| F[请求API接口] + F --> G[更新L2缓存] + G --> E + C --> H[结束] + + style F stroke-width:8px +``` + +部分应用场景如下: + +1. 高访问频率和低延迟需求:例如热门新闻、商品详情,可以进一步减少网络开销,在网络不稳定时也保持更快的响应。 +2. 减轻下游服务器压力,例如有访问高峰期的服务,上层缓存可以有效减少对后端数据库和微服务的压力。 +3. 整合多个下游服务器的数据合并和处理,多个串行请求可能导致更长的响应时间,也可能因复杂的数据转换消耗性能,可将转换后的数据进行缓存。 +4. API 速率限制和计费,天气预报服务 API 每小时更新一次天气信息,地理位置数据 API 等。 + +以下是一个使用使用进程间内存共享适配器加 lru cache 作为一级缓存,redis 作为二级缓存的示例: + +```js +const { createPSCAdapter, NodeSyncAdapter } = require('@alova/psc'); +const { LRUCache } = require('lru-cache'); +const { createRedisAdapter } = require('@alova/redis'); + +function lRUCache(options = {}) { + const cache = new LRUCache(options); + return { + set(key, value) { + return cache.set(key, value); + }, + + get(key) { + return cache.get(key); + }, + + remove(key) { + return cache.delete(key); + }, + + clear() { + return cache.clear(); + } + }; +} + +const alovaInstance = createAlova({ + baseURL: 'https://api.alovajs.dev', + + // 进程间共享缓存适配器 + l1Cache: createPSCAdapter( + NodeSyncAdapter(), + lRUCache({ + max: 1000, + ttl: 1000 * 60 * 10 + }) + ), + + // redis缓存适配器 + l2Cache: createRedisAdapter({ + host: 'localhost', + port: 6379, + username: 'default', + password: 'my-top-secret', + db: 0 + }) +}); +``` + +> 深入了解响应缓存,请移步[深入响应缓存](/xxx)。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/09-plugin-integration.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/09-plugin-integration.md new file mode 100644 index 000000000..895f9190a --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/09-plugin-integration.md @@ -0,0 +1,92 @@ +--- +title: 编辑器扩展集成 +sidebar_position: 90 +--- + +集成 alova 的编辑器扩展可以让它展现出它更强大的力量。 + +1. 自动生成请求代码和响应数据类型,在 js 项目中也能体验对接口数据的智能提示。 +2. 将 api 文档嵌入代码中,带你体验边查边用 API 的效果。 +3. 定时更新 api 并主动通知前端开发,不再依赖服务端开发人员通知。 + +安装 VS Code 扩展 + +> 自动生成支持 swagger-v2 和 openapi-v3 规范。 + +## 配置 + +使用扩展时,你需要指定从 openapi 文件的输入源和输出目录等,在项目根目录下创建`alova.config.js`,具体配置如下: + +```js +// alova.config.js +module.exports = { + // api生成设置数组,每项代表一个自动生成的规则,包含生成的输入输出目录、规范文件地址等等 + generator: [ + // 服务器1 + { + // input参数1:openapi的json文件url地址 + input: 'http://localhost:3000/openapi.json', + + // input参数2:以当前项目为相对目录的本地地址 + // input: 'openapi/api.json' + + // input参数3:没有直接指向openapi文件时,是一个文档地址,必须配合platform参数指定文档类型 + // input: 'http://192.168.5.123:8080' + + // (可选)platform为支持openapi的平台,目前只支持swagger,默认为空 + // 当指定了此参数后,input字段只需要指定文档的地址而不需要指定到openapi文件 + platform: 'swagger' + + // 接口文件和类型文件的输出路径,多个generator不能重复的地址,否则生成的代码会相互覆盖 + output: 'src/api', + + // (可选)指定生成的响应数据的mediaType,以此数据类型来生成200状态码的响应ts格式,默认application/json + responseMediaType: 'application/json', + + // (可选)指定生成的请求体数据的bodyMediaType,以此数据类型来生成请求体的ts格式,默认application/json + bodyMediaType: 'application/json', + + /** + * (可选)生成代码的类型,可选值为auto/ts/typescript/module/commonjs,默认为auto,会通过一定规则判断当前项目的类型,如果生成不正确你也可以自定义指定类型: + * ts/typescript:意思相同,表示生成ts类型文件 + * module:生成esModule规范文件 + * commonjs:表示生成commonjs规范文件 + */ + type: "auto", + + /** + * (可选)过滤或转换生成的api接口函数,返回一个新的apiDescriptor来生成api调用函数,未指定此函数时则不转换apiDescripor对象 + */ + handleApi: apiDescriptor => { + // 返回falsy值表示过滤此api + if (!apiDescriptor.path.startWith('/user')) { + return; + } + + apiDescriptor.parameter = apiDescriptor.parameter.filter(param => param.in === 'header' && param.name === 'token'); + delete apiDescriptor.requestBody.id; + apiDescriptor.url = apiDescriptor.url.replace('/user', ''); + return apiDescriptor; + } + }, + + // 服务器2 + { + // ... + } + ], + + // (可选)是否自动更新接口,默认开启,每5分钟检查一次,false时关闭 + autoUpdate: true, + + /* 也可以配置更详细的参数 + autoUpdate: { + // 编辑器开启时更新,默认false + launchEditor: true, + // 自动更新间隔,单位毫秒 + interval: 5 * 60 * 1000 + } + */ +} + +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/10-congratulations.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/10-congratulations.md new file mode 100644 index 000000000..844e96aa5 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/10-congratulations.md @@ -0,0 +1,26 @@ +--- +title: 结语 +sidebar_position: 100 +--- + +import NavCard from '@site/src/components/NavCard'; + +🎉 恭喜你!已经完成了 alova 的基础使用,至此,你已可以使用 alova 来满足日常的项目开发了,接下来的内容你可以根据自己的兴趣自由选择接下来的学习内容。 + + diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/README.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/README.md index 4e79cfb41..8ec8f7efc 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/README.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/README.md @@ -6,21 +6,25 @@ import Link from '@docusaurus/Link'; import NavCard from '@site/src/components/NavCard'; import SupportList from '@site/src/components/SupportList'; -alova 是一个轻量级的请求策略库,它提供了一套完整的应对复杂请求场景的方案,我们称之为**请求策略**,只需一行代码就能快速实现各种复杂的请求逻辑,不仅能帮你提升开发效率,还能帮你提升 App 的运行效率,降低服务端压力。 +alova.js 是一个创新的下一代请求工具,它可以帮你在请求方面省去大部分的工作,解决前后端协作问题,让网络请求变得非常简单。我们来看看 alova 是如何帮你的简化工作的。 -这是一个最简单的请求示例: +![](/img/overview_flow_cn.png) -```javascript -const response = await alova.Get('/api/user'); -``` +它可以将 API 的消费从 7 步简化为 1 步,你只需要选择使用的 API 就可以了。 + +## 如何做的? + +### 请求策略 -平平无奇吧?让我们再看一个自动管理请求状态的示例,**loading、error、data 是响应式的数据**,在 react、vue、svelte 等 UI 框架中可以直接在视图中绑定它们,而且会根据请求状态自动维护它这些响应式数据。 +在实际项目中,前端请求总是需要根据不同场景考虑应该什么时候发出请求、什么时候不能发出请求、如何处理响应数据等才能满足项目的表现、性能的提高,这将导致开发人员时间成本和代码维护成本的增加,在 alova 中提供了一套完整的应对复杂请求场景的方案,我们称之为**请求策略**,只需一行代码就能快速实现各种复杂的请求逻辑,不仅能帮你提升开发效率,还能帮你提升 App 的运行效率,降低服务端压力。 + +例如,`useRequest`可以自动管理请求状态,**`loading/error/data` 是响应式的数据**,在 react、vue、svelte 等 UI 框架中可以直接在视图中绑定它们,而且会根据请求状态自动维护它这些响应式数据。 ```javascript const { loading, error, data } = useRequest(alova.Get('/api/user')); ``` -以下是一个分页请求策略的示例,**当 page、pageSize 等发生变化时会自动以不同参数触发请求**。 +再来一个分页请求策略,**当`page/pageSize`等发生变化时会自动以不同参数触发请求**。 ```javascript const { loading, error, data, page, pageSize, total } = usePagination((page, size) => @@ -32,56 +36,38 @@ const { loading, error, data, page, pageSize, total } = usePagination((page, siz alova 提供了 10+个基于[RSM](/tutorial/others/RSM)规范的请求策略模块,它们以 useHook 的形式实现。 -## 核心 useHook - -useRequest -useWatcher -useFetcher - -## 场景化请求策略 - -usePagination -useSQRequest -useForm -TokenAuthentication -useAutoRequest -useCaptcha -actionDelegationMiddleware -useSerialRequest -useSerialWatcher -useRetriableRequest -useUploader -useSSE +### alova 编辑器扩展 -## 高灵活性 +在 vscode 中使用 alova 扩展可以帮你自动生成包含完整的 API 文档标注,响应类型的请求代码。在过去,你需要先查询 API 文档,并不断地在 API 文档与编辑器切换来编写请求代码,使用 alova 插件后,你可以不再需要离开编辑器,直接在编辑器中边查边使用 API,感受不一样的 API 使用体验。 -得益于 alova 的高灵活性,你可以在以下不同 JS 环境下搭配不同的请求库使用(灰色部分将在未来逐渐支持)。 - - +> 关于 alova 插件的详细介绍,请参考 [集成 IDE 插件](/tutorial/getting-started/plugin-integration)。 ## 有什么不同吗? -与其他请求库不同的是,alova 的目标是让请求变得更简单并保持更高效的数据交互。 +与其他请求库不同的是,alova 的目标是让请求变得非常简单,并且保持更高效的数据交互。 -我们为开发者和 App 使用者双方考虑,对于开发者来说,alova 为他们提供了简单的请求 api,和开箱即用的高性能请求策略模块,对于应用的用户来说,他们可以享受到 alova 的高性能数据交互带来的流畅体验。 +我们为开发者和 App 使用者双方考虑,对于开发者来说,alova 为他们提供了极致的使用体验,对于应用的用户来说,他们可以享受到 alova 的高性能数据交互带来的流畅体验。 此外,再从具体的特性来看看: - 与 axios 相似的 api 设计,让使用者学习成本更低; -- 10+个开箱即用的高性能请求策略,让应用更流畅; -- alova 是轻量级的,只有 4kb+,是 axios 的 30%+; -- 灵活性高,alova 的适配器可以让 alova 在任何 js 环境下,与任何 UI 框架协作使用(内置支持的 UI 框架为`vue/react/svelte`),并且提供了统一的使用体验和完美的代码迁移; -- 3 种缓存模式和请求共享机制,提升请求性能并降低服务端压力; +- 高性能的客户端和服务端请求策略,让应用更流畅; +- 灵活性高,alova 的适配器可以让 alova 在任何 js 环境下,与任何 UI 框架协作使用,并且提供了统一的使用体验和完美的代码迁移; +- 2 种缓存模式和请求共享机制,提升请求性能并降低服务端压力; - api 代码的高聚合组织,每个 api 的请求参数、缓存行为、响应数据转换等都将聚集在相同的代码块中,这对于管理大量的 api 有很大的优势; -在[alova 的未来](/tutorial/others/future)中,将实现更进一步的请求简单化。 - -:::info 与其他请求库的对比 +:::info 对比 你还可以查看请[与其他请求库比较](/tutorial/others/comparison)详细了解 alova 的不同之处。 ::: +## 在任何 JS 环境下运行 + +不仅如此,alova 的灵活性非常高,你可以在以下任意的 JS 环境下,配合不同的请求工具使用(灰色部分将在未来逐渐支持)。 + + + ## 在线试用 你可以通过 Codesandbox [在线可编辑示例尝试 alovajs](/category/examples)直接在浏览器中运行项目,因此它与本地开发几乎无差别,同时无需在你的机器上安装任何东西。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-use-request.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-use-request.md new file mode 100644 index 000000000..9bb44c6d8 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-use-request.md @@ -0,0 +1,229 @@ +--- +title: 自动管理请求状态 +sidebar_position: 10 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info 策略类型 + +use hook + +::: + +useRequest 表示一次请求的发送,调用时默认将发送一次请求,在企业级项目中,在视图中展示数据的传输状态非常重要。在页面获取初始数据或提交数据时,useRequest 是最常用的 use hook 之一。 + +## 使用 + +它的基础用法已在[开始-结合 UI 框架](/tutorial/getting-started/combine-framework)中详细介绍。 + +### 设置初始数据 + +`data` 在请求成功前默认为 `undefined`,但有时候我们需要 data 在请求成功前也有初始值,例如在请求列表时通常需要将它初始化为`[]`,否则在渲染视图时会因为无法循环渲染而导致报错。 + +```javascript +const { data } = useRequest(todoListGetter, { + // highlight-start + // 请求响应前,data的初始值 + initialData: [] + // highlight-end +}); +``` + +也可以将`initialData`设置成函数动态设置初始值,例如,当你不希望应用每次进入时都显示 Loading 图标,而希望使用旧数据替代时,你可以返回旧数据作为初始值,它的体验比 Loading 更好。 + +```js +const { onSuccess } = useRequest(todoListGetter, { + initialData() { + // 设置上一次的响应数据 + const storedData = localStorage.getItem('placeholder-data'); + return JSON.parse(storedData || '{}'); + + // 也使用alova的level2存储适配器 + // return alovaInst.l2cache.get('placeholder-data'); + } +}); +onSuccess(({ data, method }) => { + // 保存响应数据 + localStorage.setItem('placeholder-data', JSON.stringify(data)); + + // 也使用alova的level2存储适配器 + // alovaInst.l2cache.set('placeholder-data', data); +}); +``` + +### 不立即发送请求 + +设置`immediate` 为 `false`避免立即请求。 + +```javascript +const { data } = useRequest(todoListGetter, { + // highlight-start + immediate: false + // highlight-end +}); +``` + +### 强制请求 + +强制请求是指绕过缓存的检查触发请求发送的机制,当需要在一定条件下获取最新的数据时很有用。 + +```javascript +useRequest(todoListGetter, { + // highlight-start + force: true + // highlight-end +}); +``` + +也可以设置为函数,当函数返回值为`true`时,强制请求。 + +```javascript +useRequest(todoListGetter, { + // highlight-start + force: ({ method, sendArgs }) => { + return !!sendArgs[0]; + } + // highlight-end +}); +``` + +### 手动发送请求 + +将`useRequest`的第一个参数设置为函数。 + +```javascript +const { + // ... + // 手动发送器请求的函数,调用后发送请求 + send + + // 在这边将会接收到 send 函数的参数 +} = useRequest(newTodo => alovaInstance.Post('/todo', newTodo), { + // 当immediate为false时,默认不发出 + immediate: false +}); + +send({ + /* todo data */ +}); +``` + +`send`函数可以让你自由地重复发起请求。 + +> 在 react 中,send 函数使用了`useCallback`包裹,同时它也不受闭包陷阱限制,你可以直接在事件中使用它,不用担心引起性能问题。 + +在`useRequest`和`useWatcher`中我们都可以调用`send`函数手动触发请求,send 函数触发请求时候,可以传入任意多个参数,这些参数其实可以分别被以下 3 个位置接收。 + +调用`send`支持传入任意多个参数,这些参数可以在以下 3 个位置接收。 + +#### 在 method handler 中接收 + +当它们的第一个参数设置为回调函数时可以接收到,这通常在删除列表项时很有用,具体如下: + +```javascript +const { send } = useRequest(id => + // id将接收到1 + removeTodoPoster(id) +); +send(1); +``` + +#### 在事件回调函数中接收 + +在事件回调函数中通过`event.sendArgs`接收,它是一个包含了 send 函数的所有参数的数组。 + +```javascript +const { send, onSuccess, onError, onComplete } = useRequest(newTodo => + alovaInstance.Post('/todo', newTodo) +); +onSuccess(event => { + // sendArgs的值为[1] + console.log(event.sendArgs); +}); +onError(event => { + // sendArgs的值为[1] + console.log(event.sendArgs); +}); +onComplete(event => { + // sendArgs的值为[1] + console.log(event.sendArgs); +}); + +// 发送请求 +send(1); +``` + +#### 在 force 函数中接收 + +force 用于指定是否需要穿透响应缓存,关于响应缓存的内容将在后面的[缓存模式](/tutorial/cache/mode)中讲解。 + +```javascript +const { send } = useRequest(alovaInstance.Get('/todo'), { + force: event => { + return event.sendArgs[0]; + } +}); +send(1); +``` + +### 下载和上传进度 + +通过`downloading`和`uploading`可以获得下载和上传的进度信息,你可以直接在视图中展示进度信息。 + +```javascript +const { downloading } = useRequest(alovaInstance.Get('/todo/downloadfile')); +const { uploading } = useRequest(alovaInstance.Get('/todo/uploadingfile')); +``` + +`downloading`和`uploading`的数据格式如下: + +```ts +export type Progress = { + total: number; + loaded: number; +}; +``` + +### 中断请求 + +通过 useHook 接收`abort`用于手动中断请求。 + +```javascript +const { + // ... + // highlight-start + // abort函数用于中断请求 + abort + // highlight-end +} = useRequest(todoListGetter); + +abort(); +``` + +### 额外的管理状态 + +设置额外的管理状态,这些状态可以实现跨组件更新,具体请查看[额外的管理状态](/tutorial/managed-state)。 + +### 中间件 + +通过`useRequest`的中间件几乎能控制一个请求的所有行为,例如你可以使用它阻止请求。 + +```js +let allowRequest = false; +useRequest(todoListGetter, { + middleware(ctx, next) { + if (!allowRequest) { + return; + } + return next(); + } +}); +``` + +具体请查看[深入客户端-中间件](/tutorial/middleware)。 + +## API + +请查看[API-useRequest](/api/core-hooks#userequest)。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/02-use-watcher.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/02-use-watcher.md new file mode 100644 index 000000000..f2270cf26 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/02-use-watcher.md @@ -0,0 +1,148 @@ +--- +title: 监听请求 +sidebar_position: 20 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import EmbedSandpack from "@site/src/components/EmbedSandpack"; +import CodeBlock from '@theme/CodeBlock'; +import useWatcherSearchVue from '!!raw-loader!@site/codesandbox@3/02-client/02-use-watcher/vueComposition-search.zh.vue'; +import useWatcherSearchReact from '!!raw-loader!@site/codesandbox@3/02-client/02-use-watcher/react-search.zh.jsx'; +import useWatcherSearchSvelte from '!!raw-loader!@site/codesandbox@3/02-client/02-use-watcher/svelte-search.zh.svelte'; + +:::info 策略类型 + +use hook + +::: + +在一些需要随数据变化而重新请求的场景下,如分页、数据筛选、模糊搜索、tab 栏切换等,可以使用`useWatcher` 来监听指定的状态变化时立即发送请求。 + +## 示例 + +接下来我们以搜索 todo 项为例,尝试改变选择框中的选项,看看 todo 列表是如何变化的。 + + + + + + + + + + + + + +{useWatcherSearchSvelte} + + + + +## 使用 + +:::tip 用法提示 + +useWatcher 支持 useRequest 的所有功能,详情请查看 [useRequest](/tutorial/client/use-request)。以下是 useWatcher 特有的用法。 + +::: + +### 立即发送请求 + +与`useRequest`不同的是,`useWatcher`的`immediate`属性默认是`false`。 + +```javascript +const { send } = useWatcher(() => getTodoList(currentPage), [currentPage], { + // highlight-start + immediate: true + // highlight-end +}); +send(); +``` + +### 请求防抖 + +通常我们都会在频繁触发的事件层面编写防抖代码,这次我们在请求层面实现了防抖功能,这意味着你再也不用在模糊搜索功能中自己实现防抖了,用法也非常简单。 + +:::info 什么是防抖 + +防抖(debounce),就是指触发事件后,在 n 秒内函数只能执行一次,如果触发事件后在 n 秒内又触发了事件,则会重新计算函数延执行时间(在这里和节流区分一下,节流是在触发完事件之后的一段时间之内不能再次触发事件) + +::: + +**设置所有监听状态的防抖时间** + +```javascript +const { loading, data, error } = useWatcher( + () => filterTodoList(keyword, date), + [keyword, date], + { + // highlight-start + // 设置debounce为数字时表示为所有监听状态的防抖时间,单位为毫秒 + // 如这边表示当状态keyword、date的一个或多个变化时,将在500ms后才发送请求 + debounce: 500 + // highlight-end + } +); +``` + +**为单个监听状态设置防抖时间** + +很多场景下,我们只需要对某几个频繁变化的监听状态进行防抖,如文本框的`onInput`触发的状态变化,可以这样做: + +```javascript +const { loading, data, error } = useWatcher( + () => filterTodoList(keyword, date), + [keyword, date], + { + // highlight-start + // 以监听状态的数组顺序分别设置防抖时间,0或不传表示不防抖 + // 这边监听状态的顺序是[keyword, date],防抖数组设置的是[500, 0],表示只对keyword单独设置防抖 + debounce: [500, 0] + // 也可以这么按如下设置: + // debounce: [500], + // highlight-end + } +); +``` + +### 请求时序 + +有时候当`useWatcher`监听的状态发生连续的改变导致连续的请求的发起时,后一次的请求先于前一次的请求获得响应,但是当前一次请求获得响应时,会覆盖后一次请求的响应,导致获取到与状态不匹配的响应;例如说有个状态`state`改变后发出了请求`1`,然后在请求`1`还未响应时又改变了`state`值,并发出了请求`2`,如果请求`1`后于请求`2`返回,最终的响应数据会维持在请求`1`。 +所以我们设计了`abortLast`参数,它用于标记当下一次请求发出时,是否中断上一次的未响应请求,默认为`true`,这样`useWatcher`所发出的请求只有最后一次有效。 + +```mermaid +sequenceDiagram + participant U as 用户 + participant S as 服务器 + U ->> U: 监听state状态 + U ->> S: state改变发起请求1 + U ->> S: state改变发起请求2 + S ->> U: 请求2先响应 + S ->> U: 请求1后响应 + U ->> U: 请求2的响应被覆盖 +``` + +```javascript +useWatcher( + () => getTodoList($currentPage), + // 被监听的状态数组,这些状态变化将会触发一次请求 + [state], + { + // highlight-start + abortLast: true // 是否中断上一次的未响应请求,默认为true + // highlight-end + } +); +``` + +:::warning 注意事项 + +`abortLast`默认为`true`,在正常情况下你不需要关注这个参数,如果修改为`false`,可能会导致状态与响应不匹配的问题。 + +::: + +## API + +请查看[API-useWatcher](/api/core-hooks#usewatcher)。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/03-use-pagination.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/03-use-pagination.md new file mode 100644 index 000000000..94878e909 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/03-use-pagination.md @@ -0,0 +1,699 @@ +--- +title: 分页请求策略 +sidebar_position: 30 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info 策略类型 + +use hook + +::: + +> 在使用扩展 hooks 前,确保你已熟悉了 alova 的基本使用。 + +为分页场景下设计的 hook,它可以帮助你自动管理分页数据,数据预加载,减少不必要的数据刷新,**流畅性提高 300%,编码难度降低 50%**。你可以在下拉加载和页码翻页两种分页场景下使用它,此 hook 提供了丰富的特性,助你的应用打造性能更好,使用更便捷的分页功能。 + +## 示例 + +[页码列表](/tutorial/example/paginated-list) + +[下拉加载更多](/tutorial/example/load-more) + +## 特性 + +- ✨ 丰富全面的分页状态; +- ✨ 丰富全面的分页事件; +- ✨ 更改 page、pageSize 自动获取指定分页数据; +- ✨ 数据缓存,无需重复请求相同参数的列表数据; +- ✨ 前后页预加载,翻页不再等待; +- ✨ 搜索条件监听自动重新获取页数; +- ✨ 支持列表数据的新增、编辑、删除; +- ✨ 支持刷新指定页的数据,无需重置; +- ✨ 请求级搜索防抖,无需自行维护; + +## 使用 + +### 渲染列表数据 + + + + +```html + + + +``` + + + + +```jsx +import { queryStudents } from './api.js'; +import { usePagination } from 'alova/client'; + +const App = () => { + const { + // 加载状态 + loading, + + // 列表数据 + data, + + // 是否为最后一页 + // 下拉加载时可通过此参数判断是否还需要加载 + isLastPage, + + // 当前页码,改变此页码将自动触发请求 + page, + + // 每页数据条数 + pageSize, + + // 分页页数 + pageCount, + + // 总数据量 + total, + + // 更新状态 + update + } = usePagination( + // Method实例获取函数,它将接收page和pageSize,并返回一个Method实例 + (page, pageSize) => queryStudents(page, pageSize), + { + // 请求前的初始数据(接口返回的数据格式) + initialData: { + total: 0, + data: [] + }, + initialPage: 1, // 初始页码,默认为1 + initialPageSize: 10 // 初始每页数据条数,默认为10 + } + ); + + // 翻到上一页,page值更改后将自动发送请求 + const handlePrevPage = () => { + update({ + page: page - 1 + }); + }; + + // 翻到下一页,page值更改后将自动发送请求 + const handleNextPage = () => { + update({ + page: page + 1 + }); + }; + + // 更改每页数量,pageSize值更改后将自动发送请求 + const handleSetPageSize = () => { + update({ + pageSize: 20 + }); + }; + + return ( +
+ {data.map(item => ( +
+ {item.name} +
+ ))} + + + + 共有{pageCount}页 + 共有{total}条数据 +
+ ); +}; +``` + +
+ + +```html + + +{#each $data as item} +
+ {item.name} +
+{/each} + + + +共有{pageCount}页 +共有{total}条数据 +``` + +
+
+ +### 指定分页数据 + +每个分页数据接口返回的数据结构各不相同,因此我们需要分别告诉`usePagination`列表数据与总条数,从而帮助我们管理分页数据。 + +假如你的分页接口返回的数据格式是这样的: + +```typescript +interface PaginationData { + totalNumber: number; + list: any[]; +} +``` + +此时你需要通过函数的形式返回列表数据与总条数。 + +```javascript +usePagination((page, pageSize) => queryStudents(page, pageSize), { + // ... + // highlight-start + total: response => response.totalNumber, + data: response => response.list + // highlight-end +}); +``` + +如果不指定 total 和 data 回调函数,它们将默认通过以下方式获取数据。 + +```javascript +const total = response => response.total; +const data = response => response.data; +``` + +:::warning 注意 + +data 回调函数必须返回一个列表数据,表示分页中所使用的数据集合,而 total 主要用于计算当前页数,在 total 回调函数中如果未返回数字,将会通过请求的列表数量是否少于 pageSize 值来判断当前是否为最后一页,这一般用于下拉加载时使用。 + +::: + +### 开启追加模式 + +默认情况下,翻页时会替换原有的列表数据,而追加模式是在翻页时会将下一页的数据追加到当前列表底部,常见的使用场景是下拉加载更多。 + +```javascript +usePagination((page, pageSize) => queryStudents(page, pageSize), { + // ... + // highlight-start + append: true + // highlight-end +}); +``` + +### 预加载相邻页数据 + +为了让分页提供更好的体验,在当前页的上一页和下一页满足条件时将会自动预加载,这样在用户翻页时可直接显示数据而不需要等待,这是默认的行为。如果你不希望预加载相邻页的数据,可通过以下方式关闭。 + +```javascript +usePagination((page, pageSize) => queryStudents(page, pageSize), { + // ... + // highlight-start + preloadPreviousPage: false, // 关闭预加载上一页数据 + preloadNextPage: false // 关闭预加载下一页数据 + // highlight-end +}); +``` + +:::warning 预加载触发条件 + +在开启预加载时,并不会一味地加载下一页,需要满足以下两个条件: + +1. 预加载是基于缓存的,用于分页加载的 Method 实例必须开启缓存,默认情况下 get 请求会有 5 分钟的 memory 缓存,如果是非 get 请求或者全局关闭了缓存,你还需要在这个 Method 实例中单独设置`localCache`开启缓存。 +2. 根据`total`和`pageSize`参数判断出下一页还有数据。 + +::: + +除了`onSuccess、onError、onComplete`请求事件外,在触发了预加载时,你还可以通过`fetching`来获知预加载状态,还可以通过`onFetchSuccess、onFetchError、onFetchComplete`来监听预加载请求的事件。 + +```javascript +const { + // 预加载状态 + fetching, + + // 预加载成功事件绑定函数 + onFetchSuccess, + + // 预加载错误事件绑定函数 + onFetchError, + + // 预加载完成事件绑定函数 + onFetchComplete +} = usePagination((page, pageSize) => queryStudents(page, pageSize), { + // ... +}); +``` + +### 监听筛选条件 + +很多时候列表需要通过条件进行筛选,此时可以通过`usePagination`的状态监听来触发重新请求,这与 alova 提供的`useWatcher`是一样的。 + +例如通过学生姓名、学生年级进行筛选。 + + + + +```html + + + +``` + + + + +```jsx +import { queryStudents } from './api.js'; +import { usePagination } from 'alova/client'; + +const App = () => { + // 搜索条件状态 + const [studentName, setStudentName] = useState(''); + const [clsName, setClsName] = useState(''); + const { + // ... + } = usePagination( + (page, pageSize) => queryStudents(page, pageSize, studentName, clsName), + { + // ... + // highlight-start + watchingStates: [studentName, clsName] + // highlight-end + } + ); + + return ( + // highlight-start + setStudentName(target.value)} /> + + // highlight-end + // ... + ); +}; +``` + + + + +```html + + + + + + + +``` + + + + +与`useWatcher`相同,你也可以通过指定`debounce`来实现请求防抖,具体可参考[useWatcher 的 debounce 参数设置](/api/core-hooks#usewatcher)。 + +```javascript +usePagination((page, pageSize) => queryStudents(page, pageSize, studentName, clsName), { + // ... + // highlight-start + debounce: 300 // 防抖参数,单位为毫秒数,也可以设置为数组对watchingStates单独设置防抖时间 + // highlight-end +}); +``` + +需要注意的是,`debounce`是通过 [**useWatcher**](/api/core-hooks#usewatcher) 中的请求防抖实现的。**监听状态末尾分别还有 page 和 pageSize 两个隐藏的监听状态,也可以通过 debounce 来设置。** + +举例来说,当`watchingStates`设置了`[studentName, clsName]`,内部将会监听`[studentName, clsName, page, pageSize]`,因此如果需要对 page 和 pageSize 设置防抖时,可以指定为`[0, 0, 500, 500]`。 + +### 关闭初始化请求 + +默认情况下,`usePagination`会在初始化时发起请求,但你也可以使用`immediate`关闭它,并在后续通过`send`函数,或者改变`page`或`pageSize`,以及`watchingStates`等监听状态来发起请求。 + +```javascript +usePagination((page, pageSize) => queryStudents(page, pageSize, studentName, clsName), { + // ... + // highlight-start + immediate: false + // highlight-end +}); +``` + +## 列表操作函数 + +usePagination 提供了功能完善的列表操作函数,它可以在不重新请求列表的情况下,做到与重新请求列表一致的效果,大大提高了页面的交互体验,具体的函数说明继续往下看吧! + +### 插入列表项 + +你可以用它插入列表项到列表任意位置,它将会在插入之后去掉末尾的一项,来保证和重新请求当前页数据一致的效果。 + +```typescript +/** + * 插入一条数据 + * 如果未传入index,将默认插入到最前面 + * 如果传入一个列表项,将插入到这个列表项的后面,如果列表项未在列表数据中将会抛出错误 + * @param item 插入项 + * @param indexOrItem 插入位置(索引) + */ +declare function insert(item: LD[number], indexOrItem?: number | LD[number]): void; +``` + +以下为**非 append 模式**下(页码翻页场景),返回第一页再插入列表项的示例: + +```javascript +page.value = 1; +nextTick(() => { + insert(newItem, 0); +}); +``` + +以下为在**append 模式**下(下拉加载场景),插入列表项后滚动到最顶部的示例: + +```javascript +insert(newItem, 0); +nextTick(() => { + window.scrollTo(0, {}); +}); +``` + +你也可以将`insert`的第二个参数指定为列表项,当查找到这个列表项的相同引用时,插入项将插入到这个列表项的后面。 + +```javascript +insert(newItem, afterItem); +``` + +:::warning 注意 + +为了让数据正确,insert 函数调用会清除全部缓存。 + +::: + +### 移除列表项 + +在下一页有缓存的情况下,它将会在移除一项后使用下一页的缓存补充到列表项尾部,来保证和重新请求当前页数据一致的效果,在**append 模式**和**非 append 模式**下表现相同。 + +```typescript +/** + * 移除一条数据 + * 如果传入的是列表项,将移除此列表项,如果列表项未在列表数据中将会抛出错误 + * @param position 移除的索引或列表项 + */ +declare function remove(position: number | LD[number]): void; +``` + +你也可以将`remove`的第二个参数指定为列表项,当查找到这个列表项的相同引用时,将会移除此列表项。 + +但在以下两种情况下,它将重新发起请求刷新对应页的数据: + +1. 下一页没有缓存 +2. 同步连续调用了超过下一页缓存列表项的数据,缓存数据已经不够补充到当前页列表了。 + +:::warning 注意 + +为了让数据正确,remove 函数调用会清除全部缓存。 + +::: + +### 更新数据项 + +当你想要更新列表项时,使用此函数实现。 + +```typescript +/** + * 替换一条数据 + * 当position传入数字时表示替换索引,负数表示从末尾算起,当 position 传入的是列表项,将替换此列表项,如果列表项未在列表数据中将会抛出错误 + * @param item 替换项 + * @param position 替换位置(索引)或列表项 + */ +declare function replace( + item: LD extends any[] ? LD[number] : any, + position: number | LD[number] +): void; +``` + +你也可以将`replace`的第二个参数指定为列表项,当查找到这个列表项的相同引用时,将会替换此列表项。 + +### 刷新指定页的数据 + +当你在数据操作后不希望本地更新列表项,而是重新请求服务端的数据,你可以用 refresh 刷新任意页的数据,而不需要重置列表数据让用户又从第一页开始浏览。 + +```typescript +/** + * 刷新指定页码数据,此函数将忽略缓存强制发送请求 + * 如果未传入页码则会刷新当前页 + * 如果传入一个列表项,将会刷新此列表项所在页 + * @param pageOrItemPage 刷新的页码或列表项 + */ +declare function refresh(pageOrItemPage?: number | LD[number]): void; +``` + +在 append 模式下,你可以将`refresh`的参数指定为列表项,当查找到这个列表项的相同引用时,刷新此列表项所在页数的数据。 + +### 手动更新列表数据 + +使用`update`函数更新响应式数据,这与[useRequest 的 update](/tutorial/combine-framework/use-request)相似,唯一不同的是,在调用`update`更新`data`时,更新的是列表数据,而非响应数据。这在手动清除列表数据,而不重新发起请求时很有用。 + +```typescript +// 情况列表数据 +update({ + data: [] +}); +``` + +### 重置列表 + +它将清空全部缓存,并重新加载第一页。 + +```typescript +/** + * 从第一页开始重新加载列表,并清空缓存 + */ +declare function reload(): void; +``` + +## API + +### Hook 配置 + +继承[**useWatcher**](/api/core-hooks#usewatcher)所有配置。 + +| 名称 | 描述 | 类型 | 默认值 | 版本 | +| ------------------- | ---------------------------------------- | ------------------------- | -------------------------- | ---- | +| initialPage | 初始页码 | number | 1 | - | +| initialPageSize | 初始每页数据条数 | number | 10 | - | +| watchingStates | 状态监听触发请求,使用 useWatcher 实现 | any[] | [page, pageSize] | - | +| debounce | 状态监听的防抖参数,使用 useWatcher 实现 | number \| number[] | - | - | +| append | 是否开启追加模式 | boolean | false | - | +| data | 指定分页的数组数据 | (response: any) => any[] | response => response.data | - | +| total | 指定数据总数量值 | (response: any) => number | response => response.total | - | +| preloadPreviousPage | 是否预加载上一页数据 | boolean | true | - | +| preloadNextPage | 是否预加载下一页数据 | boolean | true | - | + +### 响应式数据 + +继承[**useWatcher**](/api/core-hooks#usewatcher)所有响应式数据。 + +| 名称 | 描述 | 类型 | 版本 | +| ---------- | ------------------------------------------------------------------------------------------------------------------ | ------- | ---- | +| page | 当前页码,由 initialPage 决定 | number | - | +| pageSize | 当前每页数量,由 initialPageSize 决定 | number | - | +| data | 分页列表数组数据,由 data 配置得到 | any[] | - | +| total | 数据总数量,由 total 配置得到,可为空 | number | - | +| pageCount | 总页数,由 total 和 pageSize 计算得到 | number | - | +| isLastPage | 当前是否为最后一页,pageCount 有值时会通过 pageCount 和 page 对比得到,否则会通过列表数据长度是否少于 pagSize 得到 | number | - | +| fetching | 是否正在预加载数据 | boolean | - | + +### 操作函数 + +继承[**useWatcher**](/api/core-hooks#usewatcher)所有操作函数。 + +| 名称 | 描述 | 函数参数 | 返回值 | 版本 | +| ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------- | ------ | ---- | +| refresh | 刷新指定页码数据,此函数将忽略缓存强制发送请求,append 模式下可传入列表项表示刷新此列表项所在的页数 | pageOrItemPage: 刷新的页码或列表项 | - | - | +| insert | 插入一条数据,如果未传入 index,将默认插入到最前面,如果传入一个列表项,将插入到这个列表项的后面,如果列表项未在列表数据中将会抛出错误 | 1. item: 插入项
2. indexOrItem: 插入位置(索引)或列表项,默认为 0 | - | - | +| remove | 移除一条数据,当传入数字时表示移除的索引,当 position 传入的是列表项,将移除此列表项,如果列表项未在列表数据中将会抛出错误 | position: 移除位置(索引)或列表项 | - | - | +| replace | 替换一条数据,当第二个参数传入数字时表示替换索引,负数表示从末尾算起,当 position 传入的是列表项,将替换此列表项,如果列表项未在列表数据中将会抛出错误 | 1. item: 替换项
2. position: 替换位置(索引)或列表项,传入负数时表示从末尾开始算起 | - | - | +| reload | 清空数据,并重新请求第一页数据 | - | - | - | +| update | 更新状态数据,与 alova 的 use hook 用法相同,但在更新 data 字段时是更新列表数据 | newFrontStates:新的状态数据对象 | - | - | + +### 事件 + +继承[**useWatcher**](/api/core-hooks#usewatcher)所有事件。 + +| 名称 | 描述 | 回调参数 | 版本 | +| --------------- | ------------------------ | ------------------------- | ---- | +| onFetchSuccess | fetch 成功的回调绑定函数 | event: alova 成功事件对象 | - | +| onFetchError | fetch 失败的回调绑定函数 | event: alova 失败事件对象 | - | +| onFetchComplete | fetch 完成的回调绑定函数 | event: alova 完成事件对象 | - | diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/04-use-form.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/04-use-form.md new file mode 100644 index 000000000..b66b03707 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/04-use-form.md @@ -0,0 +1,488 @@ +--- +title: 表单提交策略 +sidebar_position: 40 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info 策略类型 + +use hook + +::: + +> 在使用扩展 hooks 前,确保你已熟悉了 alova 的基本使用。 + +为表单提交而设计的 hook,通过此 hook 你可以很方便地实现表单草稿、多页面(多步骤)表单,除此以外还提供了表单重置等常用功能。 + +## 示例 + +[表单提交 Demo](/tutorial/example/form-hook) + +## 特性 + +- ✨ 表单草稿; +- ✨ 多页面(多步骤)表单; +- ✨ 表单提交自动重置数据; +- ✨ 手动重置表单数据; + +## 使用 + +### 基本用法 + +展示表单 hook 的基本使用。 + + + + +```html + + + +``` + + + + +```jsx +import { formSubmit } from './api.js'; +import { useForm } from 'alova/client'; + +const App = () => { + const { + // 提交状态 + loading: submiting, + + // 响应式的表单数据,内容由initialForm决定 + form, + + // 提交数据函数 + send: submit, + + // 更新表单项 + updateForm, + + // 提交成功回调绑定 + onSuccess, + + // 提交失败回调绑定 + onError, + + // 提交完成回调绑定 + onComplete + } = useForm( + formData => { + // 可以在此转换表单数据并提交 + return formSubmit(formData); + }, + { + // 初始化表单数据 + initialForm: { + name: '', + cls: '1' + } + } + ); + + // 提交表单数据 + const handleSubmit = () => { + // 验证表单数据... + submit(); + }; + + return ( +
+ updateForm({ name: target.value })} + /> + + +
+ ); +}; +``` + +
+ + +```html + + + + + +``` + + +
+ +`useForm`默认不会请求,在调用`send`后才会发出请求,同时在`useForm`的回调函数将传入最新的 form 数据,如果需要在提交前转换数据,可在此进行转换,也可以在`formSubmit`函数中转换。 + +:::warning 注意 + +1. `initialForm`是设置初始表单数据,`initialData`是设置初始响应数据,注意区分; +2. `updateForm`是更新表单数据,`update`是更新响应数据,注意区分; + +::: + +以上示例只展示了简单的表单提交功能,普通的表单提交没有差别,但`useForm`还实现了更多的实用功能,让我们继续往下看。 + +### 提交自动重置表单 + +很多时候,我们都需要在表单提交后重置表单数据,在自行实现时我们总是需要手动一个一个重新赋值,而`useForm`可以帮我们自动完成。 + +```javascript +useForm(submitData, { + // ... + // highlight-start + // 设置这个参数为true即可在提交完成后自动重置表单数据 + resetAfterSubmiting: true + // highlight-end +}); +``` + +如果你需要手动重置表单数据,也可以通过调用`reset`函数做到。 + +```javascript +const { + // highlight-start + // 表单重置函数 + reset + // highlight-end +} = useForm(submitData, { + // ... +}); + +// highlight-start +const handleReset = () => { + reset(); +}; +// highlight-end +``` + +### 更新表单数据 + +在编辑表单时,我们需要展示原表单的数据,此时可以使用`updateForm`来异步更新表单数据。 + +```javascript +const { + // ... + updateForm +} = useForm(submitData, { + // ... + { + // 初始化表单数据 + initialForm: { + name: '', + cls: '1' + } + } +}); + +// 请求表单数据并更新到表单中 +const { onSuccess } = useRequest(getData); +onSuccess(({ data }) => { + updateForm({ + name: data.name, + cls: data.cls + }); +}); +``` + +### 表单草稿 + +`useForm`还提供了表单草稿功能,在数据重置前即使刷新页面也可以恢复表单数据,其原理是使用 alova 实例上的存储适配器,将表单数据进行持久化。使用时只需要将`store`设置为 true 即可。 + +```javascript +useForm(submitData, { + // ... + // highlight-start + // 开启持久化保存数据,设置为true后将实时持久化未提交的数据 + store: true + // highlight-end +}); +``` + +数据持久化前将会调用`JSON.stringify`转换为 JSON 字符串,在默认情况下,表单数据在持久化保存时将会进行数据序列化,`useForm`内置了`Date`和`RegExp`实例的序列化,这在使用时间选择器时将很有用。 + +在表单数据只涉及到`Date`和`RegExp`实例时你无需进行更多操作,但如果有其他非 JSON 数据时,例如`moment`实例,我们需要自定义序列化器,不过别担心,自定义序列化器很简单,以下将展示设置一个`moment`序列化器。 + +```javascript +import moment from 'moment'; +const momentSerializer = { + // forward在序列化时被调用 + // 需要判断是否为moment实例,如果不是目标值则返回undefined,表示不处理它 + forward: data => moment.isMoment(data) ? data.valueOf() : undefined, + + // backward在反序列化时被调用,data为forward中返回的值 + backward: timestamp => moment(timestamp); +}; + +useForm( + submitData, + { + store: { + enable: true, + serializers: { + moment: momentSerializer + } + } + } +); +``` + +### 多页面/多步骤表单 + +很多时候我们会遇到表单项分为了多个页面,或多个步骤进行填写,并在最后统一提交的情况,例如多步骤的用户注册、填写问卷等,以及多个步骤的表单可能有相互依赖关系,如果自行实现将会带来一定麻烦。而`useForm`实现了表单数据共享,你可以在不同的页面或组件中获取到同一份表单数据,解决了多步骤表单数据依赖的问题,也不需要在提交时汇总表单数据,可直接提交。 + +使用时,你需要通过`useForm`设置 id,通过相同的 id 你可以在不同页面间共享同一份表单数据。例如我们有一个表单需要经过 3 个步骤填写表单,它们分别会经过组件 A、组件 B、组件 C。 + +``` +组件A -> 组件B -> 组件C +``` + +此时,我们可以在组件 A 内初始化表单数据: + +```javascript title=组件A +const returnStates = useForm(submitData, { + initialForm: { + step1Input: '', + step2Input: '', + step3Input: '' + }, + // highlight-start + id: 'testForm' + // highlight-end +}); +const { form, send } = returnStates; +``` + +在组件 B、组件 C 中可在此指定 id 来获取组件 A 内的共享数据。 + +```javascript title=组件B、组件C +const returnStates = useForm(submitData, { + id: 'testForm' +}); +const { form, send } = returnStates; +``` + +组件 B、C 内通过 id 返回的 `returnStates` 与组件 A 内的 `returnStates` 是相同的引用,你可以使用同一个 `form`,也可以在任意一个组件内调用 `send` 统一提交表单数据。 + +**额外的** + +如果你的多步骤表单并不是按一定顺序,而是根据一定条件随机顺序的,例如: + +```bash +# 可能的顺序1 +组件B -> 组件A -> 组件C + +# 可能的顺序2 +组件A -> 组件C -> 组件B + +# 可能的顺序3 +组件C -> 组件A -> 组件B + +# ... +``` + +在这种情况下,你可以在组件 B、C 中像组件 A 一样设置`useForm`参数。 + +```javascript title=组件B、组件C +const returnStates = useForm(submitData, { + initialForm: { + step1Input: '', + step2Input: '', + step3Input: '' + }, + id: 'testForm' +}); +``` + +这样无论先渲染哪个组件都可以对 id 为 testForm 的表单初始化,后面的组件在遇到 id 为 testForm 时将优先使用已初始化的表单数据,而不会再次初始化。这样你就可以在任意组件内初始化表单数据。 + +> 更详细的多步骤表单也可以在[表单提交 Demo](/tutorial/example/form-hook)中体验和查看。 + +### 条件筛选 + +`useForm`还可以用于对数据筛选场景所用到的筛选表单,例如你希望通过城市名搜索城市信息,你可以设置`immedidate=true`,它将会在初始化时就开始查询数据,在之后的操作中再调用`send`重复查询数据。 + +```javascript +const { send: searchData } = useForm(queryCity, { + initialForm: { + cityName: '' + }, + immediate: true +}); +``` + +:::warning 条件限制 + +在条件筛选场景下,`useForm`更适用于非分页的列表条件查询,如果你需要在分页列表中进行条件查询,建议使用 [分页请求策略(usePagination)](/tutorial/strategy/usePagination)。 + +::: + +## API + +### Hook 配置 + +继承[**useRequest**](/api/core-hooks#userequest)所有配置。 + +| 名称 | 描述 | 类型 | 默认值 | 版本 | +| ------------------- | -------------------------------------------------------------------------------------------------------- | -------------------------------------------------- | ------ | ---- | +| initialForm | 初始表单数据 | any | - | - | +| id | form id,相同 id 的 data 数据是同一份引用,可以用于在多页表单时共用同一份表单数据。单页表单不需要指定 id | string \| number | - | - | +| store | 是否持久化保存数据,设置为 true 后将实时持久化未提交的数据 | boolean \| [StoreDetailConfig](#storedetailconfig) | false | - | +| resetAfterSubmiting | 提交后重置数据 | boolean | false | - | + +### 响应式数据 + +继承[**useRequest**](/api/core-hooks#userequest)所有响应式数据。 + +| 名称 | 描述 | 类型 | 版本 | +| ---- | ----------------------------- | ---- | ---- | +| form | 表单数据,由 initialForm 决定 | any | - | + +#### StoreDetailConfig + +| 名称 | 描述 | 类型 | 默认值 | 版本 | +| ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | -------- | ---- | +| enable | 是否启用持久化数据 | boolean | required | - | +| serializers | 自定义序列化器的集合,内置的序列化器:
1. date 序列化器用于转换日期
2. regexp 序列化器用于转化正则表达式
可以通过设置同名序列化器来覆盖内置序列化器 | Record\ | - | - | + +#### DataSerializer + +| 名称 | 描述 | 类型 | 默认值 | 版本 | +| -------- | ----------------------------------------------------------------------------------------------------- | --------------------------------------- | -------- | ---- | +| forward | 序列化函数,forward 中序列化时需判断是否为指定的数据,并返回转换后的数据,否则返回 undefined 或不返回 | (data: any) => any \| undefined \| void | required | - | +| backward | 反序列化函数,直接反序列化数据 | (data: any) => any \| undefined \| void | required | - | + +### 操作函数 + +继承[**useRequest**](/api/core-hooks#userequest)所有操作函数。 + +| 名称 | 描述 | 函数参数 | 返回值 | 版本 | +| ---------- | ------------------------------------------ | ---------------------------------------------------------------------- | ------ | ---- | +| updateForm | 更新一项或多项表单数据 | newForm: Partial\ \| (oldForm: F) => F)
F 为`initialForm`类型 | - | - | +| reset | 重置为初始化数据,如果有持久化数据也会清空 | - | - | - | + +### 事件 + +继承[**useRequest**](/api/core-hooks#userequest)所有事件。 + +| 名称 | 描述 | 回调参数 | 版本 | +| --------- | -------------------- | -------- | ---- | +| onRestore | 持久化数据恢复后触发 | - | - | diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/05-token-authentication.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/05-token-authentication.md new file mode 100644 index 000000000..138d28fc4 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/05-token-authentication.md @@ -0,0 +1,509 @@ +--- +title: Token认证拦截器 +sidebar_position: 50 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info + +策略类型:拦截器 + +版本要求:v1.3.0+ + +::: + +> 在使用扩展 hooks 前,确保你已熟悉了 alova 的基本使用。 + +Token 认证拦截器,对基于 token 的登录、登出、token 附带、token 刷新进行统一管理,并支持无感刷新 token。 + +## 特性 + +- ✨ 统一维护 Token 身份认证的所有代码,包括登录、登出、token 附带、token 刷新等; +- ✨ 支持在客户端和服务端验证 token 过期,并无感刷新 token; +- ✨ 依赖 token 的请求自动等待 token 刷新完成再请求; +- ✨ 使用元数据设置请求身份; +- ✨ 自动放行不依赖 token 的访客请求; + +## 绑定 Token 认证拦截器 + +Token 身份认证是通过全局的拦截器完成的,分别提供了`createClientTokenAuthentication`和`createServerTokenAuthentication` 用于基于客户端和服务端的身份认证。 + +- 基于客户端的身份认证:表示从客户端判断 token 是否过期,例如在登录时获取到的 token 过期时间; +- 基于服务端的身份认证:表示从服务端返回的状态判断 token 是否过期,例如`status`为 401 时表示过期; + +### 绑定基于客户端的身份认证的拦截器 + +```javascript +import { createClientTokenAuthentication } from 'alova/client'; +import { createAlova } from 'alova'; + +const { onAuthRequired, onResponseRefreshToken } = createClientTokenAuthentication({ + // ... +}); + +const alovaInstance = createAlova({ + // ... + beforeRequest: onAuthRequired(method => { + // ...原请求前拦截器 + }), + responded: onResponseRefreshToken((response, method) => { + //...原响应成功拦截器 + return response.json(); + }) +}); +``` + +在`onResponseRefreshToken`中也可以绑定响应错误和完成的拦截器,也和原来的用法相同。 + +```javascript +createAlova({ + // ... + // highlight-start + responded: onResponseRefreshToken({ + onSuccess: (response, method) => { + //...原响应成功拦截器 + }, + onError: (error, method) => { + //...原响应错误拦截器 + }, + onComplete: method => { + //...原响应完成拦截器 + } + }) + // highlight-end +}); +``` + +如果不需要设置拦截器,也可以不传入拦截器函数。 + +```javascript +createAlova({ + //... + // highlight-start + beforeRequest: onAuthRequired(), + responded: onResponseRefreshToken() + // highlight-end +}); +``` + +### 绑定基于服务端的身份认证拦截器 + +与基于客户端的用法相同 + +```javascript +import { createServerTokenAuthentication } from 'alova/client'; +import { createAlova } from 'alova'; + +const { onAuthRequired, onResponseRefreshToken } = createServerTokenAuthentication({ + // ... +}); + +const alovaInstance = createAlova({ + // ... + beforeRequest: onAuthRequired(method => { + // ...原请求前拦截器 + }), + responded: onResponseRefreshToken((response, method) => { + //...原响应成功拦截器 + return response.json(); + }) +}); +``` + +:::warning + +当你使用`GlobalFetch`适配器时,你可能会遇到`TypeError: Failed to execute 'json' on 'Response': body stream already read`这个问题,这是因为`Response`的`body stream`只能访问一次,你可以`response.clone().json()`来解决它。 + +::: + +## 在客户端无感刷新 Token + +设置`refreshToken`并指定 token 是否过期,以及调用刷新 token 的函数就可以了。当 token 刷新完成前,所有依赖 token 的请求都将会等待 token 刷新完成。 + +```javascript +createClientTokenAuthentication({ + refreshToken: { + // 在请求前触发,将接收到method参数,并返回boolean表示token是否过期 + isExpired: method => { + return tokenExpireTime < Date.now(); + }, + + // 当token过期时触发,在此函数中触发刷新token + handler: async method => { + const { token, refresh_token } = await refreshToken(); + localStorage.setItem('token', token); + localStorage.setItem('refresh_token', refresh_token); + } + } +}); +``` + +为了让`refreshToken`请求顺利通过,需要通过元数据标识`authRole`为`refreshToken`。 + +> 了解更多元数据的信息,请前往[method 元数据](/tutorial/getting-started/method-metadata)。 + +```javascript +export const refreshToken = () => { + const method = alovaInstance.Get('/refresh_token'); + method.meta = { + authRole: 'refreshToken' + }; + return method; +}; +``` + +## 在服务端无感刷新 Token + +与在客户端无感刷新 Token 相同,指定 token 是否过期,以及调用刷新 token 的函数就可以了。当 token 刷新完成前,所有依赖 token 的请求都将会等待 token 刷新完成。 + +### 在请求成功拦截器中处理 + +当使用`GlobalFetch`时,只要服务端返回了响应数据,就会触发响应成功拦截器,此时我们需要在响应成功拦截器中处理 token 的刷新。 + +```javascript +createServerTokenAuthentication({ + refreshTokenOnSuccess: { + // 响应时触发,可获取到response和method,并返回boolean表示token是否过期 + // 当服务端返回401时,表示token过期 + isExpired: (response, method) => { + return response.status === 401; + }, + + // 当token过期时触发,在此函数中触发刷新token + handler: async (response, method) => { + const { token, refresh_token } = await refreshToken(); + localStorage.setItem('token', token); + localStorage.setItem('refresh_token', refresh_token); + } + } +}); +``` + +### 在请求错误拦截器中处理 + +当使用`axios`拦截器时,服务端返回了非`200/300`的状态码就会触发响应错误拦截器,此时我们需要在响应错误拦截器中处理 token 的刷新。 + +```javascript +createServerTokenAuthentication({ + refreshTokenOnError: { + // 响应时触发,可获取到error和method,并返回boolean表示token是否过期 + // 当服务端返回401时,表示token过期 + isExpired: (error, method) => { + return error.response.status === 401; + }, + + // 当token过期时触发,在此函数中触发刷新token + handler: async (error, method) => { + const { token, refresh_token } = await refreshToken(); + localStorage.setItem('token', token); + localStorage.setItem('refresh_token', refresh_token); + } + } +}); +``` + +为了让`refreshToken`请求顺利通过,需要通过元数据标识`authRole`为`refreshToken`。 + +> 了解更多元数据的信息,请前往[method 元数据](/tutorial/getting-started/method-metadata)。 + +```javascript +export const refreshToken = () => { + const method = alovaInstance.Get('/refresh_token'); + method.meta = { + authRole: 'refreshToken' + }; + return method; +}; +``` + +## 放行访客请求 + +有些接口不需要依赖 token 认证,我们称它们为“访客请求”,此时我们可以设置它们的元数据为`authRole: null`来绕过前端的拦截,让它们顺利发出请求和接收响应。 + +```javascript +export const requestTokenNotRequired = () => { + const method = alovaInstance.Get('/token_not_required'); + method.meta = { + authRole: null + }; + return method; +}; +``` + +## 登录拦截 + +在身份认证拦截器中,你还可以拦截登录请求,在拦截器中保存登录信息,达到统一维护身份认证代码的目的。 + +首先标识登录请求的元数据为`authRole: 'login'`。 + +```javascript +export const login = () => { + const method = alovaInstance.Get('/login'); + method.meta = { + authRole: 'login' + }; + return method; +}; +``` + +再在登录拦截器中保存登录信息。 + +```javascript +createClientTokenAuthentication({ + login(response, method) { + localStorage.setItem('token', response.token); + localStorage.setItem('refresh_token', response.refresh_token); + } +}); +``` + +> `createServerTokenAuthentication`的登录拦截器用法相同。 + +## 附加 token + +通常,我们会在`beforeRequest`附加 token 到请求信息中。在 Token 认证拦截器中提供了`assignToken`回调函数用于附加 token,它会过滤访客请求和登录请求,并在请求前触发,也可以达到统一维护身份认证代码的目的。 + +```javascript +createClientTokenAuthentication({ + assignToken: method => { + method.config.headers.Authorization = localStorage.getItem('token')}; + } +}); +``` + +> `createServerTokenAuthentication`的 assignToken 回调函数用法相同。 + +## 登出拦截 + +当你的登出也需要调用接口时,也可以拦截登出请求,清除登录信息。 + +首先标识登出请求的元数据为`authRole: 'logout'`。 + +```javascript +export const logout = () => { + const method = alovaInstance.Get('/logout'); + method.meta = { + authRole: 'logout' + }; + return method; +}; +``` + +再在登出拦截器中清除登录信息。 + +```javascript +createClientTokenAuthentication({ + logout(response, method) { + localStorage.removeItem('token'); + localStorage.removeItem('refresh_token'); + } +}); +``` + +> `createServerTokenAuthentication`的登录拦截器用法相同。 + +## 自定义标识身份 + +在上面的元数据身份标识中,实际上都默认的身份标识,如果需要自定义身份标识,你可以按下面这样设置。 + +### token 刷新身份标识 + +```javascript +createClientTokenAuthentication({ + refreshToken: { + // highlight-start + metaMatches: { + refreshToken: true + } + // highlight-end + // ... + } +}); +``` + +```javascript +createServerTokenAuthentication({ + refreshTokenOnSuccess: { + // highlight-start + metaMatches: { + refreshToken: true + } + // highlight-end + // ... + }, + refreshTokenOnError: { + // highlight-start + metaMatches: { + refreshToken: true + } + // highlight-end + // ... + } +}); +``` + +然后,元数据中具有`refreshToken: true`的请求,就会被认定为`refreshToken`身份。 + +```javascript +export const refreshToken = () => { + const method = alovaInstance.Get('/refresh_token'); + method.meta = { + refreshToken: true + }; + return method; +}; +``` + +### 访客身份标识 + +```javascript +createClientTokenAuthentication({ + visitorMeta: { + isVisitor: true + } +}); +``` + +然后,元数据中具有`isVisitor: true`的请求,就会被认定为访客身份。 + +```javascript +export const requestTokenNotRequired = () => { + const method = alovaInstance.Get('/token_not_required'); + method.meta = { + isVisitor: true + }; + return method; +}; +``` + +### 登录身份标识 + +```javascript +createClientTokenAuthentication({ + login: { + // highlight-start + metaMatches: { + login: true + }, + // highlight-end + handler(response, method) { + // 登录拦截器 + } + } +}); +``` + +然后,元数据中具有`login: true`的请求,就会被认定为`login`身份。 + +```javascript +export const login = () => { + const method = alovaInstance.Get('/login'); + method.meta = { + login: true + }; + return method; +}; +``` + +### 登出身份标识 + +```javascript +createClientTokenAuthentication({ + logout: { + // highlight-start + metaMatches: { + logout: true + }, + // highlight-end + handler(response, method) { + // 登出拦截器 + } + } +}); +``` + +然后,元数据中具有`logout: true`的请求,就会被认定为`logout`身份。 + +```javascript +export const logout = () => { + const method = alovaInstance.Get('/logout'); + method.meta = { + logout: true + }; + return method; +}; +``` + +> `createServerTokenAuthentication`的登录拦截器用法相同。 + +## Typescript + +默认情况下,`createClientServerTokenAuthentication`和`createServerTokenAuthentication`适配了`GlobalFetch`请求适配器,你只需要指定`statesHook`的类型,如下: + +```typescript +// highlight-start +const { onAuthRequired, onResponseRefreshToken } = createClientTokenAuthentication< + typeof VueHook +>({ + // highlight-end + //... +}); + +const alovaInstance = createAlova({ + // ... + statesHook: VueHook, + beforeRequest: onAuthRequired(method => { + // method的类型为 Method + }), + responded: onResponseRefreshToken((response, method) => { + // response的类型为Response + return response.json(); + }) +}); +``` + +如果你使用的不是`GlobalFetch`请求适配器,你还需要指定请求适配器的类型,这也很简单。 + +以下为 axios 请求适配器为例,在`createClientTokenAuthentication`中指定请求适配器类型。 + +```typescript +import { axiosRequestAdapter } from '@alova/adapter-axios'; + +// highlight-start +const { onAuthRequired, onResponseRefreshToken } = createClientTokenAuthentication< + typeof VueHook, + typeof axiosRequestAdapter +>({ + // highlight-end + //... +}); +const alovaInstance = createAlova({ + //... + statesHook: VueHook, + // highlight-start + beforeRequest: onAuthRequired(method => { + // method的类型为 Method + // highlight-end + }), + // highlight-start + responded: onResponseRefreshToken((response, method) => { + // response的类型为AxiosResponse + // highlight-end + return response.data; + }) +}); +``` + +基于服务端的 Token 认证拦截器的用法相同。 + +```typescript +import { axiosRequestAdapter } from '@alova/adapter-axios'; + +// highlight-start +createServerTokenAuthentication({ + // highlight-end + //... +}); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/06-use-auto-request.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/06-use-auto-request.md new file mode 100644 index 000000000..380cabae6 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/06-use-auto-request.md @@ -0,0 +1,132 @@ +--- +title: 自动拉取数据 +sidebar_position: 60 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info 策略类型 + +use hook + +::: + +> 在使用扩展 hooks 前,确保你已熟悉了 alova 的基本使用。 + +通过浏览器事件或轮询自动拉取数据,让界面展示最新数据。 + +## 特性 + +- ✨ 支持浏览器聚焦、tab 切换、网络重连、轮询请求等场景下拉取最新数据,可自定义配置监听类型; +- ✨ 支持请求节流,在短时间内多次触发只会发送 1 次请求; +- ✨ 支持自定义事件的监听函数,以适应非浏览器环境下的使用场景; + +## 使用 + +在默认情况下,自动拉取数据的 useHook`useAutoRequest`会在浏览器显示隐藏、聚焦、网络重连时自动拉取最新数据,并在组件卸载时自动取消监听事件。 + +```javascript +import { useAutoRequest } from 'alova/client'; + +const { loading, data, error } = useAutoRequest(() => method()); +``` + +`useAutoRequest`的返回值与[useRequest](/api/core-hooks#userequest)相同。 + +除了支持[useRequest](/api/core-hooks#userequest)的所有配置参数外,还支持自动拉取的配置参数,你可以通过以下配置开启或关闭一些事件,或修改请求节流事件。 + +```javascript +const { loading, data, error, onSuccess, onError, onComplete } = useAutoRequest( + () => method(), + { + /** + * 浏览器显示隐藏触发 + * @default true + */ + enableVisibility: true, + + /** + * 浏览器聚焦触发 + * @default true + */ + enableFocus: true, + + /** + * 网络重连触发 + * @default true + */ + enableNetwork: true, + + /** + * 节流时间,在一定时间内多次触发只会发送1次请求,单位ms + * @default 1000 + */ + throttle: 1000, + + /** + * 轮询请求的时间,设置大于0时有效,单位ms + * @default 0 + */ + pollingTime: 2000 + + // 其他参数同useRequest... + } +); +``` + +:::warning 缓存建议 + +建议在使用`useAutoRequest`时关闭对应请求的缓存,因为当设置缓存时,在触发自动请求时也会命中缓存而获取不到最新数据。具体请阅读[缓存模式](/tutorial/cache/mode)。 + +::: + +## 自定义监听函数 + +以上 4 种自动拉取数据的方式,默认是监听浏览器事件来实现的,当用户在非浏览器环境下使用时,需要自定义监听函数,此函数接收通知请求和 useHook 配置对象作为参数,并返回一个取消监听函数。 +。 + +以下是在`react-native`中自定义监听函数的示例: + +### 网络重连自定义函数 + +```javascript +import NetInfo from '@react-native-community/netinfo'; +useAutoRequest.onNetwork = (notify, config) => { + const unsubscribe = NetInfo.addEventListener(({ isConnected }) => { + isConnected && notify(); + }); + return unsubscribe; +}; +``` + +### 轮询自定义函数 + +```javascript +useAutoRequest.onPolling = (notify, config) => { + const timer = setInterval(notify, config.pollingTime); + return () => clearInterval(timer); +}; +``` + +### 应用切换自定义函数 + +```javascript +import { AppState, Text } from 'react-native'; +useAutoRequest.onVisibility = (notify, config) => { + const subscription = AppState.addEventListener('change', state => { + state === 'active' && notify(); + }); + return () => subscription.remove(); +}; +``` + +### 应用聚焦自定义函数 + +由于 App 没有聚焦事件,所以可以设置为空函数避免报错。 + +```javascript +useAutoRequest.onFocus = (notify, config) => { + return () => {}; +}; +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/07-action-delegation-middleware.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/07-action-delegation-middleware.md new file mode 100644 index 000000000..d6bbf88e3 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/07-action-delegation-middleware.md @@ -0,0 +1,205 @@ +--- +title: 跨组件触发请求 +sidebar_position: 70 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info 策略类型 + +中间件 + +::: + +> 在使用扩展 hooks 前,确保你已熟悉了 alova 的基本使用。 + +从前,在一个组件中想要触发另一个组件中的请求,你需要将数据保存到 Store 中,通过分发 Action 完成。现在,你可以使用这个中间件**消除组件层级的限制**,在任意组件中快速地触发任意请求的操作函数。 + +例如,你可以某个组件中更新了菜单数据后,重新触发侧边菜单栏的重新请求,从而刷新数据。当操作了列表数据后,触发列表更新。 + +## 示例 + +[跨组件触发请求 Demo](/tutorial/example/action-delegation-middleware) + +## 特性 + +- ✨ 委托任意 alova 中的 use hook 的操作函数; +- ✨ 任意位置触发已委托的操作函数; + +## 使用 + +### 基本使用 + +> 以 vue3 为例,在 react、svelte 中用法相同。 + +在组件 A 中使用`actionDelegationMiddleware`委托`useRequest`的操作函数。 + +```javascript title=组件A +import { actionDelegationMiddleware } from 'alova/client'; + +useRequest(queryTodo, { + // ... + middleware: actionDelegationMiddleware('actionName') +}); +``` + +在任意一个组件中(如组件 B)通过`accessAction`传入指定的委托名称触发组件 A 中的`useRequest`的操作函数。 + +```javascript title=组件B +import { accessAction } from 'alova/client'; + +accessAction('actionName', delegatedActions => { + // 调用组件A中的send函数 + delegatedActions.send(); + + // 调用组件A中的abort函数 + delegatedActions.abort(); +}); +``` + +:::info 注意 + +1. alova 内的全部 use hook 都支持操作函数委托,但不同的 use hook 所委托的函数有所不同。 +2. 使用`actionDelegationMiddleware`时,委托名称可传入字符串、数字、symbol 值。 + +::: + +### 批量触发操作函数 + +在上面的例子中,我们使用`accessAction`触发了一个 use hook 的操作函数,但实际上,相同名称的委托不会互相覆盖,而是会保存在一组中,我们可以使用这个名称同时触发它们委托的函数。 + +```javascript title=组件C +import { actionDelegationMiddleware } from 'alova/client'; + +useRequest(queryTodo, { + // ... + middleware: actionDelegationMiddleware('actionName1') +}); +``` + +```javascript title=组件D +import { actionDelegationMiddleware } from 'alova/client'; + +useRequest(queryTodo, { + // ... + middleware: actionDelegationMiddleware('actionName1') +}); +``` + +```javascript title=组件E +import { accessAction } from 'alova/client'; + +// 因为将会匹配组件C、组件D的委托hook,因此回调函数将被执行两次 +accessAction('actionName1', delegatedActions => { + // 调用组件C、组件D中的send函数 + delegatedActions.send(); + + // 调用组件C、组件D中的abort函数 + delegatedActions.abort(); +}); +``` + +同时,还可以在`accessAction`中使用正则表达式来批量触发委托名称满足条件的操作函数 + +```javascript title=组件F +import { actionDelegationMiddleware } from 'alova/client'; + +useRequest(queryTodo, { + // ... + middleware: actionDelegationMiddleware('prefix_name1') +}); +``` + +```javascript title=组件G +import { actionDelegationMiddleware } from 'alova/client'; + +useRequest(queryTodo, { + // ... + middleware: actionDelegationMiddleware('prefix_name2') +}); +``` + +```javascript title=组件H +import { accessAction } from 'alova/client'; + +// 因为将会匹配组件F、组件G的委托hook,因此回调函数将被执行两次 +accessAction(/^prefix_/, delegatedActions => { + // 调用组件F、组件G中的send函数 + delegatedActions.send(); + + // 调用组件F、组件G中的abort函数 + delegatedActions.abort(); +}); +``` + +## 操作函数委托表 + +尽管大部分 hook 委托的操作函数与它本身带有的操作函数相同,但这不是绝对的,以下是每个 hook 的操作函数委托表。 + +### useRequest + +| 名称 | 描述 | 函数参数 | 返回值 | 版本 | +| ------ | ------------------------------------------------------- | -------- | ------ | ---- | +| send | 与 [useRequset](/api/core-hooks#userequest).send 相同 | | | - | +| abort | 与 [useRequset](/api/core-hooks#userequest).abort 相同 | | | - | +| update | 与 [useRequset](/api/core-hooks#userequest).update 相同 | | | - | + +### useWatcher + +与[useRequest 委托列表](#userequest)相同。 + +### useFetcher + +| 名称 | 描述 | 函数参数 | 返回值 | 版本 | +| ------ | ------------------------------------------------------- | -------- | ------ | ---- | +| fetch | 与 [useFetcher](/api/core-hooks#usefetcher).fetch 相同 | | | - | +| abort | 与 [useFetcher](/api/core-hooks#usefetcher).abort 相同 | | | - | +| update | 与 [useFetcher](/api/core-hooks#usefetcher).update 相同 | | | - | + +### usePagination + +| 名称 | 描述 | 函数参数 | 返回值 | 版本 | +| -------- | ------------------------------------------------------------------ | ---------------------------------------------------------------------------------- | ------------------ | ---- | +| refresh | 详见[usePagination 操作函数](/tutorial/strategy/usePagination#api) | | | - | +| insert | 详见[usePagination 操作函数](/tutorial/strategy/usePagination#api) | | | - | +| remove | 详见[usePagination 操作函数](/tutorial/strategy/usePagination#api) | | | - | +| replace | 详见[usePagination 操作函数](/tutorial/strategy/usePagination#api) | | | - | +| reload | 详见[usePagination 操作函数](/tutorial/strategy/usePagination#api) | | | - | +| update | 详见[usePagination 操作函数](/tutorial/strategy/usePagination#api) | | | - | +| getState | 按名称获取分页相关数据 | stateKey: 'page' \| 'pageSize' \| 'data' \| 'pageCount' \| 'total' \| 'isLastPage' | 对应 statekey 的值 | - | + +### useSQRequest + +与[useRequest 委托列表](#userequest)相同。 + +### useForm + +| 名称 | 描述 | 函数参数 | 返回值 | 版本 | +| ---------- | ------------------------------------------------------- | -------- | ------ | ---- | +| updateForm | 详见[useForm 操作函数](/tutorial/strategy/useForm#api) | | | - | +| reset | 详见[useForm 操作函数](/tutorial/strategy/useForm#api) | | | - | +| send | 与 [useRequset](/api/core-hooks#userequest).send 相同 | | | - | +| abort | 与 [useRequset](/api/core-hooks#userequest).abort 相同 | | | - | +| update | 与 [useRequset](/api/core-hooks#userequest).update 相同 | | | - | + +### useCaptcha + +与[useRequest 委托列表](#userequest)相同。 + +### useRetriableRequest + +| 名称 | 描述 | 函数参数 | 返回值 | 版本 | +| ------ | ------------------------------------------------------------------------------ | -------- | ------ | ---- | +| stop | 详见[useRetriableRequest 操作函数](/tutorial/strategy/useRetriableRequest#api) | | | - | +| send | 与 [useRequset](/api/core-hooks#userequest).send 相同 | | | - | +| abort | 与 [useRequset](/api/core-hooks#userequest).abort 相同 | | | - | +| update | 与 [useRequset](/api/core-hooks#userequest).update 相同 | | | - | + +### useSerialRequest + +与[useRequest 委托列表](#userequest)相同。 + +### useSerialWatcher + +与[useRequest 委托列表](#userequest)相同。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/02-virtual-data.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/02-virtual-data.md new file mode 100644 index 000000000..19036ae56 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/02-virtual-data.md @@ -0,0 +1,177 @@ +--- +title: 虚拟数据 +sidebar_position: 20 +--- + +实际上,虚拟数据是一个拥有唯一 id 的引用对象,其追查机制就是通过先生成虚拟数据 id 和响应数据之间的映射,再通过虚拟数据 id 查找并替换为实际值来实现的。 + +当原始值为引用类型时,表现和原始值相同,但基本类型的虚拟数据使用的是`Number, String, Boolean`封装类,以及自定义的`Undefined, Null`封装类,它们的表现形式与原始值有一些偏差,以下列出了虚拟数据的特性,以及虚拟数据的辅助函数的使用,辅助函数详情将在章节末尾介绍。 + +## 字符串拼接 + +当虚拟数据进行字符串拼接时,将被转换为虚拟数据 id 进行拼接。 + +```javascript +const virtualData = createVirtualData(1); +'a' + virtualData; // a[vd:xxxxxx] +1 + virtualData; // 1[vd:xxxxxx] +``` + +## 数据比较 + +虚拟数据无法直接用于比较,但实际场景中经常使用虚拟数据和实际数据混合比较,此时可使用`equals`比较。 + +```javascript +import { equals } from 'alova/client'; + +equals('a', 'a'); // true + +const virtualData1 = createVirtualData(1); +const virtualData2 = virtualData1.clone(); // 克隆虚拟数据 +equals(virtualData1, virtualData2); // virtualData1和virtualData2的id相同时为true +equals(virtualData1, '[vd:xxxxxx]'); // virtualData1的id也为[vd:xxxxxx]时为true +``` + +## 参与运算 + +参与运算如`+-*/%`,数值比较、以及位运算时,无法自动转换为原始值,可通过`dehydrateVData`转换为原始值再进行计算。 + +```javascript +import { dehydrateVData } from 'alova/client'; + +const virtualData = createVirtualData(1); +dehydrateVData(virtualData) + 1; // 2 +dehydrateVData(virtualData) > 0; // true +``` + +## 类型操作符 + +因为虚拟数据在基本数据类型上使用封装类实现,使用`typeof`获取类型时将始终会返回`object`,也可通过`dehydrateVData`转换为原始值再获取类型 + +```javascript +const vNum = createVirtualResponse(1); +typeof vNum === 'object'; // true +const vUndef = createVirtualResponse(undefined); +typeof vUndef === 'object'; // true + +typeof dehydrateVData(vNum) === 'number'; // true +typeof dehydrateVData(vUndef) === 'undefined'; // true +``` + +为了解决这个问题,你可以使用虚拟数据辅助函数**dehydrateVData**,它可以获取一个虚拟数据的原始值,当遇到非虚拟数据时将原样返回 + +```javascript +const vNum = createVirtualResponse(1); +typeof dehydrateVData(vNum) === 'number'; // true +dehydrateVData(vNum) === 1; // true +dehydrateVData('string') === 'string'; // true +``` + +## 视图展示 + +默认情况下,虚拟数据展示到视图中时将隐式调用`toString`,但有时候也会遇到显示错乱的问题,以及在**react**中渲染一个对象将会报以下错误: + +``` +Uncaught Error: Objects are not valid as a React child (found: object with keys {}). If you meant to render a collection of children, use an array instead. +``` + +因此建议在显示时使用`dehydrateVData`转换为原始数据进行展示。 + +## 虚拟数据辅助函数 + +### dehydrateVData + +将虚拟数据脱水,返回它的原始值,如 target 为非虚拟数据则原样返回。 + +```typescript +// type +function dehydrateVData(target: any): any; + +// example +dehydrateVData(1); // 1 +const virtualData = createVirtualData(1); +dehydrateVData(virtualData); // 1 +``` + +### stringifyVData + +虚拟数据字符串化,返回虚拟数据 id,当 returnOriginalIfNotVData 设置为 false 时,非虚拟数据将原样返回。 + +```typescript +// type +function stringifyVData(target: any, returnOriginalIfNotVData?: boolean): any; + +// example +stringifyVData(1); // 1 +stringifyVData(1, false); // undefined + +const virtualData = createVirtualData(1); +stringifyVData(virtualData); // [vd:xxxxxx] +``` + +### equals + +以兼容虚拟数据的方式判断两个值是否相等,当没有虚拟数据参与比较时将严格对比,否则将对比虚拟数据 id 是否相同,如果比较数据可能存在虚拟数据参与,则建议使用此函数比较。 + +```typescript +// type +function equals(prevValue: any, nextValue: any): boolean; + +// example +equals('a', 'a'); // true +const virtualData1 = createVirtualData(1); +const virtualData2 = virtualData1.clone(); // 克隆虚拟数据 +equals(virtualData1, virtualData2); // virtualData1和virtualData2的id相同时为true +equals(virtualData1, '[vd:xxxxxx]'); // virtualData1的id也为[vd:xxxxxx]时为true +``` + +### isVData + +判断目标数据是否为虚拟数据 + +```typescript +// type +function isVData(target: any): boolean; + +// example +isVData(1); // false +const virtualData = createVirtualData(1); +isVData(virtualData); // true +isVData('[vd:xxxxxx]'); //true +``` + +## 虚拟数据的替换限制 + +虚拟数据的追查机制只能深度遍历相关数据,然后将具有虚拟数据标识的数据替换为实际,如果一些数据依赖虚拟数据生成的,在虚拟数据替换为实际数据后不会重新计算。 + +以下情况,即使 virtualId 替换为实际数据了,这个请求的 id 也不会重新再计算,因此如果需要替换,需要直接将 virtualId 作为请求参数,示例如下: + +```javascript +const deleteTodo = virtualId => { + return alova.Delete('/deleteTodo', { + id: dehydrateVData(virtualId) === null ? 1 : 2 + }); +}; +``` + +但如果将虚拟数据作为字符串拼接时,它将自动转换为虚拟数据 id 参与字符串拼接,这种情况将会有效。以下情况,请求 id 在开始时的值为`id_[vd:xxxxxx]`,当 virtualId 被替换为响应值后(假设替换为 1),它将自动更新为`id_1`。 + +```javascript +const deleteTodo = virtualId => { + return alova.Delete('/deleteTodo', { + id: 'id'_virtualId + }); +}; +``` + +## 接下来 + +虽然实现无感交互已经足够简单了,但相比保守请求还是有一些额外的处理,具体实现大致分为以下几个步骤。 + +1. 以保守请求方式实现功能; +2. 手动更新列表数据,实现本地化数据补偿; +3. 记录数据操作,以便在还有未提交修改时手动补充到最新记录; +4. 将未提交的数据手动补偿到列表,以便即使数据未提交也能展示最新状态; +5. 修改未提交数据时,拦截带虚拟数据的请求; + +接下来,我们将以一个简单的示例进行演示。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/03-start-silent-factory.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/03-start-silent-factory.md new file mode 100644 index 000000000..9c1412416 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/03-start-silent-factory.md @@ -0,0 +1,68 @@ +--- +title: 启动静默工厂 +sidebar_position: 30 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +静默队列默认不启动,需要我们指定启动参数进行初始化,一般情况下,在入口文件中调用`bootSilentFactory`来初始化静默工厂,它将通过指定的配置项来读取还未执行的请求到对应的静默队列中并启动这些队列。 + + + + +```javascript +import { bootSilentFactory } from 'alova/client'; +import { alovaInst } from '@/api'; + +bootSilentFactory({ + // 启动时指定 alova 实例,用于请求信息存储、请求发送 + alova: alovaInst, + + // 延迟启动的时间,单位毫秒,默认为2000ms,具体描述看后续说明 + delay: 1000 +}); +``` + + + + + +```javascript +import { bootSilentFactory } from 'alova/client'; +import { alovaInst } from '@/api'; + +bootSilentFactory({ + // 启动时指定 alova 实例,用于请求信息存储、请求发送 + alova: alovaInst, + + // 延迟启动的时间,单位毫秒,默认为2000ms,具体描述看后续说明 + delay: 1000 +}); +``` + + + + + +```javascript +import { bootSilentFactory } from 'alova/client'; +import { alovaInst } from '@/api'; + +bootSilentFactory({ + // 启动时指定 alova 实例,用于请求信息存储、请求发送 + alova: alovaInst, + + // 延迟启动的时间,单位毫秒,默认为2000ms,具体描述看后续说明 + delay: 1000 +}); +``` + + + + +:::warning `delay`参数说明 + +在实际场景下,进入当前页面时也会发送请求加载页面数据,为了保证用户可以更快地看到页面数据,需要将加载数据的请求前置到队列起始位置,否则可能会造成加载数据的请求后置到队列尾部,此时就需要等到前面的所有请求完成才会加载页面数据,这显然是不合适的,因此通过延迟一段时间初始化来让加载数据的请求先进入队列,达到“插队”的效果,具体的延迟时间需要根据页面渲染所需的时间而定。 + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/04-conservative-request.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/04-conservative-request.md new file mode 100644 index 000000000..b6c395468 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/04-conservative-request.md @@ -0,0 +1,136 @@ +--- +title: 步骤1-以保守请求实现功能 +sidebar_position: 40 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +以 Todo 管理为示例,来实现 Todo 在无感交互模式下的创建、编辑、删除等功能,在接下来的章节中将提供请求相关的关键代码。 + +> 这里的[简单列表页示例](/tutorial/example/silent-submit-simple-list)包含了完整的代码,你可以进入体验。 + +使用 **useSQRequest** 来替代 alova 提供的 **useRequest**,接下来先以最常见的保守请求模式来实现,再一步一步处理无感交互模式的兼容性。 + +## 创建 alova 实例和相关 method + +```javascript title="api.js" +import { createAlova } from 'alova'; + +export const alovaInst = createAlova({ + /*...*/ +}); + +/** 加载todo列表 */ +const todoList = () => alovaInst.Get('/todo'); + +/** 加载todo详情 */ +const todoDetail = id => + alovaInst.Get('/todo', { + params: { id } + }); + +/** 创建、编辑todo项 */ +const createOrEditTodo = (data, id) => + alovaInst.Post('/todo', { + data, + id + }); + +/** 删除todo项 */ +const deleteTodo = id => alovaInst.Delete('/todo', { id }); +``` + +## 启动静默工厂 + +```javascript title="main.js" +import { bootSilentFactory } from 'alova/client'; +import { alovaInst } from './api.js'; + +bootSilentFactory({ + alova: alovaInst +}); +``` + +## 加载 Todo 列表 + +以最简单的方式加载并展示页面数据 + +```javascript +import { useSQRequest } from 'alova/client'; +import { todoList } from './api.js'; +const { data, loading, error } = useSQRequest(todoList, { + initialData: [] +}); +``` + +## 进入 Todo 创建/编辑页 + +创建 todo 项时,id 为空,不发送详情获取请求,编辑 todo 项时,id 有值,将获取详情数据。 + +```javascript +import { useSQRequest } from 'alova/client'; +import { todoDetail } from './api.js'; + +const id = /* todo id */; +const { loading, data } = useSQRequest(() => todoDetail(id), { + initialData: { + title: '', + time: new Date() + }, + immediate: !!id +}); +``` + +## 创建/编辑 Todo 项 + +通过提交事件触发请求,提交成功后调用 fetch 重新拉取最新的列表数据,界面将自动展示最新数据。 + +```javascript +import { useFetcher } from 'alova'; +import { useSQRequest } from 'alova/client'; +import { createOrEditTodo, todoList } from './api.js'; + +const id = /* todo id */; +const { loading, data, send, onSuccess } = useSQRequest(createOrEditTodo, { + immediate: false, +}); + +const { fetch } = useFetcher(); +onSuccess(() => { + // 重新拉取列表数据 + fetch(todoList); +}) + +// 提交事件回调函数 +const handleSubmit = newData => { + send(newData, id); +}; + +``` + +## 删除 Todo 项 + +通过 id 删除对应的 todo 项 + +```javascript +import { useSQRequest } from 'alova/client'; +import { deleteTodo, todoList } from './api.js'; + +const { loading, data, send, onSuccess } = useSQRequest(deleteTodo, { + immediate: false +}); + +const { fetch } = useFetcher(); +onSuccess(() => { + // 重新拉取列表数据 + fetch(todoList); +}); + +// 事件回调触发删除请求 +const handleDelete = deletingId => { + send(deletingId); +}; +``` + +至此,一个简单的 Todo 列表管理的相关请求功能就完成了,接下来我们将开始对它进行改造,来兼容无感交互模式。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/05-modify-response.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/05-modify-response.md new file mode 100644 index 000000000..63d69adf9 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/05-modify-response.md @@ -0,0 +1,260 @@ +--- +title: 步骤2-调整响应处理 +sidebar_position: 50 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +在上一节的保守请求示例中,我们在 Todo 项的创建、编辑和删除请求响应后调用`fetch`重新拉取数据刷新的页面,为了能在操作后立即展示结果,我们需要进行以下调整: + +1. 将创建、编辑和删除请求的行为模式设置为`silent`,它们将会在请求时立即触发成功回调; +2. 手动更新列表,而不是拉取数据,使用虚拟数据占位服务端的响应数据; +3. 保存操作记录,用于在刷新页面时进行数据补偿; + +## 设置行为模式 + +通过配置参数`behavior`进行设置,可选参数为`queue`、`silent`、`static`,或者一个返回行为数据的函数实现动态设置行为模式,默认为`queue`。 + +以下为静态设置 behavior 参数。 + +```javascript +useSQRequest(createOrEditTodo, { + // highlight-start + behavior: 'silent', + // highlight-end + immediate: false +}); +``` + +以下为动态设置 behavior 参数。 + +```javascript +const { send } = useSQRequest(createOrEditTodo, { + // highlight-start + // arg参数可通过send函数传入 + behavior: arg => { + if (arg === 0) return 'silent'; + return 'queue'; + }, + // highlight-end + immediate: false +}); +``` + +> 当 behavior 设置为函数时,它将在每次发起请求时被调用,来确定本次请求以哪种行为来处理。 + +## 静默队列说明 + +将 behavior 参数设置为`queue`或`silent`后,请求将进入静默队列等待发起请求,默认情况下它们将进入名称为`default`的队列,你还可以指定其他队列来保存 silentMethod 实例,队列之间互不干扰。 + +```javascript +useSQRequest(createOrEditTodo, { + // highlight-start + // 指定请求信息进入名称为queue-2的队列中 + queue: 'queue-2', + // highlight-end + behavior: 'silent', + immediate: false +}); +``` + +## 在回调中手动更新列表 + +### 在新增/编辑后更新列表 + + + + +当列表页未被销毁,例如在当前页使用模态框操作,或使用了``(Vue)保留了页面组件,数据还会存在,此时我们使用**updateStateEffect**来更新列表数据,相比于 alova 导出的**updateState**,它具有追踪虚拟数据追踪功能,当获取到响应数据后,它将自动追踪列表数据内的虚拟数据,并替换为实际数据。 + +```javascript +import { useSQRequest, updateStateEffect } from 'alova/client'; +import { createOrEditTodo, todoList } from './api.js'; + +const { onSuccess } = useSQRequest(createOrEditTodo, { + behavior: 'silent', + immediate: false, + + // highlight-start + // 在处理列表更新前,需要根据响应数据的结构先构造相同结构的虚拟响应数据 + // 例如,在创建 Todo 项时将返回这条数据的 id。 + silentDefaultResponse: () => { + return { + id: '--' + }; + } + // highlight-end +}); + +// highlight-start +onSuccess(({ data, silentMethod }) => { + // 构造列表数据项 + const editingItem = { + ...detail, + + // 当编辑时,使用原id,否则使用响应数据内的id + // 在静默提交时,data.id为虚拟数据,在static行为模式时,data.id为实际的id值 + id: id || data.id + }; + + // 使用updateStateEffect,而不是updateState + updateStateEffect(todoList(), todoListRaw => { + if (id) { + todoListRaw = todoListRaw.map(item => (item.id === id ? editingItem : item)); + } else { + todoListRaw.unshift(editingItem); + } + return todoListRaw; + }); +}); +// highlight-end +``` + +> updateStateEffect 的使用方法与[updateState](/tutorial/advanced/update-across-components)一致 + + + + +当列表页已被销毁,数据已被释放,例如跳转到了新页面,此时使用**setCache**更新缓存数据,当返回列表页时将重新发起请求并命中更新的缓存。 + +```javascript +import { useSQRequest, setCache, equals } from 'alova/client'; +import { createOrEditTodo, todoList } from './api.js'; + +const urlParams = new URLSearchParams(window.location.search); +const id = urlParams.get('id') || ''; +const { onSuccess } = useSQRequest(createOrEditTodo, { + behavior: 'silent', + immediate: false, + + // highlight-start + // 在处理列表更新前,需要根据响应数据的结构先构造相同结构的虚拟响应数据 + // 例如,在创建 Todo 项时将返回这条数据的 id。 + silentDefaultResponse: () => { + return { + id: '--' + }; + } + // highlight-end +}); +// highlight-start +onSuccess(({ data, silentMethod }) => { + // 构造列表数据项 + const editingItem = { + ...detail, + + // 当编辑时,使用原id,否则使用响应数据内的id + // 在静默提交时,data.id为虚拟数据,在static行为模式时,data.id为实际的id值 + id: id || data.id + }; + + const methodTodoList = todoList(); + setCache(methodTodoList, todoListRaw => { + if (id) { + todoListRaw = todoListRaw.map(item => (equals(item.id, id) ? editingItem : item)); + } else { + todoListRaw.unshift(editingItem); + } + return todoListRaw; + }); + // 调用setUpdateState设置响应数据追踪,这样就实现了和updateStateEffect相同的延迟更新效果 + if (silentMethod) { + silentMethod.setUpdateState(methodTodoList); + silentMethod.save(); + } +}); +// highlight-end +``` + + + + +### 移除后更新列表 + +```javascript +import { useSQRequest, updateStateEffect } from 'alova/client'; +import { deleteTodo, todoList } from './api.js'; + +const { loading, data, send, onSuccess } = useSQRequest(deleteTodo, { + immediate: false, + // highlight-start + behavior: 'silent' + // highlight-end +}); + +onSuccess(({ sendArgs: [deletingId] }) => { + updateStateEffect(todoList(), todoListRaw => + todoListRaw.filter(item => item.id !== deletingId) + ); +}); + +// 事件回调触发删除请求 +const handleDelete = deletingId => { + send(deletingId); +}; +``` + +## 保存操作记录 + +仅仅完成手动更新列表还不够,我们还需要考虑当网络恢复后,当请求队列还存等待中的请求时,此时加载的列表数据还不包括未提交请求的部分,这会让用户造成一定的困惑: + +> “我明明已经新增了多条数据,为什么列表中没有?” + +因此我们需要在成功回调中记录操作,以及相关数据,以便再次加载列表数据时,将未提交的数据手动补偿到列表中,从而让列表数据始终保持最新状态。 + +保存操作记录也很简单,只需要将相关数据挂载到 silentMethod 实例上,它将随着实例一同被持久化。 + +### 创建/编辑成功回调 + +```javascript +// ... +onSuccess(({ silentMethod }) => { + // 构造列表数据项 + const editingItem = { + ...detail, + id: id || data.id + }; + // ... + // highlight-start + if (silentMethod) { + // 设置名称,以便后续查询 + // 如果editingItem.id是虚拟数据将自动转换为它的id + silentMethod.entity.setName('edit' + editingItem.id); + silentMethod.reviewData = { + operate: id ? 'edit' : 'add', + data: editingItem + }; + silentMethod.save(); + } + // highlight-end +}); +``` + +### 删除成功回调 + +```javascript +// ... +onSuccess(({ sendArgs: [deletingId], silentMethod }) => { + // ... + // highlight-start + if (silentMethod) { + silentMethod.reviewData = { + operate: 'delete', + data: { + id: deletingId + } + }; + silentMethod.save(); + } + // highlight-end +}); +``` + +### 注意事项 + +1. onSuccess 回调函数中,只有在`queue`和`silent`行为模式下 silentMethod 才有值; +2. 一般而言,你可以通过`silentMethod.a = ...`、或`silentMethod.b = ...`来保存操作记录,但在 typescript 中会报错,因此特别提供了*reviewData*作为静默提交操作记录的保存属性; +3. 修改 silentMethod 数据后,需要通过`silentMethod.save()`来保存修改; + +下一步将对静默提交请求设置重试参数。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/01-sensorless-data-interaction/06-request-retry.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/06-request-retry.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/01-sensorless-data-interaction/06-request-retry.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/06-request-retry.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/07-data-compensation.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/07-data-compensation.md new file mode 100644 index 000000000..b1922a75b --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/07-data-compensation.md @@ -0,0 +1,56 @@ +--- +title: 步骤4-数据补偿 +sidebar_position: 70 +--- + +用户可能在断网环境下进行了一些数据操作,此时静默队列内将堆满未提交的请求,当网络恢复后,由于时序机制的限制,完成这些请求需要消耗一点时间,此时加载的列表数据还不包括未提交请求的部分,这会让用户造成一定的困惑: + +> “我明明已经新增了多条数据,为什么列表中没有?” + +因此我们需要将未提交的数据手动补偿到列表中,从而让列表数据始终保持最新状态,在这一步中将会使用到保存的操作记录,对列表数据进行数据补偿,它其实也很简单,我们只需要在列表请求成功后遍历相关队列的 silentMethod 实例,将上一步记录的操作记录更新到列表数据中。 + +```javascript +import { useSQRequest, filterSilentMethods, equals } from 'alova/client'; +import { todoList } from './api.js'; +const { data, loading, onSuccess } = useSQRequest(todoList, { + initialData: [] +}); + +onSuccess(() => { + // 获取default队列下的所有silentMethod实例 + const silentMethods = filterSilentMethods(); + silentMethods.forEach(({ reviewData }) => { + if (!reviewData) { + return; + } + const { operate, data } = reviewData; + const index = todoListData.findIndex(({ id }) => equals(id, data.id)); + if ((operate === 'edit' || operate === 'remove') && index >= 0) { + operate === 'edit' ? todoListData.splice(index, 1, data) : todoListData.splice(index, 1); + } else if (operate === 'add' && index < 0) { + // 在重新请求并命中缓存时将会有已添加的未提交项,这些需要过滤 + todoListData.unshift(data); + } + }); +}); +``` + +## 相关函数解释 + +### filterSilentMethods + +按 method 名称或正则表达式过滤出指定的 silentMethod 实例,它的定义如下: + +```typescript +function filterSilentMethods( + methodNameMatcher?: string | number | RegExp, + queueName?: string, + filterActive?: boolean +): SilentMethod[]; +``` + +**methodNameMatcher:**method 名称匹配器,如果是数字或字符串将过滤出完全匹配名称的结果,如果是正则表达式则过滤出匹配的结果,如果不传则过滤出所有结果; + +**queueName**:指定查找的队列,未传时默认查找*default*队列; + +**filterActive**:是否过滤掉激活状态的实例 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/08-edit-item.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/08-edit-item.md new file mode 100644 index 000000000..916a877d8 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/08-edit-item.md @@ -0,0 +1,111 @@ +--- +title: 步骤5-编辑数据 +sidebar_position: 80 +--- + +> 当用户在断网情况下需要编辑数据,怎么办? + +这时,需要分两种情况来说明: + +1. 列表数据可以满足编辑页的数据回显,此时可以将列表数据传递到编辑页,而不需要通过请求获取,此时所有列表数据在静默提交模式下都支持编辑; +2. 编辑页回显数据需要通过 api 获取的,只有在本地有缓存的列表项才可以正常回显数据,例如: + 1. 在网络断开前已经访问过的列表项,再次请求可以命中缓存; + 2. 通过静默提交模式创建,但还没有提交成功的列表项,提交数据还存在于 silentMethod 实例中; + +而在这边我们将重点讨论 **2-2** 的情况。 + +## 编辑静默提交项 + +在之前的章节中我们知道,当新建的数据项还没有成功提交时,将会使用虚拟数据作为 id 的占位符,通常,我们也是通过 id 来获取数据项的,此时我们在`useSQRequeset`上实现了虚拟数据拦截,一个请求如果附带了虚拟数据信息,它将在发送前被拦截并可以指定数据来替代响应数据,并且放弃这次请求。 + +还记得在 [步骤 2-调整响应处理](/tutorial/strategy/sensorless-data-interaction/modify-response) 中保存的 **silentMethod.reviewData** 吗? + +```javascript +onSuccess(({ silentMethod }) => { + // 构造列表数据项 + const editingItem = { + ...detail, + id: id || data.id + }; + // ... + if (silentMethod) { + // highlight-start + // 设置名称是为了在拦截时查找对应的silentMethod实例 + silentMethod.entity.setName('edit' + editingItem.id); + silentMethod.reviewData = { + operate: id ? 'edit' : 'add', + data: editingItem + }; + // 别忘了调用save + silentMethod.save(); + // highlight-end + } +}); +``` + +它不仅可以用于数据补偿,还可以用于在编辑页中回显数据。 + +```javascript +const { loading, data } = useSQRequest(id => todoDetail(id), { + initialData: { + title: '', + time: new Date() + }, + immediate: false, + + // highlight-start + // 设置拦截函数,当这个请求存在虚拟数据时函数将被调用 + // 如果返回了reviewData则会替代响应数据,并放弃本次请求,否则仍然发起请求 + vDataCaptured: () => { + const targetSM = filterSilentMethods('edit' + todoId).pop(); + if (targetSM?.reviewData) { + return { ...targetSM.reviewData.data }; + } + } + // highlight-end +}); +``` + +:::warning 注意 + +你可以在 **silentMethod.reviewData** 中保存足够多的数据,让它既可以满足列表数据补偿,也可以满足编辑页数据回显。 + +::: + +至此,通过静默提交模式创建的数据项也支持编辑了!还有什么问题呢,嗯...还有最后一个。 + +## 当正在编辑的数据项提交成功时 + +当用户正在编辑一条还未提交成功的数据项时,它突然提交成功了!这时我们需要将编辑页中使用到的虚拟数据替换为实际数据,例如将虚拟 id 替换为实际的 id,在接下来的编辑中使用实际的 id 进行提交,这也很简单,我们只需要监听静默提交成功事件即可完成,它将接收到虚拟数据和实际数据组成的数据集合。 + +```javascript +import { onSilentSubmitSuccess, stringifyVData } from 'alova/client'; + +// ... +// id在初始化时是虚拟数据 +let id = /* todo virtual id */; + +// highlight-start +// 绑定监听静默提交成功事件来更新id,并返回解绑函数,别忘了在组件销毁时调用解绑函数 +const unbindEvent = onSilentSubmitSuccess(event => { + const vDataId = stringifyVData(id); + if (event.vDataResponse[vDataId]) { + id = event.vDataResponse[vDataId]; + + // 以下是将url中的虚拟id更改为实际的id + history.replaceState(null, '', '?id=' + currentId); + } +}); +// highlight-end +``` + +在这边,`event.vDataResponse` 值是由虚拟数据 id 和实际数据组成的集合,它的格式如下: + +```javascript +{ + '[vd:aaaaaa]': { id: 1 }, + '[vd:bbbbbb]': 1 +} +``` + +至此,我们已经完成了一个简单列表的无感交互的所有内容,但在其他应用场景下如编辑类应用、复杂列表管理等,将可能遇到更多不一样的需求,此时 alova 还有什么特性是我们能使用的呢?请再看下一章! diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/09-what-more.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/09-what-more.md new file mode 100644 index 000000000..fce2949f1 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/09-what-more.md @@ -0,0 +1,353 @@ +--- +title: 还有什么? +sidebar_position: 90 +--- + +## 虚拟数据的作用说明 + +在之前的章节中我们以虚拟数据作为 id 占位符,但它的作用不止于此,它可以占位任意的响应数据,例如在复杂列表中,当创建数据项时服务端需要计算产生额外的数据,此时就可以将这些额外数据也通过虚拟数据占位,但这要求额外的数据需要在创建数据项时一并返回。看以下示例: + +```javascript +const { onSuccess, send } = useSQRequest(createOrEditData, { + behavior: 'silent', + immediate: false, + + // 构造与响应数据相同的数据结构 + silentDefaultResponse: () => { + return { + id: '--', + extra1: '', + extra2: '' + }; + } +}); +onSuccess(event => { + event.data.id; // 虚拟数据 + event.data.extra1; //虚拟数据 + event.data.extra2; //虚拟数据 +}); +``` + +## useSQRequest 中新增的事件 + +为了更好地监听请求在队列中的行为,`useSQRequset` 还额外提供了以下 3 个事件监听函数,你可以通过以下方式获取绑定函数。 + +```javascript +const { onBeforePushQueue, onPushedQueue, onFallback } = useSQRequest(/* ... */); +``` + +### onBeforePushQueue + +silentMethod 进入请求队列前事件,当行为模式为`queue`或`silent`时有效,可以在这个事件回调中返回 `false` 来阻止当前 silentMethod 进入队列,例如你可能想当前 silentMethod 替换掉另外一个时,可以这样做: + +```javascript +// ... +onBeforePushQueue(event => { + // 每次替换指定id的旧silentMethod,减少请求次数 + const prevSumbmitMethod = getSilentMethod('temp' + id); + if (event.silentMethod && prevSumbmitMethod) { + prevSumbmitMethod.replace(event.silentMethod); + return false; + } +}); +``` + +### onPushedQueue + +silentMethod 进入队列后的事件,当行为模式为`queue`或`silent`时有效,如果在 **onBeforePushQueue** 事件中阻止了进入队列,则此函数不会触发。 + +### onFallback + +类似传统的 optimistic ui 的解决方案,我们也提供了请求回退事件,当请求达到最大重试次数或者重试判定失败时都将会触发这个时间,你可以使用它处理一些回退操作。 + +:::warning 警告 + +当绑定回退事件后,即使行为模式是`silent`的请求也不再被持久化,它将会在刷新页面后丢失,这是因为持久化的 silentMethod 通常都是需要确保完成的,而不是回退让用户重新处理的,在这种情况下不应该使用回退功能。 + +::: + +## 保存额外的操作数据 + +在创建或编辑数据项时,之前的章节仅保存了回显数据到`silentMethod.reviewData`中,如果有一些额外的数据需要记录,例如编辑页的菜单可选项等,我们同样需要记录它们以确保在断网情况下还可以选择到它们,此时将这些数据挂载到 silentMethod 实例上一起持久化。 + +一般而言,你可以以任意属性名保存持久化数据,但在 typescript 中会报错,因此为你指定了 `silentMethod.extraData` 属性作为额外数据的保存字段,记得通过 `silentMethod.save()` 持久化数据。 + +## 自定义序列化器 + +默认情况下,alova 是使用 localStorage 进行 silentMethod 的数据持久化的,因此在持久化时会调用`JSON.stringify`转换为字符串,但 json 数据只支持基本数据类型、纯对象以及数组,如果你希望序列化特殊的数据结构例如 Date 实例、RegExp 实例、函数以及自定义的类实例,alova 支持自定义的序列化器来处理它们,在存储时应该如何将它转换为 json 支持的数据结构,在取出时如何转换为原对象结构。 + +```javascript +const regExpSerializer = { + // forward在序列化时被调用 + // 需要判断data是否为目标值,如果不是目标值则返回undefined,表示不处理它 + forward: data => data instanceof RegExp ? data.source : undefined, + + // backward在反序列化时被调用,data为forward中返回的值 + backward: source => new RegExp(source); +}; + +bootSilentFactory({ + // ... + // 通过serializers使用这个序列化器 + serializers: { + customRegExp: regExpSerializer + } +}) +``` + +SilentFactory 内部默认提供了 Date 和 RegExp 的序列化器,你也可以使用相同的 key 来覆盖默认的序列化器 + +```javascript +const defaultSerializers = { + Date: dateSerializer, + RegExp: regExpSerializer +}; +``` + +[阅读 Date 序列化器源码](https://github.com/alovajs/scene/blob/main/src/hooks/silent/serializer/date.ts) + +## 操纵静默队列 + +静默队列用于保证请求时序问题,我们可以任意创建队列,进入队列的请求都将会以**SilentMethod**实例的形式保存在队列中,每个**SilentMethod**除了包含请求信息外,还包含静默提交的相关配置。静默队列是可以生成任意多个的,且支持查找、修改、删除队列中的 silentMethod 实例。 + +### 使用多个静默队列 + +行为模式设置为`queue`和`silent`的请求都会进入静默队列,默认情况下,silentMethod 实例将会被分配到**default**队列中,当需要分配到其他队列时可以在*useSQRequest*中指定`queue`参数。 + +```javascript +useSQRequest(createOrEditTodo, { + // ... + // 指定silentMethod实例进入名称为customQueue的队列中 + queue: 'customQueue', + behavior: 'silent' +}); +``` + +也可以将`queue`指定为函数,要求返回队列名称,这个函数将会在每次发起请求时被调用,函数参数来自于 send 函数。 + +```javascript +const { send } = useSQRequest(createOrEditTodo, { + // ... + // 根据useCustomQueue判断是否进入customQueue队列 + queue: useCustomQueue => (useCustomQueue ? 'customQueue' : 'default'), + behavior: 'silent', + immediate: false +}); +const handleClick = () => { + send(true); +}; +``` + +### 查找 silentMethod + +在之前的[数据补偿](/tutorial/strategy/sensorless-data-interaction/data-compensation)中,我们使用了 [filterSilentMethods](/tutorial/strategy/sensorless-data-interaction/data-compensation#filtersilentmethods) 查找指定队列的 silentMethod 实例,它将返回所有匹配的 silentMethod 实例,这里再介绍两种查找队列的方式: + +#### 查找个 silentMethod 实例 + +使用`getSilentMethod`查询匹配的第一个 silentMethod 实例,用法与 [filterSilentMethods](/tutorial/strategy/sensorless-data-interaction/data-compensation#filtersilentmethods) 相同。 + +```typescript +function filterSilentMethods( + methodNameMatcher?: string | number | RegExp, + queueName?: string, + filterActive?: boolean +): SilentMethod | undefined; +``` + +#### 自定义查找 + +通过导出的 `silentQueueMap` 队列集合自定义查找,`silentQueueMap` 的数据结构为: + +```javascript +const silentQueueMap = { + default: [silentMethod1, silentMethod2 /* ... */], + queueName1: [silentMethod3, silentMethod4 /* ... */], + queueName2: [silentMethod5, silentMethod6 /* ... */] + // ... +}; +``` + +### 更改队列中的 silentMethod + +当查找到你想要的 silentMethod 实例后,可以对这些等待中的 silentMethod 实例进行操纵。 + +#### 更新 silentMethod + +调用`silentMethod.save`更新此 silentMethod 数据到缓存中。 + +```javascript +silentMethod.extraData = { ... }; +await silentMethod.save(); +``` + +#### 替换 silentMethod + +调用`silentMethod.replace`可以在队列中将一个 silentMethod 替换为另一个 silentMethod。 + +```javascript +await oldSilentMethod.replace(newSilentMethod); +``` + +#### 移除 silentMethod + +调用`silentMethod.remove`可以将当前 silentMethod 在队列中移除。 + +```javascript +await oldSilentMethod.remove(); +``` + +#### 使用 silentQueueMap 更改 silentMethod + +你还可以访问 `silentQueueMap` 自定义更改任意队列的任意数据。 + +```javascript +import { silentQueueMap } from 'alova/client'; + +// 修改default队列中的所有silentMethod +silentQueueMap.default.forEach(silentMethodItem => { + // ... +}); +``` + +## 队列请求延迟 + +有些应用需要频繁地提交数据,例如编辑器类型应用,在编辑过程中进行实时保存,同时不打断用户使用,在这种类型的应用中使用静默提交时将会产生较多的请求信息,不仅会塞满前端缓存还会让服务端接收过多的请求,此时,我们可能不再需要将同步所有保存操作,而是在一段时间内发送一次操作,将有以下两种方案: + +1. 对编辑操作节流,在 n 秒内只发起一次提交,这种方案可能会丢失延迟时间段内的操作记录,导致刷新时只能获取最后一次提交时的状态; +2. 延迟队列内的保存请求,并在延迟时间内只保留最新一次的请求信息,这样就可以减少请求的同时保留最新的编辑状态; + +默认情况下,队列中的 silentMethod 会在上一个响应后立即发送请求,我们可以在启动时通过 `requestWait` 设置 silentMethod 的延迟发送时间,在这段时间里通过操纵队列保留最新的 silentMethod。 + +### 设置请求延迟 + +你可以对指定的队列设置延迟时间,也可以一次设置多个队列。 + +```javascript +bootSilentFactory({ + alova: alovaInst, + // highlight-start + requestWait: [ + // customQueue队列内的每个silentMethod将会延迟5000ms发起请求 + { queue: 'customQueue', wait: 5000 }, + + // 通过正则表达式对名称前缀为delay的队列统一设置3000ms延迟请求时间 + { queue: /^delay/, wait: 3000 } + ] + // highlight-end +}); +``` + +当 `requestWait` 直接设置为数字时默认对 default 队列有效。 + +```javascript +bootSilentFactory({ + alova: alovaInst, + // highlight-start + // default队列内的每个silentMethod将会延迟5000ms发起请求 + requestWait: 5000 + // highlight-end +}); +``` + +### 动态设置请求延迟 + +很多时候我们只希望队列内的特定 silentMethod 设置延迟请求,此时可以通过函数来动态设置请求延迟,指定队列的每个 silentMethod 在发起请求前都将会调用这个函数,来确定延迟的时间。 + +```javascript +bootSilentFactory({ + alova: alovaInst, + // highlight-start + requestWait: [ + { + queue: /^delay, + // 只对url为/edit的post请求延迟5000ms + wait: (silentMethod, queuName) => { + const { type, url, data } = silentMethod.entity; + if (type === 'POST' && url === '/edit') { + return 5000; + } + } + }, + ] + // highlight-end +}); +``` + +同样的,当 `requestWait` 直接设置为函数时默认对 default 队列有效。 + +```javascript +bootSilentFactory({ + alova: alovaInst, + // highlight-start + requestWait: (silentMethod, queuName) => { + const { type, url, data } = silentMethod.entity; + if (type === 'POST' && url === '/edit') { + return 5000; + } + } + // highlight-end +}); +``` + +## 动态设置 method 名称 + +为了便于查找,如果你需要动态设置 silentMethod 内 method 的名称,可以通过调用 setName。 + +```javascript +// 在请求成功时对silentMethod重新设置名称 +onSuccess(({ data, silentMethod }) => { + silentMethod.entity.setName('name' + data.id); +}); +``` + +## 全局的静默提交事件 + +在之前的章节中,我们接触了 `onSilentSubmitSuccess`,我们总共提供了 5 个全局的事件。 + +### onSilentSubmitBoot + +静默工厂启动事件,在静默工厂启动后触发。 + +```typescript +function onSilentSubmitBoot(handler: () => void): OffEventCallback; +``` + +### onBeforeSilentSubmit + +`behavior=silent`的 silentMethod 请求前触发。 + +```typescript +function onBeforeSilentSubmit(handler: (event: GlobalSQEvent)): OffEventCallback; +``` + +### onSilentSubmitSuccess + +`behavior=silent`的 silentMethod 请求成功时触发。 + +```typescript +function onSilentSubmitSuccess( + handler: (event: GlobalSQSuccessEvent) => void +): OffEventCallback; +``` + +### onSilentSubmitError + +`behavior=silent`的 silentMethod 请求失败,但还没有达到最大重试次数时触发。 + +```typescript +function onSilentSubmitError(handler: (event: GlobalSQErrorEvent) => void): OffEventCallback; +``` + +### onSilentSubmitFail + +`behavior=silent`的 silentMethod 在遇到请求失败时,以下 3 种情况将会触发此事件: + +1. 请求重试到达最大次数时触发; +2. 未设置重试次数时,首次请求失败将会触发; +3. 请求未到最大重试次数,但重试判定为不再重试时触发; + +```typescript +function onSilentSubmitFail(handler: (event: GlobalSQFailEvent) => void): OffEventCallback; +``` + +以上的事件绑定函数都将返回解绑函数,你可以在组件卸载前解绑事件。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/01-sensorless-data-interaction/README.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/README.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/01-sensorless-data-interaction/README.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/README.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/01-sensorless-data-interaction/_category_.json b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/_category_.json similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/01-sensorless-data-interaction/_category_.json rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/_category_.json diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-use-captcha.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-use-captcha.md new file mode 100644 index 000000000..8e7f06bf9 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-use-captcha.md @@ -0,0 +1,174 @@ +--- +title: 发送验证码 +sidebar_position: 90 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info 策略类型 + +use hook + +::: + +> 在使用扩展 hooks 前,确保你已熟悉了 alova 的基本使用。 + +验证码发送 hook,减掉你在开发验证码发送功能时的繁琐。 + +## 示例 + +[发送验证码 Demo](/tutorial/example/captcha-send) + +## 特性 + +- ✨ 验证码发送后自动开始倒计时; +- ✨ 自定义倒计时秒数; +- ✨ 验证码发送限制; + +## 使用 + +## 基本使用 + +展示表单 hook 的基本使用。 + + + + +```html + + + +``` + + + + +```jsx +import { useState } from 'react'; +import { apiSendCaptcha } from './api.js'; +import { useCaptcha } from 'alova/clientct'; + +const App = () => { + const [mobile, setMobile] = ref(''); + const { + // 发送状态 + loading: sending, + + // 调用sendCaptcha才会请求接口发送验证码 + send: sendCaptcha + } = useCaptcha(() => apiSendCaptcha(mobile)); + + return ( +
+ setMobile(target.value)} + /> + +
+ ); +}; +``` + +
+ + +```html + + + + +``` + + +
+ +默认情况下,验证码发送成功后将会倒计时 60 秒,当倒计时没有结束时再调用`send`将会抛出错误。 + +### 自定义倒计时秒数 + +你也可以自定义倒计时秒数 + +```javascript +useCaptcha(() => apiSendCaptcha(mobile.value), { + // ... + // highlight-start + // 将倒计时设为20秒 + initialCountdown: 20 + // highlight-end +}); +``` + +## API + +### Hook 配置 + +继承[**useRequest**](/api/core-hooks#userequest)除`immediate`外的所有配置,`useCaptcha`中`immediate`已硬编码为 false。 + +| 名称 | 描述 | 类型 | 默认值 | 版本 | +| ---------------- | ------------------------------------------------------ | ------ | ------ | ---- | +| initialCountdown | 初始倒计时,当验证码发送成功时将会以此数据来开始倒计时 | number | 60 | - | + +### 响应式数据 + +继承[**useRequest**](/api/core-hooks#userequest)所有响应式数据。 + +| 名称 | 描述 | 类型 | 版本 | +| --------- | ------------------------------------------------------ | ------ | ---- | +| countdown | 当前倒计时,每秒-1,当倒计时结束后才可以再次发送验证码 | number | - | + +### 操作函数 + +继承[**useRequest**](/api/core-hooks#userequest)所有操作函数。 + +| 名称 | 描述 | 函数参数 | 返回值 | 版本 | +| ---- | ---------------------------------------- | ----------------------- | ------------------- | ---- | +| send | 发送请求,当倒计时未结束时调用将抛出错误 | 与 useRequest.send 一致 | Promise\ | - | + +### 事件 + +继承[**useRequest**](/api/core-hooks#userequest)所有事件。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/10-use-serial-request.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/10-use-serial-request.md new file mode 100644 index 000000000..c9691fc0d --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/10-use-serial-request.md @@ -0,0 +1,107 @@ +--- +title: 串行请求的useRequest +sidebar_position: 100 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info 策略类型 + +use hook + +::: + +> 在使用扩展 hooks 前,确保你已熟悉了 alova 的基本使用。 + +这个 use hook 比[在最佳实践中的串行请求](/tutorial/best-practice/skills)更加简洁易用,统一的 loading 状态、error、回调函数。 + +## 特性 + +- ✨ 更加简洁易用的串行方式; +- ✨ 统一的请求状态和回调函数; +- ✨ send 函数可触发多个请求串行执行; + +## 示例 + +[串行请求](/tutorial/example/serial-request) + +## 使用 + +### 基本用法 + +和`useRequest`的用法一样,只是第一个参数改变成了一个串行执行的 handler 数组,每个 handler 将接收上一个请求的响应数据。 + +```javascript +import { useSerialRequest } from 'alova/client'; + +const { + // 串行加载状态,全部请求完成才会改为false + loading, + + // 最后一个请求的响应数据 + data, + + // 任意一个请求错误都将在这记录错误信息 + error, + + // 手动发送串行请求 + send, + + // 串行请求成功回调绑定函数 + onSuccess, + + // 串行请求错误回调绑定函数,任意一个请求错误都将触发它 + onError, + + // 串行请求完成回调绑定函数 + onComplete +} = useSerialRequest( + [ + // args为send函数传入的参数 + (...args) => request1(args), + + // 从第二个handler开始,第一个参数为上一个请求的响应数据,args从第二个开始接收 + (response1, ...args) => request2(response1, args), + (response2, ...args) => request3(response2, args) + ], + { + immediate: false + } +); + +// 手动触发请求并传参 +send(1, 2, 3); +``` + +值得注意的是,handler 数组中的第一项也可以指定为一个 method 实例,从第二项开始必须为函数。 + +```javascript +useSerialRequest([ + methodInstance, + (response1, ...args) => request2(response1, args), + (response2, ...args) => request3(response2, args) +]); +``` + +### 请求错误 + +串行请求任意一个请求错误时,将会触发`onError`,它的`event.method`将指向请求错误的 method 实例。 + +## API + +### Hook 配置 + +继承[**useRequest**](/api/core-hooks#userequest)所有配置。 + +### 响应式数据 + +继承[**useRequest**](/api/core-hooks#userequest)所有响应式数据。 + +### 操作函数 + +继承[**useRequest**](/api/core-hooks#userequest)所有操作函数。 + +### 事件 + +继承[**useRequest**](/api/core-hooks#userequest)所有事件。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/11-use-serial-watcher.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/11-use-serial-watcher.md new file mode 100644 index 000000000..e1020519a --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/11-use-serial-watcher.md @@ -0,0 +1,108 @@ +--- +title: 串行请求的useWatcher +sidebar_position: 110 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info 策略类型 + +use hook + +::: + +> 在使用扩展 hooks 前,确保你已熟悉了 alova 的基本使用。 + +状态更新触发一组串行请求,比[在最佳实践中的串行请求](/tutorial/best-practice/skills)更加简洁易用,统一的 loading 状态、error、回调函数。 + +## 特性 + +- ✨ 更加简洁易用的串行方式; +- ✨ 统一的请求状态和回调函数; +- ✨ 状态更新触发多个请求串行执行; + +## 示例 + +[串行请求](/tutorial/example/serial-request) + +## 使用 + +### 基本用法 + +和`useWatcher`的用法一样,只是第一个参数改变成了一个串行执行的 handler 数组,每个 handler 将接收上一个请求的响应数据。 + +```javascript +import { useSerialWatcher } from 'alova/client'; + +const { + // 串行加载状态,全部请求完成才会改为false + loading, + + // 最后一个请求的响应数据 + data, + + // 任意一个请求错误都将在这记录错误信息 + error, + + // 手动发送串行请求 + send, + + // 串行请求成功回调绑定函数 + onSuccess, + + // 串行请求错误回调绑定函数,任意一个请求错误都将触发它 + onError, + + // 串行请求完成回调绑定函数 + onComplete +} = useSerialWatcher( + [ + // args为send函数传入的参数 + (...args) => request1(args), + + // 从第二个handler开始,第一个参数为上一个请求的响应数据,args从第二个开始接收 + (response1, ...args) => request2(response1, args), + (response2, ...args) => request3(response2, args) + ], + [watchedState1, watchedState2], + { + immediate: true + } +); + +// 手动触发请求并传参 +send(1, 2, 3); +``` + +值得注意的是,handler 数组中的第一项也可以指定为一个 method 实例,从第二项开始必须为函数。 + +```javascript +useSerialRequest([ + methodInstance, + (response1, ...args) => request2(response1, args), + (response2, ...args) => request3(response2, args) +]); +``` + +### 请求错误 + +串行请求任意一个请求错误时,将会触发`onError`,它的`event.method`将指向请求错误的 method 实例。 + +## API + +### Hook 配置 + +继承[**useWatcher**](/api/core-hooks#usewatcher)所有配置。 + +### 响应式数据 + +继承[**useWatcher**](/api/core-hooks#usewatcher)所有响应式数据。 + +### 操作函数 + +继承[**useWatcher**](/api/core-hooks#usewatcher)所有操作函数。 + +### 事件 + +继承[**useWatcher**](/api/core-hooks#usewatcher)所有事件。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/12-use-retriable-request.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/12-use-retriable-request.md new file mode 100644 index 000000000..c4b468d6c --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/12-use-retriable-request.md @@ -0,0 +1,232 @@ +--- +title: 请求重试策略 +sidebar_position: 120 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info 策略类型 + +use hook + +::: + +> 在使用扩展 hooks 前,确保你已熟悉了 alova 的基本使用。 + +请求失败可自动重试的 use hook,你可以将它用于重要的请求上。 + +## 示例 + +[请求重试 Demo](/tutorial/example/retriable-hook) + +## 特性 + +- ✨ 自定义重试次数或按条件判断是否需要重试; +- ✨ 重试延迟机制; +- ✨ 手动停止重试; + +## 使用 + +### 基本用法 + +```javascript +import { useRetriableRequest } from 'alova/client'; + +const { + // 加载状态,在重试期间一直为true,直到重试成功或失败 + loading, + + // 响应数据 + data, + + // 请求错误信息,每次请求或重试失败都将会有error实例 + // 上一次的error实例将被覆盖 + error, + + // 每次请求或重试失败都将触发onError事件 + onError, + + // 请求重试事件,在每次重试请求发出后立即触发 + onRetry, + + // 请求重试失败事件 + // 达到最大重试次数仍未请求成功,或手动停止重试都将触发 + onFail, + + // 请求或重试成功事件 + onSuccess, + + // 每次请求或重试,无论成功或失败都将触发完成事件 + onComplete +} = useRetriableRequest(request); +``` + +`useRetriableRequest`的最大请求重试次数默认为 3,且每次将延迟 1 秒重试。同时也会默认发出请求,可以通过设置`immediate`为 false 改变行为。 + +### 设置静态的最大重试次数 + +最大重试次数表示首次请求失败后,最多重试请求的次数,期间如果请求成功的话将会停止继续重试。默认最大重试次数为 3 次,你可以通过以下方式自定义设置。 + +请求重试达到最大次数仍然未成功时,将会触发`onFail`事件并停止请求重试,如果你在失败后希望继续重试,可以调用`send`函数,此时它将进行新一轮的请求和重试。 + +```javascript +const { send } = useRetriableRequest(request, { + // ... + // highlight-start + // 设置最大重试次数为5 + retry: 5 + // highlight-end +}); +``` + +### 动态设置最大重试次数 + +可能有时候你希望通过某个条件来判断是否需要继续重试,此时你可以将`retry`设置为返回 boolean 值的函数,来动态判断是否继续重试。 + +```javascript +useRetriableRequest(request, { + // ... + // highlight-start + // 第一个参数为上一次的错误实例,从第二个参数开始为send传入的参数 + retry(error, ...args) { + // 请求超时则继续重试 + return /network timeout/i.test(error.message); + } + // highlight-end +}); +``` + +### 设置延迟时间 + +默认重试延迟时间为 1 秒,你可以通过以下方式自定义设置。 + +```javascript +useRetriableRequest(request, { + // ... + backoff: { + // highlight-start + // 设置延迟时间为2秒 + delay: 2000 + // highlight-end + } +}); +``` + +### 设置不固定的重试延迟时间 + +有时候你希望每次请求延迟时间都不是固定的,你可以按以下方式设置延迟增长倍数,延迟时间将按重试次数指数增长。 + +```javascript +useRetriableRequest(request, { + // ... + backoff: { + delay: 2000, + // highlight-start + // multiplier设置为2时,第一次重试延迟为2秒,第二次为4秒,第三次为8秒,以此类推。 + multiplier: 2 + // highlight-end + } +}); +``` + +还不够?你甚至还可以为每次延迟时间增加随机抖动值,让它看着不那么规律。 + +```javascript +useRetriableRequest(request, { + // ... + backoff: { + delay: 2000, + multiplier: 2, + // highlight-start + /** + * 延迟请求的抖动起始百分比值,范围为0-1 + * 当只设置了startQuiver时,endQuiver默认为1 + * 例如设置为0.5,它将在当前延迟时间上增加50%到100%的随机时间 + * 如果endQuiver有值,则延迟时间将增加startQuiver和endQuiver范围的随机值 + */ + startQuiver: 0.5, + + /** + * 延迟请求的抖动结束百分比值,范围为0-1 + * 当只设置了endQuiver时,startQuiver默认为0 + * 例如设置为0.8,它将在当前延迟时间上增加0%到80%的随机时间 + * 如果startQuiver有值,则延迟时间将增加startQuiver和endQuiver范围的随机值 + */ + endQuiver: 0.8; + // highlight-end + } +}); +``` + +### 手动停止重试 + +因某些情况需要手动停止重试,无论当前正在请求中,还是在等待下一次重试,你都可以使用`stop`来停止它。 + +```javascript +const { stop } = useRetriableRequest(request, { + // ... +}); + +const handleStop = () => { + stop(); +}; +``` + +## API + +### Hook 配置 + +继承[**useRequest**](/api/core-hooks#userequest)所有配置。 + +| 名称 | 描述 | 类型 | 默认值 | 版本 | +| ------- | ------------------------------------------------------------------------- | ------------------------------- | ---------------------------------------------- | ---- | +| retry | 最大重试次数,也可以设置为返回 boolean 值的函数,来动态判断是否继续重试。 | number | (error: Error, ...args: any[]) => boolean \| 3 | - | +| backoff | 避让策略,设置重试延迟时间等 | [BackoffPolicy](#backoffpolicy) | - | - | + +#### BackoffPolicy + +| 名称 | 描述 | 类型 | 默认值 | 版本 | +| ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | ------ | ---- | +| delay | 再次请求的延迟时间,单位毫秒 | number | 1000 | - | +| multiplier | 指定延迟倍数,例如把 multiplier 设置为 2、delay 为 1 秒时,第一次重试为 1 秒,第二次为 2 秒,第三次为 4 秒,以此类推 | number | 0 | - | +| startQuiver | 延迟请求的抖动起始百分比值,范围为 0-1,当只设置了 startQuiver 时,endQuiver 默认为 1,例如设置为 0.5,它将在当前延迟时间上增加 50%到 100%的随机时间,如果 endQuiver 有值,则延迟时间将增加 startQuiver 和 endQuiver 范围的随机值 | number | 0 | - | +| endQuiver | 延迟请求的抖动结束百分比值,范围为 0-1,当只设置了 endQuiver 时,startQuiver 默认为 0,例如设置为 0.5,它将在当前延迟时间上增加 0%到 50%的随机时间,如果 startQuiver 有值,则延迟时间将增加 startQuiver 和 endQuiver 范围的随机值 | number | 0 | - | + +### 响应式数据 + +继承[**useRequest**](/api/core-hooks#userequest)所有响应式数据。 + +### 操作函数 + +继承[**useRequest**](/api/core-hooks#userequest)所有操作函数。 + +| 名称 | 描述 | 函数参数 | 返回值 | 版本 | +| ---- | ------------------------------------------------------------ | -------- | ------ | ---- | +| stop | 停止重试,只在重试期间调用有效,停止后将立即触发 onFail 事件 | - | - | - | + +### 事件 + +继承[**useRequest**](/api/core-hooks#userequest)所有事件。 + +| 名称 | 描述 | 回调参数 | 版本 | +| ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | ---- | +| onRetry | 重试事件绑定,它们将在重试发起后触发 | 重试事件实例 [RetriableRetryEvent](#retriableretryevent) | - | +| onFail | 请求失败时触发,将在不再重试时触发,例如到达最大重试次数时,重试回调返回 false 时,手动调用 stop 停止重试时
注意:
1. onError 事件是在每次请求报错时都将被触发
2. 如果没有重试次数时,onError、onComplete 和 onFail 会被同时触发 | 重试事件实例 [RetriableFailEvent](#retriablefailevent) | - | + +#### RetriableRetryEvent + +继承于 alova 的 Event 事件实例。 + +| 名称 | 描述 | 类型 | 默认值 | 版本 | +| ---------- | ----------------------------- | ------ | -------- | ---- | +| retryTimes | 当前的重试次数 | number | required | - | +| retryDelay | 本次重试的延迟时间,单位为 ms | number | required | - | + +#### RetriableFailEvent + +继承于 alova 的 Event 事件实例。 + +| 名称 | 描述 | 类型 | 默认值 | 版本 | +| ---------- | -------------- | ------ | -------- | ---- | +| retryTimes | 当前的重试次数 | number | required | - | diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/13-use-sse.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/13-use-sse.md new file mode 100644 index 000000000..3d780360f --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/13-use-sse.md @@ -0,0 +1,248 @@ +--- +title: Server-sent events发送请求 +sidebar_position: 130 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info 策略类型 + +use hook + +::: + +> 在使用扩展 hooks 前,确保你已熟悉了 alova 的基本使用。 + +通过 Server-sent Events(SSE)请求,内部使用`EventSource`实现。 + +## 特性 + +- ✨ 更加简洁易用的使用方式; +- ✨ 自动管理连接; + +## 用法 + + + + +```typescript +import { useSSE } from 'alova/client'; + +const method = (value: string) => alova.Get('/api/source', { param: { key: value } }); +const { data, eventSource, readyState, onMessage, onError, on, send, close } = useSSE(method, { + initialData: 'initial-data' // 初始时 data 中的数据 +}); + +// connect +send('value'); + +console.log(data.value); // data 在接收到事件后更新,默认是 initialData + +// 对应 eventsource 的 message 事件 +const unbindMessage = onMessage(({ data }) => { + console.log(data); +}); + +const unbindError = onError(({ error }) => { + console.error('sse error', error); + close(); +}); + +// 在需要的时候解绑 +unbindMessage(); +unbindError(); +``` + + + + +```typescript +import { useSSE } from 'alova/client'; + +const method = (value: string) => alova.Get('/api/source', { param: { key: value } }); +const { data, eventSource, readyState, onMessage, onError, on, send, close } = useSSE(method, { + initialData: 'initial-data' // 初始时 data 中的数据 +}); + +// connect +send('value'); + +console.log(data); // data 在接收到事件后更新,默认是 initialData + +// 对应 eventsource 的 message 事件 +const unbindMessage = onMessage(({ data }) => { + console.log(data); +}); + +const unbindError = onError(({ error }) => { + console.error('sse error', error); + close(); +}); + +// 在需要的时候解绑 +unbindMessage(); +unbindError(); +``` + + + + +```typescript +import { useSSE } from 'alova/client'; + +const method = (value: string) => alova.Get('/api/source', { param: { key: value } }); +const { data, eventSource, readyState, onMessage, onError, on, send, close } = useSSE(method, { + initialData: 'initial-data' // 初始时 data 中的数据 +}); + +// connect +send('value'); + +console.log(data); // data 在接收到事件后更新,默认是 initialData + +// 对应 eventsource 的 message 事件 +const unbindMessage = onMessage(({ data }) => { + console.log(data); +}); + +const unbindError = onError(({ error }) => { + console.error('sse error', error); + close(); +}); + +// 在需要的时候解绑 +unbindMessage(); +unbindError(); +``` + + + + +:::warning + +`useSSE` 目前只能连接到一个源。也就是说,当试图连接多个目标时,上一个连接总会被断开。 + +::: + +```typescript +const { data, eventSource, readyState, onMessage, onError, on, send, close } = useSSE(method); + +send('value1'); +// highlight-start +send('value2'); // 这会断开上一个连接 +send('value3'); // 这也会断开上一个连接 +// highlight-end +``` + +默认情况下,不会发送请求。当然,通过设置`immediate = true`,可以省去手动 send 的一步。 + +```typescript +const { data, eventSource, readyState, onMessage, onError, on, send, close } = useSSE(method, { + // highlight-start + immediate: true + // highlight-end +}); + +// codes here... +``` + +### 绑定自定义事件 + +```typescript +const { data, readyState, onMessage, on } = useSSE(method); + +on('event-name', ({ data }) => { + console.log(data); +}); +``` + +### 全局响应拦截 + +默认情况下,响应数据受到[全局响应拦截器的捕获](/tutorial/combine-framework/response)。如果这不是你预期的行为,可以手动关闭。 + +```typescript +const { data, readyState, onMessage, on } = useSSE(method, { + // highlight-start + interceptByGlobalResponded: false // 现在数据不会被响应拦截 + // highlight-end +}); +``` + +## 类型声明 + +```typescript +const enum SSEHookReadyState { + CONNECTING = 0, + OPEN = 1, + CLOSED = 2 +}; + +type SSEHookConfig = { + /** + * 会传给new EventSource + */ + withCredentials?: boolean; + + /** + * 是否经过alova实例的responded拦截 + * @default true + */ + interceptByGlobalResponded?: boolean; + + /** + * 初始数据 + */ + initialData?: any; + + /** + * 是否立即发起请求 + * @default false + */ + immediate?: boolean; +}; + +type SSEReturnType = { + readyState: ExportedType; + data: ExportedType; + eventSource: ExportedType; + /** + * 手动发起请求。在使用 `immediate: true` 时该方法会自动触发 + * @param sendArgs 请求参数,会传递给 method + */ + send: (...sendArgs: any[]) => Promise; + /** + * 关闭连接 + */ + close: () => void; + /** + * 注册 EventSource open 的回调函数 + * @param callback 回调函数 + * @returns 取消注册函数 + */ + onOpen(callback: SSEOnOpenTrigger): () => void; + + /** + * 注册 EventSource message 的回调函数 + * @param callback 回调函数 + * @returns 取消注册函数 + */ + onMessage(callback: SSEOnMessageTrigger): () => void; + + /** + * 注册 EventSource error 的回调函数 + * @param callback 回调函数 + * @returns 取消注册函数 + */ + onError(callback: SSEOnErrorTrigger): () => void; + + /** + * @param eventName 事件名称,默认存在 `open` | `error` | `message` + * @param handler 事件处理器 + */ + on( + eventName: string, + handler: (event: AlovaSSEMessageEvent) => void + ) => () => void; +}; +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/14-use-breakpoint-uploader.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/14-use-breakpoint-uploader.md new file mode 100644 index 000000000..6b3821453 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/14-use-breakpoint-uploader.md @@ -0,0 +1,6 @@ +--- +title: 断点续传策略 +sidebar_position: 140 +--- + +敬请期待... diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/15-use-uploader.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/15-use-uploader.md new file mode 100644 index 000000000..99b66586e --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/15-use-uploader.md @@ -0,0 +1,177 @@ +--- +title: 通用的上传策略 +sidebar_position: 150 +--- + +:::warning + +此策略暂未实现,以下为设计文档 + +::: + +> 在使用扩展 hooks 前,确保你已熟悉了 alova 的基本使用。 + +简便的文件上传,传入 base64、file 对象、blob 对象、arraybuffer 对象、Canvas 都可以直接上传,内部实现自动转换,使用场景有: + +1. 上传组件的样式不符合时 +2. 上传数据格式多样时 + +## 功能需求 + +1. 支持单个、多个文件上传,多个文件同时上传时可以同时上传 +2. 多个文件上传进度、总进度 +3. 自动转换数据格式为 File +4. 失败可手动触发重新上传 +5. 限制文件格式和大小 +6. 文件数据回显 +7. 自动的缩略图(适用于图片) +8. 文件上传总进度 + +## useUploader 的全部参数一览 + +```javascript +const { + fileList, // 文件数据列表,每项包含文件名、文件路径、上传状态、上传进度,具体格式见下面 + file, // 第一个文件数据项,可以使用计算属性,一般用于上传单个文件使用 + loading, // 是否正在上传,boolean 值 + progress, // 上传总进度,具体格式见下面 + appendFiles, // 添加文件数据项(添加后自动转换为 File 对象) + upload, // 上传操作,传入下标指定哪个项上传(用于错误重试),未传入上传全部未成功的项 + onFilesAppend, // 添加文件事件 + onExceed, // 文件数量被限制事件 + onFormatMismatch, // 文件格式不匹配事件 + onSuccess, // 每个文件上传成功事件 + onError, // 每个文件上传失败事件 + onComplete // 每个文件上传完成事件 +} = useUploader(({ file, name }) => uploadFile(file, name), { + limit: 3, // 限制上传文件 + accept: ['png', 'jpg', 'pdf'], // 接受的自动转换后的文件格式 + imageTempLink: true, // 是否生成临时图片路径,用于在图片上传完成前也可以展示图片内容,默认为 false + + // 设置一个函数,当上传成功时,可以自动将 fileList 项中的 src 字段替换为服务器上的文件地址,在上传图片时常用 + // data 为响应数据,此函数返回服务端的文件地址 + // 如果未指定此函数则不会有替换行为 + replaceSrcFromResponse: data => data.imgPath +}); +``` + +## useUploader 参数的详细格式及解释 + +参数格式使用 typescript 表示,便于理解 + +### 返回的响应式状态 + +以下都是响应式的值 + +```typescript +// file值的格式 +interface file { + src?: string; // 临时路径或上传成功后的文件路径,非图片 + file: File; // File 对象 + status: 0 | 1 | 2 | 3; // 0 表示未上传,1 表示上传中,2 表示已完成, 3 表示上传错误 + error?: Error; // 错误对象,当上传错误时需要将错误对象赋值上去 + progress: { + loaded: number; // 已上传大小 + total: number; // 文件总大小 + }; +} +// fileList值的格式 +type fileList = file[]; + +// loading值的格式 +type loading = boolean; // 是否正在上传 + +// 上传总进度信息 +interface progress { + loaded: number; // 已上传的大小,多个文件叠加 + total: number; // 文件总大小,多个文件叠加 + count: number; // 正在上传的文件总数 + successCount: number; // 已成功上传的文件数 + failCount: number; // 上传失败的文件数 +} +``` + +### 返回的操作函数 + +```typescript +interface RawFile { + file?: File | string | Blob | ArrayBuffer | HTMLCanvasElement; // 可以传入 base64、file 对象、blob 对象、arraybuffer,canvas 元素,回显时可不传 + src?: string; // 文件预览地址,如果传了即使 imageTempLink 为 true 也不会覆盖它 + name?: string; // 文件名,file 为非 File 对象时必填,在 new File 时用到 + mimeType?: string; // 内部转换为 File 对象时的文件类型,在 new File 时用到,建议 file 为非 File 对象时传入 + status?: 0 | 1 | 2 | 3; // 0 表示未上传,1 表示上传中,2 表示已完成,3 表示上传失败,不传默认为 0 +} + +/** + * 追加上传的文件进去,追加后会自动将列表的 file 项转换为 File 对象,此时 name 必须有值 + */ +type appendFiles = ( + files: RawFile | RawFile[], // 单个文件时传对象、多个文件时传数组 + start?: number // 从 fileList 哪个位置开始追加 +) => number; // 追加成功的个数,可能因为数量限制、格式限制而追加失败 + +/** + * 执行上传操作 + * 如果未传入参数则会自动将 fileList 内所有 file 属性有值的、status 为 0 和 3(未上传和上传失败)的重新发起上传请求 + * 如果传入了参数下标,则将 fileList 对应下标的项重新组成一个数组,也通过上面的条件过滤出可上传的进行上传请求,但此时如果有不符合条件的需要报错,而不是忽略 + * + * 发起上传操作后: + * 多个文件的上传将会并行发起上传请求,也就是将符合上传条件的文件数组遍历,依次调用 useUploader 的第一个回调函数获取 Method 对象发送请求,内部可以使用 useRequst 实现,因为 fileList 中有很多属性是在 useRequest 都可以提供 + */ +type upload = (...indexes: number[]) => void; +``` + +### 事件绑定函数 + +每个事件绑定的回调函数都会接收一个事件对象,格式如下 + +```typescript +// 触发时机:调用appendFiles添加文件时触发 +type onFilesAppend = (handler: (event: AlovaFileEvent) => void) => void; +interface AlovaFileEvent { + files: file[]; // 符合条件的,并且转换过后的文件数组 + rawFiles: RawFile[]; // 在 appendFiles 中传入的符合条件的数据数组,是还未转换过后的文件数组 + allRawFiles: RawFile[]; // 在 appendFiles 中传入的原数据数组 +} + +// 触发时机:当调用appendFiles时,文件数量被限制时触发 +type onExceed = (handler: (event: AlovaFileExceededEvent) => void) => void; +interface AlovaFileExceededEvent extends AlovaFileEvent { + exceeded: number; // 超过的数量 + limit: number; // 总限制数量 +} + +// 触发时机:当调用appendFiles时,有文件格式不匹配时触发 +type onFormatMismatch = (handler: (event: AlovaFileMismatchEvent) => void) => void; +interface AlovaFileMismatchEvent extends AlovaFileEvent { + mismatchFiles: RawFile[]; // 格式不匹配的文件数组 +} + +// 触发时机:每个文件上传成功时触发,内部可复用useRequest的onSuccess事件 +type onSuccess = (handler: (event: AlovaFileSuccessEvent) => void) => void; +interface AlovaFileSuccessEvent extends AlovaSuccessEvent { + file: file; // 上传成功的文件项 +} + +// 触发时机:每个文件上传失败时触发,内部可复用useRequest的onError事件 +type onError = (handler: (event: AlovaFileErrorEvent) => void) => void; +interface AlovaFileErrorEvent extends AlovaErrorEvent { + file: file; // 上传失败的文件项 +} + +// 触发时机:每个文件上传完成时触发,内部可复用useRequest的onComplete事件 +type onComplete = (handler: (event: AlovaFileCompleteEvent) => void) => void; +interface AlovaFileCompleteEvent extends AlovaCompleteEvent { + file: file; // 上传成功的文件项 +} +``` + +## 开发指南 + +开发前请仔细阅读[开发指南](/contributing/developing-guidelines) + +开发此 use hook 需要开发以下内容: + +1. 在 src/hooks 下编写 useUploader 功能代码 +2. useUploader 功能的完整单元测试,建议在 vue 和 react 下测试 +3. useUploader 的 typescript 类型声明,需要分别在`packages/scene-react/typings/index.d.ts`、`packages/scene-vue/typings/index.d.ts`、`packages/scene-svelte/typings/index.d.ts`下添加。公共的类型声明可以放在`typings/general.d.ts`中,打包时会将此文件分别复制到子包的`typings`文件夹下,也可以手动运行`npm run cp:files`复制文件夹。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/16-rate-limit-middleware.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/16-rate-limit-middleware.md new file mode 100644 index 000000000..637055c7a --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/16-rate-limit-middleware.md @@ -0,0 +1,8 @@ +--- +title: 请求速率限制 +sidebar_position: 160 +--- + +设置每个间隔应立即执行的请求数,其他请求将自动延迟。 + +敬请期待... diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-combine-framework/10-typescript.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/20-typescript.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-combine-framework/10-typescript.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/20-typescript.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/README.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/README.md new file mode 100644 index 000000000..0cce3bc14 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/README.md @@ -0,0 +1,17 @@ +--- +title: 客户端策略 +--- + +import DocCardList from '@theme/DocCardList'; + +像使用组件库一样,当你需要特定的请求策略的时候再学习它就可以了! + +所有的客户端 use hooks 都有以下共同点: + +1. 它们都依赖 statesHook,请在使用前[设置 statesHook](/tutorial/getting-started/combine-framework)。 +2. 它们的返回值中都包含`update`函数,用于主动更新导出的状态值。 +3. 在 react 下为了性能表现,所有的操作函数例如`send`、`update`、`abort`等都使用了`useCallback`包装过。 + +## 目录 + + diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/_category_.json b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/_category_.json new file mode 100644 index 000000000..d9b8c4b4a --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/_category_.json @@ -0,0 +1,3 @@ +{ + "label": "客户端策略" +} diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/01-retry.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/01-retry.md new file mode 100644 index 000000000..3961178f6 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/01-retry.md @@ -0,0 +1,128 @@ +--- +title: 请求重试策略 +sidebar_position: 10 +--- + +:::info 类型 + +server hook + +::: + +请求重试策略,你可以将它用于重要的请求上。 + +## 特性 + +- ✨ 自定义重试次数或按条件判断是否需要重试; +- ✨ 重试延迟机制; + +## 使用 + +### 基本用法 + +```javascript +const { retry } = require('alova/server'); +const { alovaInstance } = require('./api'); + +const request = alovaInstance.Get('/api/user'); +const hookedMethod = retry(request); +``` + +`retry`的最大请求重试次数默认为 3,且每次将延迟 1 秒重试。 + +### 设置静态的最大重试次数 + +最大重试次数表示首次请求失败后,最多重试请求的次数,期间如果请求成功的话将会停止继续重试。默认最大重试次数为 3 次,你可以通过以下方式自定义设置。 + +```javascript +const hookedMethod = retry(request, { + // ... + // highlight-start + // 设置最大重试次数为5 + retry: 5 + // highlight-end +}); +``` + +### 动态设置最大重试次数 + +可能有时候你希望通过某个条件来判断是否需要继续重试,此时你可以将`retry`设置为返回 boolean 值的函数,来动态判断是否继续重试。 + +```javascript +const hookedMethod = retry(request, { + // ... + // highlight-start + // 第一个参数为上一次的错误实例,从第二个参数开始为send传入的参数 + retry(error) { + // 请求超时则继续重试 + return /network timeout/i.test(error.message); + } + // highlight-end +}); +``` + +### 设置延迟时间 + +默认重试延迟时间为 1 秒,你可以通过以下方式自定义设置。 + +```javascript +retry(request, { + // ... + backoff: { + // highlight-start + // 设置延迟时间为2秒 + delay: 2000 + // highlight-end + } +}); +``` + +### 设置不固定的重试延迟时间 + +有时候你希望每次请求延迟时间都不是固定的,你可以按以下方式设置延迟增长倍数,延迟时间将按重试次数指数增长。 + +```javascript +retry(request, { + // ... + backoff: { + delay: 2000, + // highlight-start + // multiplier设置为2时,第一次重试延迟为2秒,第二次为4秒,第三次为8秒,以此类推。 + multiplier: 2 + // highlight-end + } +}); +``` + +还不够?你甚至还可以为每次延迟时间增加随机抖动值,让它看着不那么规律。 + +```javascript +retry(request, { + // ... + backoff: { + delay: 2000, + multiplier: 2, + // highlight-start + /** + * 延迟请求的抖动起始百分比值,范围为0-1 + * 当只设置了startQuiver时,endQuiver默认为1 + * 例如设置为0.5,它将在当前延迟时间上增加50%到100%的随机时间 + * 如果endQuiver有值,则延迟时间将增加startQuiver和endQuiver范围的随机值 + */ + startQuiver: 0.5, + + /** + * 延迟请求的抖动结束百分比值,范围为0-1 + * 当只设置了endQuiver时,startQuiver默认为0 + * 例如设置为0.8,它将在当前延迟时间上增加0%到80%的随机时间 + * 如果startQuiver有值,则延迟时间将增加startQuiver和endQuiver范围的随机值 + */ + endQuiver: 0.8; + // highlight-end + } +}); +``` + +## API + +请查看[API-retry](/api/core-hooks#usewatcher)。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/02-send-captcha.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/02-send-captcha.md new file mode 100644 index 000000000..f5a13670a --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/02-send-captcha.md @@ -0,0 +1,8 @@ +--- +title: 发送验证码 +sidebar_position: 20 +--- + +通过 sms 或 email 发送验证码,并根据 key 记录倒计时时间,在倒计时内再次发送验证码将报错。 + +敬请期待... diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/03-rate-limit-middleware.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/03-rate-limit-middleware.md new file mode 100644 index 000000000..2535e85eb --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/03-rate-limit-middleware.md @@ -0,0 +1,8 @@ +--- +title: 请求速率限制 +sidebar_position: 30 +--- + +设置每个间隔应立即执行的请求数,其他请求将自动延迟。 + +敬请期待... diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/README.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/README.md new file mode 100644 index 000000000..4ec50f5ad --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/README.md @@ -0,0 +1,24 @@ +--- +title: 服务端策略 +--- + +import DocCardList from '@theme/DocCardList'; + +像使用组件库一样,当你需要特定的请求策略的时候学习就可以了! + +以下是 server hooks 的规范,它接收 method 实例并返回一个新的 method 实例,因此可以很方便的组合多个 server hooks。 + +```ts +/** + * 服务器钩子模型,表示所有服务器钩子的类型。 + * 传递一个方法或被钩住的方法实例,设置选项,返回一个被钩住的方法实例。 + * 可以继续修饰这个方法,达到多个服务器钩子组合的效果。 + */ +export interface AlovaServerHook> { + (method: Method, options: Options): Method; +} +``` + +## 目录 + + diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/_category_.json b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/_category_.json new file mode 100644 index 000000000..aeaecc6d6 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/_category_.json @@ -0,0 +1,3 @@ +{ + "label": "服务端策略" +} diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/01-manage-apis.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-best-practice/01-manage-apis.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/01-manage-apis.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-best-practice/01-manage-apis.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/02-skills.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-best-practice/02-skills.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/02-skills.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-best-practice/02-skills.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/03-manage-cache-by-indexeddb.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-best-practice/03-manage-cache-by-indexeddb.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/03-manage-cache-by-indexeddb.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-best-practice/03-manage-cache-by-indexeddb.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/04-multiple-servers.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-best-practice/04-multiple-servers.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/04-multiple-servers.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-best-practice/04-multiple-servers.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/05-middleware.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-best-practice/05-middleware.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/05-middleware.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-best-practice/05-middleware.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/12-parallel-request.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-best-practice/12-parallel-request.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/12-parallel-request.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-best-practice/12-parallel-request.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/13-serial-request.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-best-practice/13-serial-request.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/13-serial-request.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-best-practice/13-serial-request.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-cache/01-mode.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/01-mode.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-cache/01-mode.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/01-mode.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-cache/02-auto-invalidate.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/02-auto-invalidate.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-cache/02-auto-invalidate.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/02-auto-invalidate.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-cache/03-manually-invalidate.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/03-manually-invalidate.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-cache/03-manually-invalidate.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/03-manually-invalidate.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-cache/04-force-request.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/04-force-request.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-cache/04-force-request.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/04-force-request.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-cache/05-set-and-query.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/05-set-and-query.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-cache/05-set-and-query.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/05-set-and-query.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-cache/06-controlled-cache.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/06-controlled-cache.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-cache/06-controlled-cache.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/06-controlled-cache.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/07-server-cache.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/07-server-cache.md new file mode 100644 index 000000000..2d809326f --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/07-server-cache.md @@ -0,0 +1,8 @@ +--- +title: 服务端缓存 +sidebar_position: 70 +--- + +## alova id 设置 + +可以设置 alova id,用于固定请求,当没有设置时将按 alova 创建顺序递增。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-cache/README.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/README.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-cache/README.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/README.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-cache/_category_.json b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/_category_.json similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-cache/_category_.json rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/_category_.json diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/01-alova.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/01-alova.md new file mode 100644 index 000000000..8e8a04786 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/01-alova.md @@ -0,0 +1,314 @@ +--- +title: alova实例 +sidebar_position: 10 +--- + +## createAlova() + +创建一个 alova 实例。 + +- **类型** + +```ts +function createAlova(options?: AlovaOptions): Alova; +``` + +- **参数** + +1. config: 配置参数 + +| 参数名 | 类型 | 说明 | +| -------------- | --------------------------- | ----------------------------------------------------------------------------------------- | +| baseURL | string | 基础路径,默认为空,[查看详情](/tutorial/getting-started/alova) | +| statesHook | object | 状态管理钩子,选填,[查看详情](/tutorial/combine-framework) | +| requestAdapter | object | 请求适配器,必填,[查看详情](/tutorial/custom/custom-http-adapter) | +| timeout | number | 超时时间,默认不超时,[查看详情](/tutorial/getting-started/alova) | +| localCache | object | 本地缓存配置,默认 GET 有 5000ms 缓存,[查看详情](/tutorial/cache/mode) | +| storageAdapter | object | 本地存储适配器,默认为`localStorage`,[查看详情](/tutorial/custom/custom-storage-adapter) | +| beforeRequest | function | 请求前钩子,[查看详情](/tutorial/getting-started/global-interceptor) | +| responded | object \| function | 请求响应钩子,[查看详情](/tutorial/getting-started/global-interceptor) | +| shareRequest | boolean | 共享请求,[查看详情](/tutorial/getting-started/alova) | +| errorLogger | boolean\| null \| function | 错误日志,[查看详情](/tutorial/advanced/error-logger) | +| cacheLogger | boolean \| null \| function | 缓存日志,[查看详情](/tutorial/advanced/cache-logger) | + +- **返回** + +Alova 实例 + +- **示例** + +```ts +import { createAlova } from 'alova'; + +const alova = createAlova({ + baseURL: 'https://example.com', + statesHook: VueHook, + requestAdapter: GlobalFetch(), + timeout: 3000 + // ... +}); +``` + +## alova.id + +alova 实例 id,用于区分不同的 alova 实例,可在[method 匹配器](/tutorial/advanced/method-matcher)中精准匹配指定 alova 的 method 实例。 + +- **类型**:string + +## alova.options + +通过`createAlova`创建 alova 实例时,默认配置与传入的配置对象合并后的对象。 + +- **类型** + +```ts +interface AlovaOptions { + statesHook: StatesHook; + requestAdapter: AlovaRequestAdapter; + baseURL?: string; + timeout?: number; + localCache?: GlobalLocalCacheConfig; + storageAdapter?: AlovaStorageAdapter; + beforeRequest?: Function; + responded?: Function | ResponsedHandlerRecord; + shareRequest?: boolean; + errorLogger?: boolean | null | Function; + cacheLogger?: boolean | null | Function; +} +``` + +## alova.storage + +alova 实例对应的存储适配器实例,默认为`AlovaGlobalStorage`,它使用的是`localStorage`。 + +- **类型** + +```ts +interface AlovaStorageAdapter { + get(key: string): any; + set(key: string, value: any): void; + remove(key: string): void; +} +``` + +## alova.Get() + +创建 GET 请求的 method 实例。 + +- **类型** + +```ts +interface Alova { + Get(url: string, config?: AlovaMethodCreateConfig): Method; +} +``` + +- **参数** + +1. url: 请求地址 +2. config: 配置参数 + +| 参数名 | 类型 | 说明 | +| -------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| headers | object | 请求头,[查看详情](/tutorial/getting-started/method) | +| params | object | 请求参数,[查看详情](/tutorial/getting-started/method) | +| name | string | method 对象名称,在 [updateState](/tutorial/advanced/update-across-components)、[invalidateCache](/tutorial/cache/manually-invalidate)、[setCache](/tutorial/cache/set-and-query)、以及 [fetch 函数](/tutorial/advanced/use-fetcher)中可以通过名称或通配符获取对应 method 实例 | +| timeout | number | 请求超时时间,[查看详情](/tutorial/getting-started/method) | +| localCache | LocalCacheConfig | 响应缓存时间,[查看详情](/tutorial/cache/mode) | +| hitSource | string | 打击源方法实例,当源方法实例请求成功时,当前方法实例的缓存将被失效,[查看详情](/tutorial/cache/auto-invalidate) | +| enableDownload | boolean | 开启下载进度信息,[查看详情](/tutorial/combine-framework/download-upload-progress) | +| enableUpload | boolean | 开启上传进度信息,[查看详情](/tutorial/combine-framework/download-upload-progress) | +| transformData | function | 转换响应数据,[查看详情](/tutorial/combine-framework/response) | +| shareRequest | boolean | 请求级共享请求开关,[查看详情](/tutorial/getting-started/method) | + +> 除了可配置上面的参数外,还支持请求适配器支持的其他参数。 + +- **返回** + +method 实例 + +- **示例** + +```ts +const getUsers = alovaInstance.Get('/users', { + params: { + id: 1 + } + // ... +}); +``` + +## alova.Post() + +创建 POST 请求的 method 实例。 + +- **类型** + +```ts +interface Alova { + Post(url: string, data?: object | FormData | string | null, config?: AlovaMethodCreateConfig): Method; +} +``` + +- **参数** + +1. url: 请求地址 +2. data: 请求 body +3. config: 配置参数,参数类型同[alova.Get](#alovaget) + +- **返回** + +method 实例 + +- **示例** + +```ts +const postUsers = alovaInstance.Post( + '/createUser', + { + name: 'alova', + age: 18, + gender: 'male' + }, + { + // 配置参数... + } +); +``` + +## alova.Delete() + +创建 DELETE 请求的 method 实例。 + +- **类型** + +```ts +interface Alova { + Delete(url: string, data?: object | FormData | string | null, config?: AlovaMethodCreateConfig): Method; +} +``` + +- **参数** + +1. url: 请求地址 +2. data: 请求 body +3. config: 配置参数,参数类型同[alova.Get](#alovaget) + +- **返回** + +method 实例 + +- **示例** + +```ts +const deleteUsers = alovaInstance.Delete( + '/deleteUser', + { + id: 1 + }, + { + // 配置参数... + } +); +``` + +## alova.Put() + +创建 PUT 请求的 method 实例。 + +- **类型** + +```ts +interface Alova { + Put(url: string, data?: object | FormData | string | null, config?: AlovaMethodCreateConfig): Method; +} +``` + +- **参数** + +1. url: 请求地址 +2. data: 请求 body +3. config: 配置参数,参数类型同[alova.Get](#alovaget) + +- **返回** + +method 实例 + +- **示例** + +```ts +const putUsers = alovaInstance.Put( + '/updateUser', + { + id: 1, + name: 'alova' + }, + { + // 配置参数... + } +); +``` + +## alova.Head() + +创建 HEAD 请求的 method 实例。 + +- **类型** + +```ts +interface Alova { + Head(url: string, config?: AlovaMethodCreateConfig): Method; +} +``` + +- **参数** + +1. url: 请求地址 +2. config: 配置参数,参数类型同[alova.Get](#alovaget) + +- **返回** + +method 实例 + +## alova.Patch() + +创建 PATCH 请求的 method 实例。 + +- **类型** + +```ts +interface Alova { + Patch(url: string, data?: object | FormData | string | null, config?: AlovaMethodCreateConfig): Method; +} +``` + +- **参数** + +1. url: 请求地址 +2. data: 请求 body +3. config: 配置参数,参数类型同[alova.Get](#alovaget) + +- **返回** + +method 实例 + +## alova.Options() + +创建 OPTIONS 请求的 method 实例。 + +- **类型** + +```ts +interface Alova { + Options(url: string, config?: AlovaMethodCreateConfig): Method; +} +``` + +- **参数** + +1. url: 请求地址 +2. config: 配置参数,参数类型同[alova.Get](#alovaget) + +- **返回** + +method 实例 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/02-method.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/02-method.md new file mode 100644 index 000000000..00430e831 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/02-method.md @@ -0,0 +1,481 @@ +--- +title: method实例 +sidebar_position: 20 +--- + +一个 method 实例对应一个请求信息描述,它拥有一次请求的 url、请求头、请求参数,以及响应数据处理、缓存数据处理等请求行为参数。通过 method 实例,你可以在任意的 js 环境下感受到统一的使用体验,只需要非常少的改动就可以正常运行起来,此外,method 实例将请求参数和请求行为参数放在了一起,更便于 api 的管理,而不是分散在多个代码文件中。 + +## PromiseLike 特性 + +在`[v2.16.0]`之后,method 实例是一个 PromiseLike 实例,它拥有`then/catch/finally`函数,因此你可以按如下方式使用: + +```ts +// 调用method的then函数 +method.then(res => { + console.log(res); +}); + +// 捕获异常 +method.catch(e => { + console.log(e); +}); + +// 请求完成调用 +method.finally(() => { + console.log('请求完成'); +}); +``` + +此外,也可以通过`await method`来发送请求。 + +## new Method() + +自定义创建 method 实例。 + +- **类型** + +```ts +interface MethodConstructor { + new ( + type: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'HEAD' | 'OPTIONS' | 'PATCH', + context: Alova, + url: string, + config?: AlovaMethodCreateConfig, + data?: Arg | string | FormData | Blob | ArrayBuffer | URLSearchParams | ReadableStream + ): Method; + readonly prototype: Method; +} +``` + +- **参数** + +1. `type`:请求类型 +2. `context`:alova 实例 +3. `url`:请求 url +4. `config`:配置参数, 类型与[alova.Get](/api/alova#alovaget)的 config 参数类型一致 +5. `data`:请求体数据 + +- **示例** + +```ts +import { Method } from 'alova'; +import { alovaInstance } from './api'; + +const method = new Method('GET', alovaInstance, '/api/users', { + params: { + id: 1 + } +}); +``` + +## getMethodKey() + +获取 method 的 key 值,此 key 值作为 alova 内部缓存 key 使用。 + +- **类型** + +```ts +function getMethodKey(method: Method): string; +``` + +- **参数** + +1. `method`:method 实例 + +- **返回** + +传入的 method 实例的 key 值。 + +- **示例** + +```ts +import { getMethodKey } from 'alova'; + +const method = alova.Get('/api/users'); +const methodKey = getMethodKey(method); +``` + +## matchSnapshotMethod() + +以[method 匹配器](/tutorial/advanced/method-matcher)的匹配方式获取已经请求过的 method 实例快照,并返回匹配的结果。 + +- **类型** + +```ts +type MethodFilter = + | string + | RegExp + | { + name?: string | RegExp; + filter?: MethodFilterHandler; + alova?: Alova; + }; +function matchSnapshotMethod(matcher: MethodFilter, matchAll?: boolean): Method[] | Method | undefined; +``` + +- **参数** + +1. `matcher`:method 匹配器 +2. `matchAll`:是否匹配全部,默认为 true + +- **返回** + +`matchAll` 为 true 时返回 method 实例数组,否则返回 method 实例或 undefined + +- **示例** + +```ts +import { matchSnapshotMethod } from 'alova'; + +await alova.Get('/api/users'); +const snapshotMethod = matchSnapshotMethod({ + name: 'user', + filter(method, i, methodArray) { + return method.url.includes('users'); + }, + alova: alova +}); +``` + +## method.headers + +请求头。 + +- **类型** + +```ts +interface Method { + headers?: any; +} +``` + +## method.baseURL + +请求的基础路径,继承于[alova 实例](/api/alova)。 + +- **类型** + +```ts +interface Method { + baseURL: string; +} +``` + +## method.url + +创建 method 实例的 url。 + +- **类型** + +```ts +interface Method { + url: string; +} +``` + +## method.type + +请求类型。 + +- **类型**: + +```ts +interface Method { + type: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'HEAD' | 'OPTIONS' | 'PATCH'; +} +``` + +## method.data + +请求 body。 + +- **类型** + +```ts +interface Method { + data?: any; +} +``` + +## method.context + +创建当前 method 的 alova 实例。 + +- **类型** + +```ts +interface Method { + context: Alova; +} +``` + +## method.hitSource + +打击源方法实例,当源方法实例请求成功时,当前方法实例的缓存将被失效。作为自动失效功能,只需设置打击源即可,而不需要手动调用`invalidateCache`失效缓存。此外,此功能在错综复杂的失效关系中比`invalidateCache`方法更简洁有效,该字段值可设置为 method 实例、其他 method 实例的 name、name 正则匹配,或者它们的数组。 + +- **类型** + +```ts +interface Method { + hitSource?: Method | string | RegExp | (Method | string | RegExp)[]; +} +``` + +## method.meta + +method 的 元数据,用于记录请求特性信息,[详情查看](/tutorial/getting-started/method-metadata)。 + +- **类型** + +```ts +interface Method { + meta?: any; +} +``` + +## method.config + +通过`alova.Get/alova.Post`等方法创建 method 时的配置信息,[详情查看](/api/alova#alovaget)。 + +- **类型** + +```ts +interface Method { + config: AlovaMethodCreateConfig; +} +``` + +## method.fromCache + +当前请求的数据是否来自缓存。 + +- **类型** + +```ts +interface Method { + fromCache: boolean; +} +``` + +## method.send() + +使用此 method 实例直接发送请求,在`[v2.16.0]`之后发送请求可省略调用此方法。 + +- **类型** + +```ts +interface Method { + send(forceRequest?: boolean): Promise; +} +``` + +- **参数** + +1. `forceRequest`:是否强制请求,默认为 false + +- **返回** + +附带响应数据的 Promise 实例。 + +- **示例** + +```ts +const method = alova.Get('/api/users'); +const response = await method.send(); +``` + +## method.abort() + +中止当前请求。 + +- **类型** + +```ts +interface Method { + abort(): void; +} +``` + +- **示例** + +```ts +const method = alova.Get('/api/users'); +method.abort(); +``` + +## method.then() + +在`[v2.16.0]`之后,method 实例是一个 PromiseLike 实例,可直接调用此方法或`await method`发送请求,获得响应数据。 + +- **类型** + +```ts +interface Method { + then(onFulfilled?: (value: Response) => any, onRejected?: (reason: any) => any): Promise; +} +``` + +- **参数** + +1. `onFulfilled`:请求成功时的回调函数 +2. `onRejected`:请求失败时的回调函数 + +- **返回** + +附带响应数据的 Promise 实例。 + +- **示例** + +```ts +const method = alova.Get('/api/users'); +const response = await method; +``` + +## method.catch() + +在`[v2.16.0]`之后,method 实例是一个 PromiseLike 实例,可直接调用此方法发送请求,并捕获错误。 + +- **类型** + +```ts +interface Method { + catch( + onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null + ): Promise; +} +``` + +- **参数** + +1. `onrejected`:请求错误时的回调函数 + +- **返回** + +Promise 实例。 + +- **示例** + +```ts +const method = alova.Get('/api/users'); +const response = await method.catch(error => { + console.error('请求错误'); +}); +``` + +## method.finally() + +在`[v2.16.0]`之后,method 实例是一个 PromiseLike 实例,可直接调用此方法发送请求,并处理响应完成。 + +- **类型** + +```ts +interface Method { + finally(onfinally?: (() => void) | undefined | null): Promise; +} +``` + +- **返回** + +Promise 实例。 + +- **示例** + +```ts +const method = alova.Get('/api/users'); +const response = await method.finally(() => { + console.log('请求完成'); +}); +``` + +## method.onDownload() + +绑定下载事件,可获得下载进度信息。 + +- **类型** + +```ts +interface Method { + onDownload(handler: ProgressHandler): () => void; +} +``` + +- **参数** + +1. `handler`下载事件回调函数 + +- **返回** + +解绑函数 + +- **示例** + +```ts +const method = alova.Get('/api/download_file'); +const offEvent = method.onDownload(event => { + console.log('文件大小:', event.total); + console.log('已下载:', event.loaded); +}); + +offEvent(); +``` + +## method.onUpload() + +绑定上传事件,可获得上传进度信息。 + +- **类型** + +```ts +interface Method { + onUpload(handler: ProgressHandler): () => void; +} +``` + +- **参数** + +1. `handler`上传事件回调函数 + +- **返回** + +解绑函数 + +- **示例** + +```ts +const method = alova.Get('/api/upload_file', formData); +const offEvent = method.onUpload(event => { + console.log('文件大小:', event.total); + console.log('已上传:', event.loaded); +}); + +offEvent(); +``` + +## method.setName() + +设置 method 实例的 name。 + +- **类型** + +```ts +interface Method { + setName(name: string | number): void; +} +``` + +- **参数** + +1. `name`:method 实例的 name + +- **返回** + +无 + +- **示例** + +```ts +const method = alova.Get('/api/users', { + name: 'user' +}); +method.setName('newUser'); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/03-core-hooks.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/03-core-hooks.md new file mode 100644 index 000000000..3ba7087fa --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/03-core-hooks.md @@ -0,0 +1,268 @@ +--- +title: 核心useHooks +sidebar_position: 30 +--- + +## useRequest + +表示一次请求的发送,执行 useRequest 时默认会发送一次请求,并创建和维护状态化的请求相关数据,如`loading/data/error`等。在页面获取初始数据时是最常用的方法,同时也支持关闭它的默认的请求发送,这在提交数据等通过点击事件触发的请求场景非常有用。 + +> 前往[useRequest](/tutorial/combine-framework/use-request)查看详情。 + +### 类型 + +```ts +function useRequest( + methodHandler: Method | (...args: any[]) => Method, + config?: RequestHookConfig +): UseHookReturnType; +``` + +### 参数 + +1. `methodHandler`: 可传入 method 实例和函数两种形式,当指定为函数时的可接收`send`传入的参数,并要求返回一个 method 实例。 +2. `config`: hook 的配置参数。 + +| 名称 | 描述 | 类型 | 默认值 | 版本 | +| ------------- | ------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | ------ | ---- | +| immediate | 是否立即发起请求 | boolean | true | - | +| initialData | 初始的 data 值,在首次响应前 data 值为初始值,未设置时为`undefined` | any | - | - | +| force | 是否强制请求,可设置为函数动态返回 boolean 值 | boolean \| (...args: any[]) => boolean \| false | - | - | +| managedStates | 额外的监管状态,可通过 updateState 更新 | Record\ | - | - | +| middleware | 中间件函数,[了解 alova 中间件](/tutorial/advanced/middleware) | (context: [AlovaFrontMiddlewareContext](#alovafrontmiddlewarecontext), next: [AlovaGuardNext](#alovaguardnext)) => Promise\ | - | - | + +#### AlovaFrontMiddlewareContext + +| 名称 | 描述 | 类型 | 版本 | +| ---------------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- | +| method | 当前请求的 method 对象 | Method | - | +| cachedResponse | 命中的缓存数据 | any | - | +| config | 当前的 use hook 配置 | Record\ | - | +| sendArgs | 响应处理回调的参数,该参数由 use hooks 的 send 传入 | any[] | - | +| frontStates | use hook 前端状态集合,如 data、loading、error 等 | [FrontRequestState](#frontrequeststate) | - | +| send | 发送请求函数 | (...args: any[]) => void | Promise | +| abort | 中断函数 | () => void | - | +| decorateSuccess | 装饰成功回调函数 | (decorator: (
handler: (event: [AlovaSuccessEvent](#alovasuccessevent)) => void,
event: [AlovaSuccessEvent](#alovasuccessevent),
index: number,
length: number
) => void) => void | - | +| decorateError | 装饰失败回调函数 | (decorator: (
handler: (event: [AlovaErrorEvent](#alovaerrorevent)) => void,
event: [AlovaErrorEvent](#alovaerrorevent),
index: number,
length: number
) => void) => void | - | +| decorateComplete | 装饰完成回调函数 | (decorator: (
handler: (event: [AlovaCompleteEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovacompleteevent),
index: number,
length: number
) => void) => void | - | +| update | 更新当前 use hook 前端状态的函数,在 react 中较有用 | (newFrontStates: [FrontRequestState](#frontrequeststate)) => void; | - | +| controlLoading | 将自定义控制 loading 的状态,调用内部不再触发 loading 状态的变更,传入 control 为 false 时将取消控制 | (control?: boolean) => void | - | + +#### AlovaGuardNext + +```typescript +type AlovaGuardNext = (guardNextConfig?: { + force?: boolean | (...args: any[]) => boolean; + method?: Method; +}): Promise; +``` + +#### FrontRequestState + +以下属性值将会根据`statesHook`自动推断出对应 UI 框架的响应式数据类型,在 vue3 中为`Ref`类型,在 react 中为普通值,在 svelte 中为`Writable`类型 + +| 名称 | 描述 | 类型 | 版本 | +| ----------- | ------------ | ------------------ | ---- | +| loading | 请求加载状态 | boolean | - | +| data | 响应数据 | any | - | +| error | 请求错误信息 | Error \| undefined | - | +| downloading | 下载进度信息 | Object | - | +| uploading | 上传进度信息 | Object | - | + +#### AlovaSuccessEvent + +| 名称 | 描述 | 类型 | 版本 | +| --------- | --------------------------------------------------- | ------- | ---- | +| method | 当前请求的 method 对象 | Method | - | +| sendArgs | 响应处理回调的参数,该参数由 use hooks 的 send 传入 | any[] | - | +| data | 响应数据 | any | - | +| fromCache | 响应数据是否来自缓存 | boolean | - | + +#### AlovaErrorEvent + +| 名称 | 描述 | 类型 | 版本 | +| -------- | --------------------------------------------------- | ------ | ---- | +| method | 当前请求的 method 对象 | Method | - | +| sendArgs | 响应处理回调的参数,该参数由 use hooks 的 send 传入 | any[] | - | +| error | 响应错误实例 | Error | - | + +#### AlovaCompleteEvent + +| 名称 | 描述 | 类型 | 版本 | +| --------- | --------------------------------------------------- | -------------------- | ---- | +| method | 当前请求的 method 对象 | Method | - | +| sendArgs | 响应处理回调的参数,该参数由 use hooks 的 send 传入 | any[] | - | +| status | 响应状态,成功时为 success,失败时为 error | 'success' \| 'error' | - | +| data | 响应数据,成功时有值 | any | - | +| fromCache | 响应数据是否来自缓存,成功时有值 | boolean | - | +| error | 响应错误实例,失败时有值 | Error | - | + +### 返回值 + +`UseHookReturnType`包含响应数据和请求相关的状态、操作函数和事件绑定函数,它们会根据`statesHook`自动推断出对应 UI 框架的响应式数据类型,在 vue3 中为`Ref`类型,在 react 中为普通值,在 svelte 中为`Writable`类型。 + +#### 响应式数据 + +| 名称 | 描述 | 类型 | 版本 | +| ----------- | ------------ | ------------------ | ---- | +| loading | 请求加载状态 | boolean | - | +| data | 响应数据 | any | - | +| error | 请求错误信息 | Error \| undefined | - | +| downloading | 下载进度信息 | Object | - | +| uploading | 上传进度信息 | Object | - | + +#### 操作函数 + +| 名称 | 描述 | 函数参数 | 返回值 | 版本 | +| ------ | --------------------------------------------------- | ------------------------------------------------------- | ------- | ---- | +| send | 发送请求函数 | ...args: any[] | - | - | +| abort | 中断函数 | - | Promise | - | +| update | 更新当前 use hook 前端状态的函数,在 react 中较有用 | newFrontStates: [FrontRequestState](#frontrequeststate) | - | + +#### 事件 + +| 名称 | 描述 | 回调参数 | 版本 | +| ---------- | ---------------- | ------------------------------------------------ | ---- | +| onSuccess | 请求成功事件绑定 | event: [AlovaSuccessEvent](#alovasuccessevent) | - | +| onError | 请求错误事件绑定 | event: [AlovaErrorEvent](#alovaerrorevent) | - | +| onComplete | 请求完成事件绑定 | event: [AlovaCompleteEvent](#alovacompleteevent) | - | + +## useWatcher + +监听状态,并在状态变化后发起请求,在一些需要随数据变化而重新请求的场景下,如分页、数据筛选、模糊搜索使用。 + +> 前往[状态变化请求](/tutorial/combine-framework/use-watcher)查看详情。 + +### 类型 + +```typescript +function useWatcher( + handler: Method | (...args: any[]) => Method, + watchingStates: State[], + config?: WatcherHookConfig +): UseHookReturnType; +``` + +### 参数 + +1. `handler`: 可传入 method 实例和函数两种形式,当指定为函数时的可接收`send`传入的参数,并要求返回一个 method 实例。 +2. `config`: hook 的配置参数。 + +| 名称 | 描述 | 类型 | 默认值 | 版本 | +| ------------- | -------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | ---------- | ---- | +| immediate | 是否立即发起请求 | boolean | true | - | +| initialData | 初始的 data 值,在首次响应前 data 值为初始值,未设置时为`undefined` | any | - | - | +| force | 是否强制请求,可设置为函数动态返回 boolean 值 | boolean \| (...args: any[]) => boolean \| false | +| managedStates | 额外的监管状态,可通过 updateState 更新 | Record\ | - | +| debounce | 请求防抖时间(毫秒),传入数组时可按 watchingStates 的顺序单独设置防抖时间 | number \| number[] | - | +| middleware | 中间件函数,[了解 alova 中间件](/tutorial/advanced/middleware) | (context: [AlovaFrontMiddlewareContext](#alovafrontmiddlewarecontext), next: [AlovaGuardNext](#alovaguardnext)) => Promise\ | - | - | +| sendable | 监听的状态改变时是否发送请求 | (methodInstance: AlovaEvent) => boolean | () => true | - | +| abortLast | 是否中断上一次的未响应请求 | boolean | true | - | + +### 返回值 + +`UseHookReturnType`包含响应数据和请求相关的状态、操作函数和事件绑定函数,它们会根据 statesHook 自动推断出对应 UI 框架的响应式数据类型,在 vue3 中为 Ref 类型,在 react 中为普通值,在 svelte 中为 Writable 类型。 + +#### 响应式数据 + +| 名称 | 描述 | 类型 | 版本 | +| ----------- | ------------ | ------------------ | ---- | +| loading | 请求加载状态 | boolean | - | +| data | 响应数据 | any | - | +| error | 请求错误信息 | Error \| undefined | - | +| downloading | 下载进度信息 | Object | - | +| uploading | 上传进度信息 | Object | - | + +#### 操作函数 + +| 名称 | 描述 | 函数参数 | 返回值 | 版本 | +| ------ | --------------------------------------------------- | ------------------------------------------------------- | ------- | ---- | +| send | 发送请求函数 | ...args: any[] | Promise | - | +| abort | 中断函数 | - | - | - | +| update | 更新当前 use hook 前端状态的函数,在 react 中较有用 | newFrontStates: [FrontRequestState](#frontrequeststate) | - | + +#### 事件 + +| 名称 | 描述 | 回调参数 | 版本 | +| ---------- | ---------------- | ------------------------------------------------ | ---- | +| onSuccess | 请求成功事件绑定 | event: [AlovaSuccessEvent](#alovasuccessevent) | - | +| onError | 请求错误事件绑定 | event: [AlovaErrorEvent](#alovaerrorevent) | - | +| onComplete | 请求完成事件绑定 | event: [AlovaCompleteEvent](#alovacompleteevent) | - | + +## useFetcher + +通过`useFetcher`用来拉取数据,在预加载数据和跨模块更新状态时很有用。 + +> 前往[数据拉取](/tutorial/advanced/use-fetcher)查看详情。 + +### 类型 + +```typescript +function useFetcher(config?: FetcherHookConfig): UseFetchHookReturnType; +``` + +### 参数 + +1. `config`: hook 的配置参数。 + +| 名称 | 描述 | 类型 | 默认值 | 版本 | +| ---------- | -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ | ---- | +| force | 是否强制请求,可设置为函数动态返回 boolean 值 | boolean | (...args: any[]) => boolean \| false | - | +| middleware | 中间件函数,[了解 alova 中间件](/tutorial/advanced/middleware) | (context: [AlovaFetcherMiddlewareContext](#alovafetchermiddlewarecontext), next: [AlovaGuardNext](#alovaguardnext)) => Promise\ | - | - | + +#### AlovaFetcherMiddlewareContext + +| 名称 | 描述 | 类型 | 版本 | +| ---------------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- | +| method | 当前请求的 method 对象 | Method | - | +| cachedResponse | 命中的缓存数据 | any | - | +| config | 当前的 use hook 配置 | Record\ | - | +| fetchArgs | 响应处理回调的参数,该参数由 useFetcher 的 fetch 传入 | any[] | - | +| fetchStates | use hook 预加载状态集合,如 fetching、error 等 | [FetchRequestState](#fetchrequeststate) | - | +| fetch | 数据预加载函数 | (method: Method, ...args: any[]) => void | Promise | +| abort | 中断函数 | () => void | - | +| decorateSuccess | 装饰成功回调函数 | (decorator: (
handler: (event: [AlovaSuccessEvent](#alovasuccessevent)) => void,
event: [AlovaSuccessEvent](#alovasuccessevent),
index: number,
length: number
) => void) => void | - | +| decorateError | 装饰失败回调函数 | (decorator: (
handler: (event: [AlovaErrorEvent](#alovaerrorevent)) => void,
event: [AlovaErrorEvent](#alovaerrorevent),
index: number,
length: number
) => void) => void | - | +| decorateComplete | 装饰完成回调函数 | (decorator: (
handler: (event: [AlovaCompleteEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovacompleteevent),
index: number,
length: number
) => void) => void | - | +| update | 更新当前 use hook 预加载状态的函数,在 react 中较有用 | (newFrontStates: [FetchRequestState](#fetchrequeststate)) => void; | - | +| controlFetching | 调用后将自定义控制 fetching 的状态,内部不再触发 fetching 状态的变更,传入 control 为 false 时将取消控制 | (control?: boolean) => void | - | + +#### FetchRequestState + +以下属性值将会根据`statesHook`自动推断出对应 UI 框架的响应式数据类型,在 vue3 中为`Ref`类型,在 react 中为普通值,在 svelte 中为`Writable`类型 + +| 名称 | 描述 | 类型 | 版本 | +| ----------- | -------------- | ------------------ | ---- | +| fetching | 预加载请求状态 | boolean | - | +| error | 请求错误信息 | Error \| undefined | - | +| downloading | 下载进度信息 | Object | - | +| uploading | 上传进度信息 | Object | - | + +### 返回值 + +`UseFetchHookReturnType`包含请求相关的状态、操作函数和事件绑定函数,它们会根据 statesHook 自动推断出对应 UI 框架的响应式数据类型,在 vue3 中为 Ref 类型,在 react 中为普通值,在 svelte 中为 Writable 类型。 + +#### 响应式数据 + +| 名称 | 描述 | 类型 | 版本 | +| ----------- | -------------- | ------------------ | ---- | +| fetching | 预加载请求状态 | boolean | - | +| error | 请求错误信息 | Error \| undefined | - | +| downloading | 下载进度信息 | Object | - | +| uploading | 上传进度信息 | Object | - | + +#### 操作函数 + +| 名称 | 描述 | 函数参数 | 返回值 | 版本 | +| ------ | --------------------------------------------------- | ------------------------------------------------------- | ------- | ---- | +| fetch | 数据预加载函数 | 1. method: 预加载的 Method 实例
2. ...args: any[] | Promise | - | +| abort | 中断函数 | - | - | - | +| update | 更新当前 use hook 前端状态的函数,在 react 中较有用 | newFrontStates: [FrontRequestState](#frontrequeststate) | - | + +#### 事件 + +| 名称 | 描述 | 回调参数 | 版本 | +| ---------- | ---------------- | ---------------------------------------------------------------------------------------------------------------- | ---- | +| onSuccess | 请求成功事件绑定 | event: [AlovaSuccessEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovasuccessevent) | - | +| onError | 请求错误事件绑定 | event: [AlovaErrorEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovaerrorevent) | - | +| onComplete | 请求完成事件绑定 | event: [AlovaCompleteEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovacompleteevent) | - | diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/04-cache.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/04-cache.md new file mode 100644 index 000000000..dbab8559b --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/04-cache.md @@ -0,0 +1,133 @@ +--- +title: 缓存操作 +sidebar_position: 40 +--- + +## invalidateCache() + +主动失效缓存。 + +> 前往[手动失效缓存](/tutorial/cache/manually-invalidate)查看详情。 + +- **类型** + +```ts +type MethodFilter = + | string + | RegExp + | { + name?: string | RegExp; + filter?: MethodFilterHandler; + alova?: Alova; + }; +function invalidateCache(matcher?: Method | Method[] | MethodFilter): void; +``` + +- **参数** + +1. `matcher`:缓存失效的匹配器,值为 method 实例或数组,也可以设置为[method 匹配器](/tutorial/advanced/method-matcher)。 + +- **返回** + +无 + +- **示例** + +```ts +import { invalidateCache } from 'alova'; + +invalidateCache(method); +invalidateCache([method1, method2]); +invalidateCache({ + name: 'userMethod', + filter: method => method.name === 'method1' +}); +``` + +## setCache() + +设置响应缓存。 + +> 前往[缓存更新与查找](/tutorial/cache/set-and-query)查看详情。 + +- **类型** + +```ts +type MethodFilter = + | string + | RegExp + | { + name?: string | RegExp; + filter?: MethodFilterHandler; + alova?: Alova; + }; +function setCache( + matcher: Method | Method[] | MethodFilter, + dataOrUpdater: R | ((oldCache: R) => R | undefined | void) +): void; +``` + +- **参数** + +1. `matcher`:值为 method 实例、method 的 name 字符串、method 的 name 正则表达式,也可以设置为[method 匹配器](/tutorial/advanced/method-matcher),将会为所有符合条件的 method 实例设置缓存数据。 +2. `dataOrUpdater`:缓存数据或更新函数,如果为函数,则需要返回新的缓存数据,如果返回`undefined`或不返回则取消更新。 + +- **返回** + +无 + +- **示例** + +```ts +import { setCache } from 'alova'; + +setCache(method, {}); +setCache([method1, method2], {}); +setCache( + { + name: 'userMethod', + filter: method => method.name === 'method1' + }, + {} +); +``` + +## queryCache() + +查询缓存。 + +> 前往[缓存更新与查找](/tutorial/cache/set-and-query)查看详情。 + +- **类型** + +```ts +type MethodFilter = + | string + | RegExp + | { + name?: string | RegExp; + filter?: MethodFilterHandler; + alova?: Alova; + }; +function queryCache(matcher?: Method | MethodFilter): R | undefined; +``` + +- **参数** + +1. `matcher`:值为 method 实例、method 的 name 字符串、method 的 name 正则表达式,也可以设置为[method 匹配器](/tutorial/advanced/method-matcher),将会为符合条件的第一个 method 实例查询缓存数据。 + +- **返回** + +缓存数据,如果没有缓存则返回`undefined`。 + +- **示例** + +```ts +import { queryCache } from 'alova'; + +const responseCache = queryCache(method); +const responseCache = queryCache({ + name: 'userMethod', + filter: method => method.name === 'method1' +}); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/05-states.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/05-states.md new file mode 100644 index 000000000..bea840a6d --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/05-states.md @@ -0,0 +1,120 @@ +--- +title: 响应状态操作 +sidebar_position: 50 +--- + +## updateState + +手动更新任意模块/页面下已存在的响应数据或额外的状态。 + +**⚠️ 请确保组件未销毁** + +`updateState`默认会查找由 alova 的 useHooks 发送请求时所创建的响应状态,但由于防止内存溢出,一个组件的销毁同时也会回收它内部创建的所有状态,因此在使用`updateState`时请确保你希望更新的响应状态对应的容器组件未被销毁,否则将无法查找到对应的响应状态而导致更新失败。 + +这个问题常常出现在跨页面更新状态时,因为当页面跳转时我们容易忽略的是,默认情况下上一个页面已经被销毁了,因此,如果你希望跨页面更新状态,这边有两个建议: + +1. 将页面组件持久化,以保证被更新的状态还可以被查找到; +2. 使用 [setCache](/tutorial/cache/set-and-query) 替代`updateState`,其原理是,当上一个页面的请求存在缓存时,更新它的缓存以保证再次创建页面时,所触发的请求可以命中更新后的缓存,达到同样的效果。 + +> 前往[跨页面/模块更新响应状态](/tutorial/advanced/update-across-components)查看详情。 + +> 使用 updateState 管理额外状态请参考[额外状态管理](/tutorial/advanced/manage-extra-states)。 + +- **类型** + +```ts +type MethodFilter = + | string + | RegExp + | { + name?: string | RegExp; + filter?: MethodFilterHandler; + alova?: Alova; + }; +function updateState( + matcher: Method | MethodFilter, + handleUpdate: UpdateStateCollection['data'] | UpdateStateCollection, + options?: UpdateOptions +): boolean; +``` + +- **参数** + +- `matcher`:值为 method 实例、method 的 name 字符串、method 的 name 正则表达式,也可以设置为[method 匹配器](/tutorial/advanced/method-matcher),如果匹配到符合条件的 method,将会调用`handleUpdate`。 +- `handleUpdate`:更新函数或更新函数集合,如果是函数集合,将会调用集合上对应的更新函数,并将返回值作为更新结果。 +- `options`:可选项。 + +| 参数名 | 类型 | 说明 | +| ------- | -------- | ---------------------------------- | +| onMatch | Function | 在匹配到 method 后,将会调用该函数 | + +- **返回** + +是否更新成功。 + +- **示例** + +在页面或组件 A 中使用`useRequest`发送请求并获得响应数据。 + +```ts +const { data } = useRequest( + alova.Get('/api/user', { + name: 'user' + }) +); +``` + +在页面或组件 B 中使用`updateState`更新响应状态。 + +```ts +import { updateState } from 'alova'; + +// 通过method实例匹配 +updateState(alova.Get('/api/user'), oldData => { + return [ + ...oldData, + { + id: 10000, + name: 'Alova', + }, + ] +}); + +// 通过method name匹配 +updateState('user', oldData => { + return [ + ...oldData, + { + id: 10000, + name: 'Alova', + }, + ]; +} + +// 通过method name正则匹配 +updateState(/^us/, oldData => { + return [ + ...oldData, + { + id: 10000, + name: 'Alova', + }, + ]; +}) + +// 通过method匹配器匹配 +updateState({ + name: 'user', + filter(method, i, methods) { + return methods.length === i + 1; + } +}, oldData => { + return [ + ...oldData, + { + id: 10000, + name: 'Alova', + }, + ]; +}); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/06-global-config.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/06-global-config.md new file mode 100644 index 000000000..45be3e559 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/06-global-config.md @@ -0,0 +1,36 @@ +--- +title: 全局配置 +sidebar_position: 60 +--- + +## globalConfig() + +全局配置。 + +- **类型** + +```ts +function globalConfig(config: AlovaGlobalConfig): void; +``` + +- **参数** + +1. config: 配置 + +| 参数名 | 类型 | 说明 | +| -------------- | ------ | -------------------------------------------------------------------------------- | +| limitSnapshots | number | method 快照数量限制,设置为 0 表示关闭保存快照,关闭后 method 快照匹配器将不可用 | + +- **返回** + +无 + +- **示例** + +```ts +import { globalConfig } from 'alova'; + +globalConfig({ + limitSnapshots: 10 +}); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/_category_.json b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/_category_.json new file mode 100644 index 000000000..4b4861e39 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "API", + "link": { + "type": "generated-index" + } +} diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/contributing/01-overview.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/contributing/01-overview.md new file mode 100644 index 000000000..7c68f3b19 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/contributing/01-overview.md @@ -0,0 +1,176 @@ +--- +title: 贡献指南 +sidebar_position: 10 +--- + +# alova 贡献指南 + +你好,很高兴在这遇到你,这是一份详细的 alova 贡献指南,它包含对 alova 各个方面的贡献提供了详细的指导,请继续往下看。 + +## 前言 + +在过去的一段时间里,我们在 Github issues 和 Github Disscussion 中收到了来自世界各地的开发者积极参与的信息,深感荣幸,这意味着 alova 正在被越来越多的开发者喜爱。即便如此,alova 也还属于新秀,它依然还有很长一段路需要走。 + +**我们期望将 alova 打造成每位愿意参与的人的共同项目,我们以开放包容的态度鼓励每个人成为 alova 社区的贡献者。而且,我们认为贡献 alova 不局限于代码贡献,而是参与任何有利于 alova 发展的活动都属于贡献 alova,** 现在参与贡献可以为你赢得更多的有效贡献机会,它可以让你为全世界的开发者提供你的价值,即使你是一位初级开发者,只要想法符合 [alova 的使命和设计理念](#alova-使命和设计理念),也请大方地参与进来! + +> 这里有一份[社区行为公约](./code-of-conduct),请参阅。 + +## 贡献目录 + +这里提供 13 个可贡献之处供你选择,但不局限于这些,你可以选择自己希望参与的部分后,链接到对应位置详细阅读: + +- [在项目中使用 alova](#在项目中使用-alova) +- [为 alova 点星](#为-alova-点星) +- [报告 bug](#报告-bug) +- [提出新特性想法](#提出新特性想法) +- [Pull Request](#pull-request) +- [基于 alova 编写适配器和策略库](#基于-alova-编写适配器和策略库) +- [参与社区交流/PR review](#参与社区交流pr-review) +- [发布和传播有利于 alova 的信息](#发布和传播有利于-alova-的信息) +- [分享使用经验](#分享使用经验) +- [项目合作](#项目合作) +- [项目捐赠](#项目捐赠) +- [更正或编写文档](#更正或编写文档) +- [翻译文档](#翻译文档) + +## alova 使命和设计理念 + +### alova 使命 + +alova 的使命为它指出了明确的发展方向,它清晰地定义了 alova 什么应该做。 + +alova 是一个轻量级的请求策略库,**它的使命就是让开发者在编写少量代码的同时,也能实现更高效地 Client-Server 数据交互**。 + +对于开发者来说,alova 为他们提供了简单的 api 和开箱即用的高级请求功能,以及各种简单的、高性能的请求策略模块,对于应用的用户来说,它们可以享受到 alova 的高性能数据交互带来的流畅体验,因此,alova 具备了以下特性: + +1. 与 axios 相似的 api 设计,让使用者学习成本更低; +2. 深度绑定 UI 框架,大大提高开发者的使用收益; +3. 开箱即用的高级功能,避免重复封装,例如请求共享、请求缓存等,减少开发者重复封装; +4. 平台无关的编码方式,可在不同平台完美迁移; +5. 高扩展性设计,可封装高复用、高性能的业务相关的请求策略; +6. 高聚合低耦合的 method 设计,提高 api 代码维护性; + +### alova 设计理念 + +设计理念指出了它应该如何设计,以下为 alova 的核心设计理念。 + +1. Method 代理设计,高聚合、平台无关的设计,贯穿请求始终,你在任意请求函数中都应该可以访问到它,从另一个角度说,与请求相关的信息也应该被放在 method 实例中; +2. 轻量级,在编码中尽量保持源码简洁,例如避免重复代码、合并变量声明、原型链函数封装、无相似 api、tree shaking,但长变量名是被允许的,因为在编译时它将会被单个字母替代; +3. 高扩展性设计,其一,alova 的设计中大量使用了适配器模式和钩子函数,例如适配器有`requestAdapter`、`storageAdapter`等,钩子函数有`beforeRequest`、`reseponded`、`transformData`、`localCache`等,而且大多存在默认行为,这样设计的目的是为了在保留高扩展性的同时,使用也足够简单;其二,全局请求参数可覆盖,例如`timeout`、`shareRequest`等,对于特别的请求可单独设置这些参数。 +4. api 设计具有普适性,其一,它表示此 api 的功能具有较高的抽象层级,而不是针对某一个具体业务而提出的;其二,api 设计具有可扩展性,以适应 api 的迭代 + +> api 普适性设计仅适用于 alova 库,如果你正在构思一个请求策略,那么可以根据具体业务来设计。 + +## 选择你感兴趣的贡献点 + +### 在项目中使用 alova + +我们认为,你们在项目中使用 alova 也是属于 alova 的贡献者,这也是在告诉人们,alova 是值得信任的开源项目,请在[这个 issue](https://github.com/alovajs/alova/issues/165)中提交你的项目,这可能会获得在 alova 官网展示你项目的机会。 + +### 为 alova 点星 + +虽然这可能会被认为微不足道,但这代表了你对 alova 的认可,对 alova 来说每一个 star 也是至关重要,请在[alova 的 Github 仓库](https://github.com/alovajs/alova)的右上角为我们点亮星星,这对我们很重要。 + +### 报告 bug + +请移步到[Github new issue](https://github.com/alovajs/alova/issues/new/choose)中选择对应的模板提交,详细说明将会在提交 issue 中展示。 + +**请注意:** 如果你想问 alova 相关的问题,请到[Github Disscussion](https://github.com/alovajs/alova/discussions)中创建,在 issue 中提问将会被立即关闭。 + +### 提出新特性想法 + +为了让 alova 可以实现它的价值和目标,在提交一个新特性想法前,请仔细阅读[alova 使命和设计理念](#alova-使命和设计理念),并保证你的新想法符合 alova 的使命和设计理念。 + +然后,请到[🚀 新特性提案](https://github.com/alovajs/alova/issues/new?assignees=&labels=feature-request&projects=&template=FEATURE_REQUEST_zh-CN.yml)中提交,详细说明将会在提交 issue 时展示。 + +### Pull Request + +你可以通过 pull request 贡献以下 3 个方面的代码。如果你是一位有意向参与的新伙伴,在[Github 贡献列表](https://github.com/alovajs/alova/contribute)中列出了所有的`good first issue`的 issues,它用来告诉有兴趣参与贡献的新伙伴,这个是一个好的开始。 + +#### bug 修改 + +在 Github issues 中被标记为[`bug:confirmed`](https://github.com/alovajs/alova/labels/bug%3Aconfirmed)的 issues,都是已被确认的 bug,你可以自由选择。 + +如果你自己遇到了 bug,也请先[报告 bug](#报告-bug)确保 bug 被确认,以避免造成无效的 pull request。 + +#### 新特性开发 + +在 Github issues 中被标记为[`feature-request:confirmed`](https://github.com/alovajs/alova/labels/feature-request%3Aconfirmed)的 issues,都是已被确认的新特性,你可以自由选择。 + +如果你有一个添加新特性的想法,也请先[提交一个新特性想法的 issue](#提出新特性想法)确保想法被确认,以避免造成无效的 pull request。 + +#### 项目配置 + +如果你很擅长项目配置,并发现了 alova 项目的不足之处,例如不够完整的配置、配置版本太老旧、自动化不足(包含项目开发自动化和 Github 仓库管理的自动化),你也可以按[新特性开发](#新特性开发)的流程进行贡献。 + +:::warning 重要 + +1. 在开发前请仔细阅读[开发指南](./developing-guidelines),它可以一步步地指导你如何贡献代码。 +2. 在你确定了一个需要解决的 issue 时,请确保它没有被其他人的 pull request 标记,它表示已被人先占有。 + +::: + +### 基于 alova 编写适配器和策略库 + +alova 提供了高扩展特性,你可以基于它编写自己的 js 库。 + +#### 自定义适配器 + +自定义各类适配器以满足不同环境下的运行要求,以下几个方向可供参考: + +1. 自定义 statesHook,满足在不同 UI 框架下执行,例如`solid/qwik`,目前内置支持`react/vue/svelte`,请阅读[自定义 statesHook](/tutorial/custom/custom-stateshook); +2. 自定义请求适配器,让 alova 可以与更多请求方案协作,例如`GraphQL/SSE`等; +3. 自定义存储适配器,满足不同环境的存储,例如`react-native`; +4. 以上任意的组合,例如官方的[uniapp 适配器](https://github.com/alovajs/adapter-uniapp),其中包含了请求适配器、存储适配器。 + +#### 自定义请求策略 + +请求策略可以帮助开发者更高效地编写出高性能功能,虽然官方的 [alova/scene](/tutorial/strategy) 提供了一些常用的请求策略,但还不足以满足广大开发者各种请求相关的业务场景,基于 alova 自定义你自己的可复用请求策略是一个不错的选择,也可以将它们发布到 npm 上给大家使用。 + +:::tip 提交你的项目 + +如果你编写了基于 alova 的 js 库,请在[这个 issue](https://github.com/alovajs/alova/issues/165)中提交你的项目,这可以让你的项目获得在 alova 官网展示的机会。 + +::: + +### 参与社区交流/PR review + +如果你对技术交流感兴趣,那么参与更多的社区交流可能更适合你,你可以在 Github issues 中参与 bug 和新特性的讨论,也可以在[Github Disscussion](https://github.com/alovajs/alova/discussions)中、[Discord](https://discord.gg/S47QGJgkVb)或[QQ 频道](https://pd.qq.com/s/1cdjx0nnw)中为别人解答问题,这可以让你与世界各地的人交流,是一件很有趣的事情。 + +同时,你也可以在[pull request](https://github.com/alovajs/alova/pulls)中参与 PR review,这也是一种交流的主题。 + +### 发布和传播 alova 的信息 + +你可以在任何社交平台、短视频平台,或技术分享平台发布或转发传播任何有利于 alova 发展的信息,这有利于提高 alova 的影响力。我们将会筛选出相关的文章或视频在 alova 官网展示它们。这里有一些优质的文章: + +- [是时候该换掉你的 axios 了](https://juejin.cn/post/7213923957824979000) +- [(深度)开源框架/库的伟大与罪恶](https://juejin.cn/post/7215608036394729532) +- [Alova.js 筆記-試用相較 axios 更輕量、更高集成的請求庫](https://uu9924079.medium.com/alova-js-%E7%AD%86%E8%A8%98-%E8%A9%A6%E7%94%A8%E7%9B%B8%E8%BC%83-axios-%E6%9B%B4%E8%BC%95%E9%87%8F-%E6%9B%B4%E9%AB%98%E9%9B%86%E6%88%90%E7%9A%84%E8%AB%8B%E6%B1%82%E5%BA%AB-546ec5424df9) + +### 分享使用经验 + +如果你有值得分享的 alova 使用经验,或者较好的实践案例,你可以在[Github Disscussion 的 Practices](https://github.com/alovajs/alova/discussions/categories/practices)中分享,较好的分享也将会在官方文档中展示。 + +### 项目合作 + +我们欢迎与任何组织或个人进行项目合作,这可以帮助我们扩大 alova 的影响力,加速项目的发展。如果您有任何合作建议或意向,请发送邮件到**hujou555@gmail.com**与我们联系。 + +### 项目捐赠 + +您可以在以下三个渠道为项目捐赠,捐赠特权说明请进入捐赠页面查看。 + +1. [Github sponsors](https://github.com/alovajs) +2. [OpenCollective](https://opencollective.com/alova) +3. [afdian](https://afdian.net/a/huzhen555) + +### 更正或编写文档 + +如果你需要添加新的文档内容,或发现 alova 的文档有错误,例如错误的示例、错误的用词、描述不正确、或未提及的内容,你可以[新建文档仓库 issue](https://github.com/alovajs/alovajs.github.io/issues/new),或[新建文档仓库 pull request](https://github.com/alovajs/alovajs.github.io/fork)直接修改错误,这应该是更好的选择,我们欢迎任何对文档的改进建议或贡献。 + +### 翻译文档 + +如果你擅长不同的语言,欢迎你为 alova 文档进行翻译,这将有助于扩展 alova 的使用范围和受众群体。 + +## 成为核心团队成员 + +具体查看[这边的内容](./become-core-member) diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/contributing/02-become-core-member.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/contributing/02-become-core-member.md new file mode 100644 index 000000000..798bca5a2 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/contributing/02-become-core-member.md @@ -0,0 +1,36 @@ +--- +title: 成为核心团队成员 +sidebar_position: 20 +--- + +🤝🤝🤝 如果你也认同 alovajs 的理念,那就让我们一同创造下一代请求库吧! + +## 职责 + +1. 负责 vscode 插件项目、请求场景模块和适配器项目,以及鸿蒙版 alovajs 的开发和维护。 +2. 编写和优化相关的开发文档。 +3. 深度参与项目设计和实现,推广维护等整个生命周期。 +4. 参与项目发展的决策和商业化探索。 + +## 收益 + +1. 核心成员头衔,有机会在前端领域提升自己的声誉,从而拓宽职业发展道路。 +2. 更多公开分享的机会,与更多前端人员接触的机会。 +3. 通过深度参与项目的设计、实现和运营,拓宽项目思维和技术经验。 +4. 对项目的决策权。 +5. 盈利分成。 + +## 要求 + +1. 认同 alova 设计理念和发展方向,并且愿意付出努力一同前行。 +2. 对技术的热情和责任心、团队协作。 +3. 技术能力能覆盖现有需求即可。 +4. 有一定的业余时间。 + +## 如何加入 + +有意向者请添加以下微信,并注明 **alova 核心成员申请**。 + +import wechatQrcode from '@site/static/img/wechat_personal.jpg'; + +wechat_qrcode diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/contributing/03-developing-guidelines.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/contributing/03-developing-guidelines.md new file mode 100644 index 000000000..c3177a591 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/contributing/03-developing-guidelines.md @@ -0,0 +1,138 @@ +--- +title: 开发指南 +sidebar_position: 30 +--- + +:::info 版本要求 + +Node.js 16+, npm 8+ + +::: + +## 1. fork 仓库 + +[打开 alova 仓库 fork 页](https://github.com/alovajs/alova/fork),点击“Create fork”fork 仓库,并将已 fork 的仓库克隆到本地。 + +## 2. 克隆项目到本地 + +使用`git clone`命令,或`Github Desktop`应用克隆项目。 + +## 3. 新建 pull request + +你可以在编写完代码后[通过 fork 仓库创建 pull request](https://docs.github.com/zh/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork),也可以分为任意多次提交代码,而无需一次提交完整代码。 + +## 4. 在本地编码 + +### 安装依赖 + +使用`npm install`安装依赖。 + +### 安装推荐插件(vscode) + +如果你使用 vscode,将会推荐你安装以下插件: + +- eslint:检查代码质量 +- prettier:格式化代码 +- jest:自动执行单元测试用例,以及执行单个合集或单元测试用例 +- EditorConfig:保证文件格式一致 + +### 项目结构 + +``` +|-.github +| |-ISSUE_TEMPLATE -> github issues模板 +| |-workflows -> github action +|-.husky -> husky配置 +|-.vscode -> vscode配置 +|-config -> rollup打包文件 +|-src -> 源代码 +|-test -> 单元测试 +| |-browser -> 浏览器环境单元测试 +| |-server -> SSR单元测试 +| |-components -> 单元测试组件 +| |-mockServer.ts -> mock接口(msw) +|-typings -> ts类型声明 +|-其他配置文件 + +``` + +### 编码规范 + +#### 代码格式 + +如果你安装了`prettier`插件,在每次保存文件时会自动进行格式化代码,因此你可以不必在意格式的问题。 + +#### 尽量减少代码 + +alova 的特性之一是轻量化,因此在编码时需要尽量减少编码量,这里有几个需要遵循的编码规范: + +1. 避免出现相同的代码块,这可以减少库的代码量,但两行代码可能就不值得封装; +2. 使用一个变量声明符聚合变量的声明,例如: + +```javascript +// ❌ +const a = 1; +const b = 2; + +// ✅ +const a = 1, + b = 2; +``` + +3. 使用常量保存固定值、原型方法,在编译`uglify`阶段减少代码量。在`src/utils/variables.ts`中定义了常用的固定值和原型方法。 + +```javascript +// ❌ +if (a === false) { + // ... +} +arr.forEach(item => { + // ... +}); + +// ✅ +import { falseValue, forEach } from '@/utils/variables'; +if (a === falseValue) { + // ... +} +forEach(arr, item => { + // ... +}); +``` + +## 5. 单元测试指南 + +编写完代码后,添加对应的单元测试用例,尽量包含边缘情况的测试。 + +alova 项目使用 jest 作为单元测试框架,使用 msw 作为模拟服务器。建议使用 TDD 模式。每次修改代码后,运行对应的单元测试并通过它。 + +:::warning 重要 + +当准备提交代码时,请确保通过了全部单元测试,当您处理 pull request 时,可以有多个小提交,GitHub 可以在合并之前自动压缩它们。 + +::: + +1. 添加浏览器相关单元测试用例,请添加到`test/browser`中对应的测试合集,如果没有合适的测试合集可自行创建; +2. 添加 SSR 相关单元测试用例,请添加到`test/server`中对应的测试合集,如果没有合适的测试合集可自行创建; + +### 运行和调试单个测试用例或合集 + +建议使用**jest**插件(上面推荐的插件之一)对单个用例或合集进行测试,你可以在测试用例中右键点击运行指定的用例,选择`Run Test`运行此测试用例,选择`Debug Test`断点调试此测试用例,如图: + +![image](https://github.com/alovajs/alova/assets/29848971/a94ba9db-c100-472f-b870-6bcecb031bea) + +### 运行全部测试用例 + +1. 使用**jest**插件运行,如下图: + +![image](https://github.com/alovajs/alova/assets/29848971/5af3ff15-16b7-4b28-9ae6-d0b5a236b181) + +2. 通过命令行`npm run test:browser`运行浏览器单元测试,通过`npm run test:node`运行 SSR 单元测试,通过`npm run test`同时运行两者。 + +## 6. 提交代码 + +alova 使用了 [semantic-release](https://semantic-release.gitbook.io) 作为自动发布工具,它可以在合并代码到`main`后自动发布新版本包,以及生成`CHANGELOG`,但需要确保提交的消息格式遵循[提交信息约定](https://www.conventionalcommits.org/zh-hans/v1.0.0/),建议你尽量使用`npm run commit`来自动生成符合规范的 git message。 + +## 7.编写文档 + +如果你正在添加新特性,可尝试添加新特性的相关文档说明,详细请阅读[更正或编写文档](/contributing/overview#更正或编写文档),否则请在 pull request 中说明。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/contributing/04-code-of-conduct.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/contributing/04-code-of-conduct.md new file mode 100644 index 000000000..42f683071 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/contributing/04-code-of-conduct.md @@ -0,0 +1,90 @@ +--- +title: 行为准则 +sidebar_position: 40 +--- + +## 我们的承诺 + +身为社区成员、贡献者和领袖,我们承诺使社区参与者不受骚扰,无论其年龄、体型、可见或不可见的缺陷、族裔、性征、性别认同和表达、经验水平、教育程度、社会与经济地位、国籍、相貌、种族、种姓、肤色、宗教信仰、性倾向或性取向如何。 + +我们承诺以有助于建立开放、友善、多样化、包容、健康社区的方式行事和互动。 + +## 我们的准则 + +有助于为我们的社区创造积极环境的行为例子包括但不限于: + +- 表现出对他人的同情和善意 +- 尊重不同的主张、观点和感受 +- 提出和大方接受建设性意见 +- 承担责任并向受我们错误影响的人道歉 +- 注重社区共同诉求,而非个人得失 + +不当行为例子包括: + +- 使用情色化的语言或图像,及性引诱或挑逗 +- 嘲弄、侮辱或诋毁性评论,以及人身或政治攻击 +- 公开或私下的骚扰行为 +- 未经他人明确许可,公布他人的私人信息,如物理或电子邮件地址 +- 其他有理由认定为违反职业操守的不当行为 + +## 责任和权力 + +社区领袖有责任解释和落实我们所认可的行为准则,并妥善公正地对他们认为不当、威胁、冒犯或有害的任何行为采取纠正措施。 + +社区领导有权力和责任删除、编辑或拒绝或拒绝与本行为准则不相符的评论(comment)、提交(commits)、代码、维基(wiki)编辑、议题(issues)或其他贡献,并在适当时机知采取措施的理由。 + +## 适用范围 + +本行为准则适用于所有社区场合,也适用于在公共场所代表社区时的个人。 + +代表社区的情形包括使用官方电子邮件地址、通过官方社交媒体帐户发帖或在线上或线下活动中担任指定代表。 + +## 监督 + +辱骂、骚扰或其他不可接受的行为可通过 [插入联系方式] 向负责监督的社区领袖报告。 +所有投诉都将得到及时和公平的审查和调查。 + +所有社区领袖都有义务尊重任何事件报告者的隐私和安全。 + +## 处理方针 + +社区领袖将遵循下列社区处理方针来明确他们所认定违反本行为准则的行为的处理方式: + +### 1. 纠正 + +**社区影响**:使用不恰当的语言或其他在社区中被认定为不符合职业道德或不受欢迎的行为。 + +**处理意见**:由社区领袖发出非公开的书面警告,明确说明违规行为的性质,并解释举止如何不妥。或将要求公开道歉。 + +### 2. 警告 + +**社区影响**:单个或一系列违规行为。 + +**处理意见**:警告并对连续性行为进行处理。在指定时间内,不得与相关人员互动,包括主动与行为准则执行者互动。这包括避免在社区场所和外部渠道中的互动。违反这些条款可能会导致临时或永久封禁。 + +### 3. 临时封禁 + +**社区影响**: 严重违反社区准则,包括持续的不当行为。 + +**处理意见**: 在指定时间内,暂时禁止与社区进行任何形式的互动或公开交流。在此期间,不得与相关人员进行公开或私下互动,包括主动与行为准则执行者互动。违反这些条款可能会导致永久封禁。 + +### 4. 永久封禁 + +**社区影响**:行为模式表现出违反社区准则,包括持续的不当行为、骚扰个人或攻击或贬低某个类别的个体。 + +**处理意见**:永久禁止在社区内进行任何形式的公开互动。 + +## 参见 + +本行为准则改编自 [Contributor Covenant][homepage] 2.1 版, 参见 [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]。 + +社区处理方针灵感来源于 [Mozilla's code of conduct enforcement ladder][mozilla coc]。 + +有关本行为准则的常见问题的答案,参见 [https://www.contributor-covenant.org/faq][faq]。 +其他语言翻译参见 [https://www.contributor-covenant.org/translations][translations]。 + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[mozilla coc]: https://github.com/mozilla/diversity +[faq]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/contributing/_category_.json b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/contributing/_category_.json new file mode 100644 index 000000000..96d3ac477 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/contributing/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "贡献指南", + "link": { + "type": "generated-index" + } +} diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/01-init-page.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/01-init-page.md new file mode 100644 index 000000000..aff0d9759 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/01-init-page.md @@ -0,0 +1,16 @@ +--- +title: (vue)页面初始化请求 +sidebar_position: 10 +--- + +import InitPage from '@site/example-links/InitPage'; + +> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); + + + +:::info 示例说明 + +本示例主要展示请求的简单性,它将返回状态化的请求状态、数据等,这些状态可直接在视图中使用,并且直接由 alova 管理 + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/02-submit-form.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/02-submit-form.md new file mode 100644 index 000000000..fccbbb3b7 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/02-submit-form.md @@ -0,0 +1,16 @@ +--- +title: (vue)表单提交 +sidebar_position: 20 +--- + +import SubmitForm from '@site/example-links/SubmitForm'; + +> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); + + + +:::info 示例说明 + +本示例主要展示基础的数据提交编码方式 + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/03-condition-search.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/03-condition-search.md new file mode 100644 index 000000000..02ea0abeb --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/03-condition-search.md @@ -0,0 +1,14 @@ +--- +title: (vue)条件搜索 +sidebar_position: 30 +--- + +import ConditionSearch from '@site/example-links/ConditionSearch'; + +> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); + + + +:::info 示例说明 +本示例主要展示数据搜索的简单性,无需开发者自己维护请求状态、数据等状态,也无需手动触发请求发送,只需绑定好搜索条件的 state 即可 +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/04-memory-cache.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/04-memory-cache.md new file mode 100644 index 000000000..c6bc2dcc5 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/04-memory-cache.md @@ -0,0 +1,21 @@ +--- +title: (vue)响应缓存-内存模式 +sidebar_position: 40 +--- + +import MemoryCache from '@site/example-links/MemoryCache'; + +> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); + + + +:::info 示例说明 + +内存缓存模式是将响应数据存放在内存中,缓存在刷新页面即失效。 + +_操作引导:_ + +1. 点击以下的学生列表项,将会发送请求学生详细信息,此时模态框显示 Loading 状态; +2. 点击遮罩关闭弹框,并重新打开它,此时将会命中缓存并立即显示学生详细信息,Request Records 中不再打印请求记录; + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/05-storage-placeholder.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/05-storage-placeholder.md new file mode 100644 index 000000000..5a63eafae --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/05-storage-placeholder.md @@ -0,0 +1,21 @@ +--- +title: (vue)响应缓存-缓存占位模式 +sidebar_position: 50 +--- + +import StoragePlaceholder from '@site/example-links/StoragePlaceholder'; + +> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); + + + +:::info 示例说明 + +缓存占位模式是将响应数据持久化,它将在刷新页面后立即更新到 data state 中作为占位数据,同时发送请求,开发者可以在响应前使用占位数据替代 Loading 状态。 + +_操作引导:_ + +1. 点击`Reload page`刷新页面,你不再看到 Loading 状态,而是旧数据被渲染出来了,且当请求响应后被替换为新数据; +2. 点击`Invalidate the data of placeholder`让缓存数据失效,此时你将重新看到 Loading 状态; + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/06-storage-restore.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/06-storage-restore.md new file mode 100644 index 000000000..3115eaa6b --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/06-storage-restore.md @@ -0,0 +1,21 @@ +--- +title: (vue)响应缓存-恢复模式 +sidebar_position: 60 +--- + +import StorageRestore from '@site/example-links/StorageRestore'; + +> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); + + + +:::info 示例说明 + +缓存恢复模式是将响应数据持久化,当请求命中缓存时将持久化缓存数据返回,不再发出请求。它一般用于一些需要服务端管理,但在一定时间内不变的数据,以下是节假日信息的恢复模式示例。 + +_操作引导:_ + +1. 点击`Reload page`刷新页面,你不再看到 Loading 状态,而会使用缓存数据并立即渲染到页面中,也不再发送请求; +2. 点击`Invalidate the data of placeholder`让缓存数据失效,此时你将重新看到 Loading 状态; + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/07-update-state.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/07-update-state.md new file mode 100644 index 000000000..5258b992e --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/07-update-state.md @@ -0,0 +1,20 @@ +--- +title: (vue)跨组件/页面更新状态 +sidebar_position: 70 +--- + +import UpdateState from '@site/example-links/UpdateState'; + +> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); + + + +:::info 示例说明 + +当需要跨组件或页面更新状态时,常常会受组件层级的限制,这里有一个可以打破这个限制的方式。 + +_操作引导:_ + +在编辑框或编辑页中,新增或编辑列表数据后,列表数据被跨越组件层级的方式更新了。 + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/08-prefetch.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/08-prefetch.md new file mode 100644 index 000000000..152c1352c --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/08-prefetch.md @@ -0,0 +1,21 @@ +--- +title: (vue)数据预拉取 +sidebar_position: 90 +--- + +import Prefetch from '@site/example-links/Prefetch'; + +> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); + + + +:::info 示例说明 + +数据预拉取是一种预测用户操作行为的一种方式,来达到内容更快展示在用户面前的策略。 + +_操作引导:_ + +1. 鼠标移动到任意列表项,并停留 0.2 秒,将会在底部面板上看到详情数据的请求被发送; +2. 单击点开这个列表项,可以立即看到详情数据; + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/09-load-more.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/09-load-more.md new file mode 100644 index 000000000..fa7d8ccdb --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/09-load-more.md @@ -0,0 +1,23 @@ +--- +title: (vue)下拉加载更多 +sidebar_position: 80 +--- + +import LoadMore from '@site/example-links/LoadMore'; + +> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); + + + +:::info 示例说明 + +使用分页策略,实现更加高性能易用的分页功能,分页相关状态自动管理、前后一页预加载、自动维护数据的新增/编辑/替换/移除,以及请求级的防抖功能。 + +_操作引导:_ + +1. 初始化完成后会预加载下一页数据,下拉翻页无需等待; +2. 添加、删除、修改列表项无需重置列表,它将自动处理成和重新请求一致的效果; + +[usePagination 文档](/tutorial/strategy/usePagination) + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/10-paginated-list.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/10-paginated-list.md new file mode 100644 index 000000000..3b5b350cc --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/10-paginated-list.md @@ -0,0 +1,23 @@ +--- +title: (vue)页码列表 +sidebar_position: 100 +--- + +import Pagination from '@site/example-links/Pagination'; + +> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); + + + +:::info 示例说明 + +使用分页策略,实现更加高性能易用的分页功能,分页相关状态自动管理、前后一页预加载、自动维护数据的新增/编辑/替换/移除,以及请求级的防抖功能。 + +_操作引导:_ + +1. 初始化完成后会预加载下一页数据,翻页到下一页时无需等待; +2. 添加、删除、修改列表项无需重置列表,它将自动处理成和重新请求一致的效果; + +[usePagination 文档](/tutorial/strategy/usePagination) + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/11-controlled-cache-by-indexeddb.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/11-controlled-cache-by-indexeddb.md new file mode 100644 index 000000000..58b630c8d --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/11-controlled-cache-by-indexeddb.md @@ -0,0 +1,23 @@ +--- +title: (vue)使用IndexedDB管理缓存 +sidebar_position: 110 +--- + +import ControlledCacheByIndexedDB from '@site/example-links/ControlledCacheByIndexedDB'; + +> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); + + + +:::info 示例说明 + +使用受控缓存让开发者自定义管理缓存,在大文件缓存下,可以配合 IndexedDB 管理本地缓存。 + +_操作引导:_ + +1. 选择其中一张图片,图片会先请求网络加载,图片数据将会保存在本地 IndexedDB 中; +2. 刷新页面,再次选择相同的图片,图片将在 IndexedDB 中获取数据,而不再发起网络请求; + +[受控缓存文档](/tutorial/cache/controlled-cache) + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/12-silent-submit-setting.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/12-silent-submit-setting.md new file mode 100644 index 000000000..e3f4af9c8 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/12-silent-submit-setting.md @@ -0,0 +1,23 @@ +--- +title: (svelte)静默提交-设置页 +sidebar_position: 120 +--- + +import SettingSilentSvelte from '@site/example-links/SettingSilentSvelte'; + +> 示例以 svelte 为例,但你还可以在 react、vue 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); + + + +:::info 示例说明 + +使用静默提交策略,提交即响应,大幅降低网络波动造成的影响,让你的应用在网络不稳定,甚至断网状态下依然非常流畅。 + +_操作引导:_ + +1. 操作设置项,它将立即产生反馈,而不需要等待服务端响应; +2. 切换请求模式和网络状态,体验它们的区别; + +[静默提交策略文档](/tutorial/strategy/sensorless-data-interaction) + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/13-silent-submit-simple-list.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/13-silent-submit-simple-list.md new file mode 100644 index 000000000..d9df67255 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/13-silent-submit-simple-list.md @@ -0,0 +1,23 @@ +--- +title: (vue)静默提交-简单列表 +sidebar_position: 130 +--- + +import SimpleListSilentVue from '@site/example-links/SimpleListSilentVue'; + +> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); + + + +:::info 示例说明 + +使用静默提交策略实现的简单列表,提交即响应,大幅降低网络波动造成的影响,让你的应用在网络不稳定,甚至断网状态下依然非常流畅。 + +_操作引导:_ + +1. 新增、编辑、删除列表项,它将立即产生反馈,而不需要等待服务端响应; +2. 切换请求模式和网络状态,体验它们的区别; + +[静默提交策略文档](/tutorial/strategy/sensorless-data-interaction) + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/14-silent-submit-notes.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/14-silent-submit-notes.md new file mode 100644 index 000000000..ec3130abe --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/14-silent-submit-notes.md @@ -0,0 +1,23 @@ +--- +title: (react)静默提交-笔记本 +sidebar_position: 140 +--- + +import NoteSilentReact from '@site/example-links/NoteSilentReact'; + +> 示例以 react 为例,但你还可以在 vue3、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); + + + +:::info 示例说明 + +使用静默提交策略实现的简单列表,提交即响应,大幅降低网络波动造成的影响,让你的应用在网络不稳定,甚至断网状态下依然非常流畅。 + +_操作引导:_ + +1. 新增、编辑、删除笔记,它将立即产生反馈,而不需要等待服务端响应; +2. 切换请求模式和网络状态,体验它们的区别; + +[静默提交策略文档](/tutorial/strategy/sensorless-data-interaction) + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/15-form-hook.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/15-form-hook.md new file mode 100644 index 000000000..253e94e36 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/15-form-hook.md @@ -0,0 +1,18 @@ +--- +title: (react)表单提交策略 +sidebar_position: 150 +--- + +import FormHook from '@site/example-links/FormHook'; + +> 示例以 react 为例,但你还可以在 vue3、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); + + + +:::info 示例说明 + +本示例分别提供了表单数据持久化、表单编辑、多模块表单、表单筛选数据 4 个部分,可分别尝试体验。 + +[表单提交策略文档](/tutorial/strategy/useForm) + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/16-captcha-send.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/16-captcha-send.md new file mode 100644 index 000000000..47e28292b --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/16-captcha-send.md @@ -0,0 +1,18 @@ +--- +title: (react)发送验证码 +sidebar_position: 160 +--- + +import CaptchaSend from '@site/example-links/CaptchaSend'; + +> 示例以 react 为例,但你还可以在 vue3、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); + + + +:::info 示例说明 + +本示例展示便捷地实现验证码发送功能。 + +[验证码发送策略文档](/tutorial/strategy/useCaptcha) + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/17-retriable-hook.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/17-retriable-hook.md new file mode 100644 index 000000000..7f5145279 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/17-retriable-hook.md @@ -0,0 +1,18 @@ +--- +title: (react)请求重试/轮询请求 +sidebar_position: 170 +--- + +import RetriableHook from '@site/example-links/RetriableHook'; + +> 示例以 react 为例,但你还可以在 vue3、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); + + + +:::info 示例说明 + +本示例分别提供了表单数据持久化、表单编辑、多模块表单、表单筛选数据 4 个部分,可分别尝试体验。 + +[请求重试策略文档](/tutorial/strategy/useRetriableRequest) + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/18.action-delegation-middleware.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/18.action-delegation-middleware.md new file mode 100644 index 000000000..e8226402d --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/18.action-delegation-middleware.md @@ -0,0 +1,18 @@ +--- +title: (react)跨组件触发请求 +sidebar_position: 180 +--- + +import ActionDelegationMiddleware from '@site/example-links/ActionDelegationMiddleware'; + +> 示例以 react 为例,但你还可以在 vue3、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); + + + +:::info 示例说明 + +本示例主要通过`actionDelegationMiddleware`中间件展示,在不使用全局状态管理的情况下,跨越任意层级组件服务端数据的刷新。 + +[操作函数委托中间件文档](/tutorial/strategy/actionDelegationMiddleware) + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/19-serial-request.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/19-serial-request.md new file mode 100644 index 000000000..cd05f8509 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/19-serial-request.md @@ -0,0 +1,19 @@ +--- +title: (vue)串行请求hook +sidebar_position: 190 +--- + +import SerialRequest from '@site/example-links/SerialRequest'; + +> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); + + + +:::info 示例说明 + +本示例主要通过`useSerialRequest`展示更优雅地进行串行请求。 + +[useSerialRequest](/tutorial/strategy/useSerialRequest) +[useSerialWatcher](/tutorial/strategy/useSerialWatcher) + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/_category_.json b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/_category_.json new file mode 100644 index 000000000..a03cd40f2 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/_category_.json @@ -0,0 +1,7 @@ +{ + "label": "示例", + "link": { + "type": "generated-index", + "description": "这里有丰富的示例,来展示alova在不同请求场景下的表现,示例运行前会先安装依赖,要稍微等待一会儿!" + } +} diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/02-quick-start.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/02-quick-start.md new file mode 100644 index 000000000..3f13c341d --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/02-quick-start.md @@ -0,0 +1,115 @@ +--- +title: 快速开始 +sidebar_position: 20 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import EmbedSandpack from "@site/src/components/EmbedSandpack"; + +import quickStartGET from '!!raw-loader!@site/codesandbox/01-getting-started/02-first-request/get.js'; +import quickStartPOST from '!!raw-loader!@site/codesandbox/01-getting-started/02-first-request/post.js'; + +:::tip 示例提示 + +如果你还未了解 alova,推荐你先阅读 [alova 概述](/tutorial/getting-started)。 + +::: + +## 安装 + + + + +```bash +npm install alova --save +``` + + + + +```bash +yarn add alova +``` + + + + +```bash +pnpm add alova +``` + + + + +```bash +bun add alova +``` + + + + +> 你也可以[通过 CDN 使用 alova](/tutorial/others/use-in-static) + +## 创建 alova 实例 + +在 alova 中需要通过 alova 实例发起请求,我们先创建一个。在创建 alova 实例时需要指定请求适配器,在这里推荐使用`GlobalFetch`请求适配器, 它是基于`fetch API`的封装。 + + + + +```javascript +import { createAlova } from 'alova'; +import GlobalFetch from 'alova/GlobalFetch'; + +const alovaInstance = createAlova({ + requestAdapter: GlobalFetch() +}); +``` + + + + +```javascript +const { createAlova } = require('alova'); +const GlobalFetch = require('alova/GlobalFetch'); + +const alova = createAlova({ + requestAdapter: GlobalFetch(); +}); +``` + +> 在 nodejs 中使用 GlobalFetch 时,nodejs 版本要求`v17.5`,或者你可以使用[axios 请求适配器](/tutorial/request-adapter/alova-adapter-axios/)。 + + + + +```javascript +import { createAlova } from 'npm:alova'; +import GlobalFetch from 'npm:alova/GlobalFetch'; + +const alova = createAlova({ + requestAdapter: GlobalFetch(); +}); +``` + + + + +## GET 请求 + +通过 `alovaInstance.Get` 发送一个请求,由于使用了`GlobalFetch`请求适配器,将会接收到一个`Response`实例,这很简单。 + + + +在异步函数中,你也可以使用`await alovaInstance.Get`等待响应。 + +## POST 请求 + +通过 `alovaInstance.Post`提交数据,这同样很简单。 + + + +## 接下来要做什么? + +实际上,这只是一个最简单的请求示例,在接下来的章节中将会了解更多功能,让我们开始学习吧。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/03-method.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/03-method.md new file mode 100644 index 000000000..c189526d3 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/03-method.md @@ -0,0 +1,306 @@ +--- +title: method详解 +sidebar_position: 30 +--- + +在上一个章节中我们尝试发送了请求,获取响应数据。实际上,`alovaInstance.Get(...)`并不是一个发起请求的函数,而是创建了一个 method 实例,它是一个 PromiseLike 实例,你可以通过`then、catch、finally`方法或`await`发送请求,就像 Promise 对象一样。 + +```javascript +const userMethodInstance = alovaInstance.Get('/api/user'); + +userMethodInstance.then(response => { + // ... +}); + +userMethodInstance.catch(error => { + // ... +}); + +userMethodInstance.finally(() => { + // ... +}); + +try { + await userMethodInstance; +} catch (error) { + // ... +} finally { + // ... +} +``` + +简便写法: + +```javascript +const response = await alovaInstance.Get('/api/user'); +``` + +每个 method 实例描述了每个请求的类型、请求 url、请求头、请求参数等。此外,你还可以在 method 实例上定义请求行为,来控制 method 以什么方式处理请求。 + +## 请求类型 + +alova 共提供了 GET、POST、PUT、DELETE、HEAD、OPTIONS、PATCH 7 种请求类型。 + +| 实例创建函数 | 参数 | +| ------------ | --------------------------------------------- | +| GET | `alovaInstance.Get(url[, config])` | +| POST | `alovaInstance.Post(url[, data[, config]])` | +| PUT | `alovaInstance.Put(url[, data[, config]])` | +| DELETE | `alovaInstance.Delete(url[, data[, config]])` | +| HEAD | `alovaInstance.Head(url[, config])` | +| OPTIONS | `alovaInstance.Options(url[, config])` | +| PATCH | `alovaInstance.Patch(url[, data[, config]])` | + +参数说明: + +- `url`是请求路径; +- `data`为请求体数据; +- `config`为请求配置对象,其中包含了请求头、params 参数等、请求行为参数等配置; + +你也可以自定义创建 method 实例,这在动态指定请求类型时很有用。 + +```javascript +import { Method } from 'alova'; + +const method = new Method('GET', alovaInstance, '/api/users', { + params: { + ID: 1 + } +}); +``` + +接下来我们先来看下如何定义请求参数,你应该会觉得很熟悉。 + +## 请求参数 + +### URL 参数 + +通过 params 传入 URL 参数,params 参数会在 url 后面以?的形式拼接。 + +```javascript +alovaInstance.Get('/todo/list', { + params: { + userId: 1 + } +}); +``` + +当然,你也可以直接拼接在 url 后面,效果是相同的。 + +```javascript +alovaInstance.Get('/todo/list?userId=1'); +``` + +### 请求体 + +当发送 **POST、PUT、DELETE、PATCH 请求** 时可以通过请求体发送数据,此时第二个参数传入的是请求体,值得注意的是,POST 请求也可以传入 params 参数。 + +```javascript +alovaInstance.Post( + '/todo', + // 第二个参数是请求体 + { + title: 'test todo', + time: '12:00' + }, + // 第三个参数是配置 + { + params: { + userId: 1 + } + } +); +``` + +### 请求头 + +通过 headers 指定请求头。 + +```javascript +alovaInstance.Get('/user', { + headers: { + 'Content-Type': 'application/json;charset=UTF-8' + } +}); +``` + +### 其他请求适配器支持的参数 + +除了请求头、params 参数等外,还支持配置对应请求适配器支持的参数,当使用`GlobalFetch`作为 alova 的请求适配器时,就可以在 method 实例上配置任何`fetch API`支持的参数,这些参数会在请求时传给`fetch`函数。 + +```javascript +alovaInstance.Get('/todo/list', { + // ... + // highlight-start + credentials: 'same-origin', + referrerPolicy: 'no-referrer', + mode: 'cors' + // highlight-end +}); +``` + +以上 method 实例在通过`fetch`发送请求时,将会以以下参数请求。 + +```javascript +fetch('/todo/list', { + // ... + // highlight-start + credentials: 'same-origin', + referrerPolicy: 'no-referrer', + mode: 'cors' + // highlight-end +}); +``` + +> 请求体除了可以传递 Object,还能传递请求适配器支持的请求体参数,例如 GlobalFetch 支持传递`string | FormData | Blob | ArrayBuffer | URLSearchParams | ReadableStream`参数。 + +如果你使用了其他的请求适配器,也可以传递它们支持的参数。 + +## 请求行为 + +在[RSM](/tutorial/others/RSM)中,请求行为用于描述将以怎样的方式处理请求。 + +### 超时时间 + +设置请求超时时间。 + +```javascript +// 请求级别的请求超时时间 +alovaInstance.Get('/todo/list', { + // ... + // highlight-start + timeout: 10000 + // highlight-end +}); +``` + +### 请求共享 + +我们总会遇到这种情况,当一个请求发出但还未响应时,又发起了相同请求,就造成了请求浪费,或者重复提交问题,例如以下三种场景: + +1. 一个组件在创建时会获取初始化数据,当一个页面同时渲染多个此组件时,将会同时发出多次相同请求; +2. 提交按钮未被禁用,用户点击了多次提交按钮; +3. 当预加载还未完成时进入了预加载页面,将会发起多次相同请求; +4. 在 react 的 StrictMode 下防止重复发送请求; + +共享请求就是用来解决这些问题的,它不仅可以提升应用流畅性,还能降低服务端压力。 + +```mermaid +flowchart LR + classDef response fill:#a8bcff + R1[请求1] --> S1[发送请求] --> W1[等待响应]:::response --> RE1[接收数据1] + R2[与请求1相同的请求] --> W1[等待响应]:::response --> RE2[接收数据1] +``` + +请求共享默认开启,如果你希望在特定请求上关闭共享请求,可以这样做: + +```javascript +alovaInst.Get('/todo', { + // ... + // highlight-start + shareRequest: false + // highlight-end +}); +``` + +:::warning 如何识别相同请求 + +通过 method 实例的请求方法、请求 url、请求头、url 参数、请求体组合作为唯一标识,标识相同即表示为相同请求,而不是对比 method 实例的引用地址。 + +::: + +### 转换响应数据 + +有时候我们需要统一转换响应数据,我们可以为 method 实例设置`transformData`函数将响应数据转换成需要的结构。 + +```javascript +alovaInstance.Get('/todo/list', { + // 函数接受响应数据和响应头数据,并要求将转换后的数据返回。 + transformData(rawData, headers) { + return rawData.list.map(item => { + return { + ...item, + statusText: item.done ? '已完成' : '进行中' + }; + }); + } +}); +``` + +### 响应缓存 + +响应缓存让你可以更好地多次利用服务端数据,而不需要每次请求时都发送请求获取数据。GET 请求将默认设置 5 分钟的内存缓存时间,我们将在后面的[响应缓存](/tutorial/cache/mode)章节中详细说明。 + +## 中断请求 + +`[2.6.2+]` 调用 method 实例的`abort`中断请求。 + +```javascript +const userMethod = alovaInstance.Get('/api/user'); +userMethod.then(res => { + // ... +}); + +const handleCancel = () => { + // highlight-start + userMethod.abort(); + // highlight-end +}; +``` + +## 监听上传下载进度 + +**[v2.17.0+]** 通过 method 实例的`onUpload`绑定上传进度事件,`onDownload`绑定下载进度事件,它将返回解绑函数。 + +```javascript +const uploadMethod = alovaInstance.Post('/todo/uploadfile', formData); +const offUploadEvent = uploadMethod.onUpload(event => { + console.log('文件大小:',event.total); + console.log('已上传:',event.loaded); +}); + +uploadMethod.then(res => { + // ... +}); + +// 解绑上传回调 +const handleOffEvent = () => { + offUploadEvent(); +}; +``` + +```javascript +const downloadMethod = alovaInstance.Get('/todo/downloadfile'); +const offDownloadEvent = downloadMethod.onDownload(event => { + console.log('文件大小:',event.total); + console.log('已下载:',event.loaded); +}); + +downloadMethod.then(res => { + // ... +}); + +// 解绑下载回调 +const handleOffEvent = () => { + offDownloadEvent(); +}; +``` + +:::warning 使用`GlobalFetch`适配器需注意 + +因 fetch api 限制,alova 提供的 **GlobalFetch** 适配器不支持上传进度,如果需要上传进度,请使用[XMLHttpRequest 适配器](/tutorial/request-adapter/alova-adapter-xhr)或[axios 适配器](/tutorial/request-adapter/alova-adapter-axios)。 + +也可以自行编写请求适配器,详见 [编写请求适配器](/tutorial/custom/custom-http-adapter)。 + +::: + +**上传/下载状态类型** + +```typescript +type Progress = { + /** 上传或下载的数据总数据量 */ + total: number; + /** 已完成的数据 */ + loaded: number; +}; +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/04-alova.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/04-alova.md new file mode 100644 index 000000000..ae5e8c4ae --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/04-alova.md @@ -0,0 +1,67 @@ +--- +title: alova详解 +sidebar_position: 40 +--- + +alova 实例不但可以创建不同类型的 method 实例,还可以设置全局参数,创建的 method 实例都会继承这个 alova 实例的参数。当 alova 实例的参数与 method 实例设置了相同的参数时,例如 `timeout`、`shareRequest`等,将优先使用 method 实例的参数。 + +接下来我们看下 alova 的全局参数。 + +## baseURL + +设置 baseURL 后,你可以不再需要为每个请求都添加相同的 url 前缀。 + +```javascript +const alovaInstance = createAlova({ + baseURL: 'https://api.alovajs.dev' + // ... +}); +``` + +此时,创建 method 实例时只需要指定 pathname 即可。 + +```javascript +alovaInstance.Get('/todo/list'); +``` + +## 全局的超时时间 + +以下为设置全局的请求超时时间。 + +```javascript +// 全局设置请求超时时间 +const alovaInstance = createAlova({ + // ... + // highlight-start + // 请求超时时间,单位为毫秒,默认为0,表示永不超时 + timeout: 50000 + // highlight-end +}); +``` + +## 全局的共享请求 + +在全局设置共享请求。 + +```javascript +const alovaInstance = createAlova({ + // ... + // highlight-start + shareRequest: false + // highlight-end +}); +``` + +## 请求适配器 + +在之前的章节中我们已经配置了`GlobalFetch`请求适配器,由这个 alova 实例发起的请求都将使用它发送请求。实际上,我们针对不同的 JS 环境,还提供了各种请求适配器。 + +- [模拟请求适配器](/tutorial/request-adapter/alova-mock) +- [XMLHttpRequest 适配器](/tutorial/request-adapter/alova-adapter-xhr) +- [axios 适配器](/tutorial/request-adapter/alova-adapter-axios) +- [uniapp 适配器](/tutorial/request-adapter/alova-adapter-uniapp) +- [taro 适配器](/tutorial/request-adapter/alova-adapter-taro) + +## 全局的响应缓存 + +你还可以全局设置响应缓存,我们将在后面的[响应缓存](/tutorial/cache/mode)章节中详细说明。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/05-global-interceptor.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/05-global-interceptor.md new file mode 100644 index 000000000..72a617f58 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/05-global-interceptor.md @@ -0,0 +1,137 @@ +--- +title: 全局拦截器 +sidebar_position: 50 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## 全局的请求拦截器 + +通常,我们需要让所有请求都用上相同的配置,例如添加 token、timestamp 到请求头,此时我们可以设置一个全局的请求拦截器,它将在所有请求前被触发,我们可以在此拦截器中统一设置请求参数。 + +```mermaid +flowchart LR + R1[请求1] --> beforeRequest + R2[请求2] --> beforeRequest + R3[请求3] --> beforeRequest + RN[请求N] --> beforeRequest + beforeRequest --> S1[发送请求] +``` + +```javascript +const alovaInstance = createAlova({ + // ... + // 函数参数为一个method实例,包含如url、params、data、headers等请求数据 + // 你可以自由修改这些数据 + // highlight-start + beforeRequest(method) { + // 假设我们需要添加token到请求头 + method.config.headers.token = 'token'; + } + // highlight-end +}); +``` + +你也可以将 beforeRequest 设置为异步函数。 + +```javascript +const alovaInstance = createAlova({ + // ... + // highlight-start + async beforeRequest(method) { + // 执行一些异步任务 + // ... + } + // highlight-end +}); +``` + +## 全局的响应拦截器 + +当我们希望统一解析响应数据、统一处理错误,以及统一处理请求完成时,此时可以在创建 alova 实例时指定全局的响应拦截器,响应拦截器包括请求成功的拦截器、请求失败的拦截器,和请求完成的拦截器。 + +```mermaid +flowchart LR + classDef error fill:#f96,stroke:#f00,stroke-width:2px; + + R1[请求1成功] --> responded.onSuccess + R2[请求2成功] --> responded.onSuccess + RN[请求N成功] --> responded.onSuccess + R4[请求4失败]:::error --> responded.onError:::error + R5[请求M失败]:::error --> responded.onError:::error + responded.onSuccess --> responded.onComplete + responded.onError --> responded.onComplete +``` + +```javascript +const alovaInstance = createAlova({ + // ... + // 使用数组的两个项,分别指定请求成功的拦截器和请求失败的拦截器 + responded: { + // highlight-start + // 请求成功的拦截器 + // 当使用GlobalFetch请求适配器时,第一个参数接收Response对象 + // 第二个参数为当前请求的method实例,你可以用它同步请求前后的配置信息 + onSuccess: async (response, method) => { + if (response.status >= 400) { + throw new Error(response.statusText); + } + const json = await response.json(); + if (json.code !== 200) { + // 抛出错误或返回reject状态的Promise实例时,此请求将抛出错误 + throw new Error(json.message); + } + + // 解析的响应数据将传给method实例的transformData钩子函数,这些函数将在后续讲解 + return json.data; + }, + // highlight-end + + // highlight-start + // 请求失败的拦截器 + // 请求错误时将会进入该拦截器。 + // 第二个参数为当前请求的method实例,你可以用它同步请求前后的配置信息 + onError: (err, method) => { + alert(error.message); + }, + // highlight-end + + // highlight-start + // 请求完成的拦截器 + // 当你需要在请求不论是成功、失败、还是命中缓存都需要执行的逻辑时,可以在创建alova实例时指定全局的`onComplete`拦截器,例如关闭请求 loading 状态。 + // 接收当前请求的method实例 + onComplete: async method => { + // 处理请求完成逻辑 + } + // highlight-end + } +}); +``` + +如果不需要设置请求失败或完成的拦截器,可以直接传入请求成功的拦截器函数,而不再需要通过对象来设置回调。 + +```javascript +const alovaInstance = createAlova({ + // ... + // highlight-start + async responded(response, method) { + // 请求成功的拦截器 + } + // highlight-end +}); +``` + +:::info 拦截器触发说明 + +当你使用`GlobalFetch`请求适配器时,由于`window.fetch`的特点,只有在连接超时或连接中断时才会触发`onError`拦截器,其他情况均会触发`onSuccess`拦截器,[详情请查看这边](https://developer.mozilla.org/docs/Web/API/fetch) + +::: + +:::warning 特别注意 + +1. `onSuccess`、`onError`和`onComplete`均可以设为同步函数和异步函数。 +2. `onError` 回调是请求错误的捕获函数,`onSuccess` 中抛出错误不会触发 `onError`。当捕获错误但没有抛出错误或返回 reject 状态的 Promise 实例,将认为请求是成功的,且不会获得响应数据。 +3. 在 2.0.x 及以前的版本中将`responded`错误地拼写为了`responsed`,在 2.1.0 中已将两者做了兼容处理,建议在后续版本中使用`responded`代替`responsed`。 + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/06-method-metadata.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/06-method-metadata.md new file mode 100644 index 000000000..a493ee4aa --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/06-method-metadata.md @@ -0,0 +1,157 @@ +--- +title: method元数据 +sidebar_position: 60 +--- + +:::info 版本要求 + +v2.7.0+ + +::: + +method 实例是贯穿 alova 的整个请求生命周期的,并且,在项目中会大量存在不同的 method 实例,有时候我们需要对特定的 method 实例添加附加信息,以便于对它们进行身份标识或额外的信息传递等,此时,我们就需要使用 method 元数据。 + +## 使用元数据标识身份 + +### 在请求前使用身份标识 + +例如,你的项目中大部分接口在每次请求时需附带`token`,但还存在一些接口无需验证的,可能你会在全局的`beforeRequest`函数中统一处理它们。 + +```javascript +const nonvalidateRequiredApi = [ + '/api/url1', + '/api/url2', + '/api/url3' + // ... +]; + +createAlova({ + beforeRequest(method) { + if (!nonvalidateRequiredApi.includes(method.url)) { + method.config.headers.token = '...'; + } + } +}); +``` + +这将导致以下两个问题: + +1. 信息没有与 method 实例聚合,可维护性更差; +2. 编码更麻烦; + +为解决这两个问题,我们将使用元数据的方式,在创建特定的 method 实例时对它进行标识。 + +**第一步:在创建 method 实例时定义元数据** + +```javascript +const loginAPI = (username, password) => { + const methodInstance = alovaInst.Post('/login', { + username, + password + }); + methodInstance.meta = { + ignoreToken: true + }; + return methodInstance; +}; +``` + +**[2.18.0+]** 你也可以直接在 config 中传入 meta 数据 + +```javascript +const loginAPI = (username, password) => { + return alovaInst.Post( + '/login', + { + username, + password + }, + { + meta: { + ignoreToken: true + } + } + ); +}; +``` + +**第二步:在`beforeRequest`中通过元数据作为判断依据** + +```javascript +createAlova({ + // ... + beforeRequest(method) { + if (!method.meta?.ignoreToken) { + method.config.headers.token = '...'; + } + } +}); +``` + +### 在响应后使用标识身份 + +这种方式还可以用于在全局的`responded`中,例如,在绝大部分情况下,请求 api 都将返回 json 数据,但可能存在文件下载接口,它将返回二进制数据流,在这种情况下,你可以在`responded`中使用不同的元数据分别处理不同的响应。 + +**第一步:创建 method 实例时同样需要分配一个元数据** + +```javascript +const downloadAPI = filePath => { + const methodInstance = alovaInst.Post('/download_file', { + filePath + }); + methodInstance.meta = { + isDownload: true + }; + return methodInstance; +}; +``` + +**第二步:在`responded`中通过元数据作为判断依据** + +```javascript +createAlova({ + // ... + responded: + onSuccess: (response, method) => method.meta?.isDownload ? response.blob() : response.json() + onError: (error, method) => { + // 在响应错误时也可以访问method实例的元数据 + } + } +}); +``` + +## 使用元数据传递信息 + +在某种情况下,如果你希望为不同的 method 实例添加附加信息,以便在其他地方使用,也可以使用元数据进行保存。以统一生成不同的 method 实例 id 为例。 + +```javascript +createAlova({ + beforeRequest(method) { + if (!method.meta.generateId) { + method.meta.uid = generateUUID(); + } + }, + + responded: { + onSuccess(response, method) { + // 在请求成功中访问当前method生成的meta数据 + const currentMethodUID = method.meta.uid; + }, + onError(error, method) { + // 在请求失败中访问当前method生成的meta数据 + const currentMethodUID = method.meta.uid; + } + } +}); +``` + +## 非 typescript 项目提示 + +在非 typescript 环境下,你可以使用任意一个属性作为信息载体,而不局限于`meta`属性。 + +```javascript +methodInstance.showResponseMsg = true; +methodInstance.others = 'abc'; +``` + +只是在 typescript 环境下,任意的属性名将会报`不存在属性“$0”。ts(2339)`,因此在类型中我们指定了`meta`属性作为信息载体。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/README.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/README.md new file mode 100644 index 000000000..4e79cfb41 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/README.md @@ -0,0 +1,148 @@ +--- +title: alova 是什么 +--- + +import Link from '@docusaurus/Link'; +import NavCard from '@site/src/components/NavCard'; +import SupportList from '@site/src/components/SupportList'; + +alova 是一个轻量级的请求策略库,它提供了一套完整的应对复杂请求场景的方案,我们称之为**请求策略**,只需一行代码就能快速实现各种复杂的请求逻辑,不仅能帮你提升开发效率,还能帮你提升 App 的运行效率,降低服务端压力。 + +这是一个最简单的请求示例: + +```javascript +const response = await alova.Get('/api/user'); +``` + +平平无奇吧?让我们再看一个自动管理请求状态的示例,**loading、error、data 是响应式的数据**,在 react、vue、svelte 等 UI 框架中可以直接在视图中绑定它们,而且会根据请求状态自动维护它这些响应式数据。 + +```javascript +const { loading, error, data } = useRequest(alova.Get('/api/user')); +``` + +以下是一个分页请求策略的示例,**当 page、pageSize 等发生变化时会自动以不同参数触发请求**。 + +```javascript +const { loading, error, data, page, pageSize, total } = usePagination((page, size) => + alova.Get('/api/user/list', { + params: { page, size } + }) +); +``` + +alova 提供了 10+个基于[RSM](/tutorial/others/RSM)规范的请求策略模块,它们以 useHook 的形式实现。 + +## 核心 useHook + +useRequest +useWatcher +useFetcher + +## 场景化请求策略 + +usePagination +useSQRequest +useForm +TokenAuthentication +useAutoRequest +useCaptcha +actionDelegationMiddleware +useSerialRequest +useSerialWatcher +useRetriableRequest +useUploader +useSSE + +## 高灵活性 + +得益于 alova 的高灵活性,你可以在以下不同 JS 环境下搭配不同的请求库使用(灰色部分将在未来逐渐支持)。 + + + +## 有什么不同吗? + +与其他请求库不同的是,alova 的目标是让请求变得更简单并保持更高效的数据交互。 + +我们为开发者和 App 使用者双方考虑,对于开发者来说,alova 为他们提供了简单的请求 api,和开箱即用的高性能请求策略模块,对于应用的用户来说,他们可以享受到 alova 的高性能数据交互带来的流畅体验。 + +此外,再从具体的特性来看看: + +- 与 axios 相似的 api 设计,让使用者学习成本更低; +- 10+个开箱即用的高性能请求策略,让应用更流畅; +- alova 是轻量级的,只有 4kb+,是 axios 的 30%+; +- 灵活性高,alova 的适配器可以让 alova 在任何 js 环境下,与任何 UI 框架协作使用(内置支持的 UI 框架为`vue/react/svelte`),并且提供了统一的使用体验和完美的代码迁移; +- 3 种缓存模式和请求共享机制,提升请求性能并降低服务端压力; +- api 代码的高聚合组织,每个 api 的请求参数、缓存行为、响应数据转换等都将聚集在相同的代码块中,这对于管理大量的 api 有很大的优势; + +在[alova 的未来](/tutorial/others/future)中,将实现更进一步的请求简单化。 + +:::info 与其他请求库的对比 + +你还可以查看请[与其他请求库比较](/tutorial/others/comparison)详细了解 alova 的不同之处。 + +::: + +## 在线试用 + +你可以通过 Codesandbox [在线可编辑示例尝试 alovajs](/category/examples)直接在浏览器中运行项目,因此它与本地开发几乎无差别,同时无需在你的机器上安装任何东西。 + +## 脚手架推荐 + +, +title: 'Uniapp 脚手架 - unibest', +desc: '集成了最新前端技术栈的跨端解决方案', +link: 'https://codercup.github.io/unibest-docs/', +target: '__blank' +} +]}> + +## 加入 alova 社区 + +import ImgDiscord from '@site/static/img/discord.svg'; +import ImgX from '@site/static/img/x.svg'; +import ImgWechat from '@site/static/img/wechat.svg'; +import wechatQrcode from '@site/static/img/wechat_qrcode.jpg'; + +, +title: 'Discord', +desc: '社区的 GPT 机器人为你解答', +link: 'https://discord.gg/S47QGJgkVb', +target: '__blank' +}, +{ +Image: , +title: '微信', +desc: '在群聊交流,更快获得回应', +link: wechatQrcode, +target: '__blank' +}, +{ +Image: , +title: 'X', +desc: '关注我们,持续获得最新动态', +link: 'https://x.com/alovajs', +target: '__blank' +} +]}> + +## 欢迎参与贡献 + +在参与贡献前,请务必详细阅读 [贡献指南](/contributing/overview),以保证你的有效贡献。 + +## 开始 + +接下来,我们将从最简单的请求开始,再到请求策略的讲解,了解 alova 如何简化你的工作,再深入到进阶指南,以及在实际项目中总结的最佳实践。 + +让我们开始学习发送第一个请求吧! + + diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/_category_.json b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/_category_.json new file mode 100644 index 000000000..b66c2bf01 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/_category_.json @@ -0,0 +1,3 @@ +{ + "label": "开始" +} diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-combine-framework/02-use-request.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/02-use-request.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-combine-framework/02-use-request.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/02-use-request.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-combine-framework/03-use-watcher.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/03-use-watcher.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-combine-framework/03-use-watcher.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/03-use-watcher.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-combine-framework/04-initial-data.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/04-initial-data.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-combine-framework/04-initial-data.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/04-initial-data.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-combine-framework/05-response.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/05-response.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-combine-framework/05-response.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/05-response.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-combine-framework/06-abort-request.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/06-abort-request.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-combine-framework/06-abort-request.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/06-abort-request.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-combine-framework/07-download-upload-progress.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/07-download-upload-progress.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-combine-framework/07-download-upload-progress.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/07-download-upload-progress.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-combine-framework/08-receive-params.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/08-receive-params.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-combine-framework/08-receive-params.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/08-receive-params.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/10-typescript.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/10-typescript.md new file mode 100644 index 000000000..5fa312aaf --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/10-typescript.md @@ -0,0 +1,159 @@ +--- +title: Typescript +sidebar_position: 100 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +在 Typescript 方面,我们确实花了很大的精力优化,为的就是提供更好的使用体验,我们尽力地使用自动推断类型来减少你定义类型的麻烦。 + +## 自动推断 alova useHooks 状态类型 + +在 createAlova 创建 alova 实例时会根据传入的`statesHook`自动推断出`useRequest`、`useWatcher`、`useFetcher`所创建的状态类型。 + +> `useFetcher`是一个用于数据拉取的 useHook,详情请阅读[进阶-数据拉取章节](/tutorial/advanced/use-fetcher)。 + +以下为预设中,useHooks 返回的状态类型。 + + + + +```typescript +const vueAlova = createAlova({ + statesHook: VueHook + // ... +}); +const { + loading, // Ref + data, // Ref<{ data: any }> + error // Ref +} = useRequest(vueAlova.Get<{ data: any }>('/todo/list')); +``` + + + + +```typescript +const reactAlova = createAlova({ + statesHook: ReactHook + // ... +}); +const { + loading, // boolean + data, // { data: any } + error // Error +} = useRequest(reactAlova.Get<{ data: any }>('/todo/list')); +``` + + + + +```typescript +const svelteAlova = createAlova({ + statesHook: SvelteHook + // ... +}); +const { + loading, // Writable + data, // Writable<{ data: any }> + error // Writable +} = useRequest(svelteAlova.Get<{ data: any }>('/todo/list')); +``` + + + + +data 的类型将会根据不同的 Method 实例中指定的响应数据类型而不同,我们继续往下看。 + +## 设置响应数据的类型 + +当你为一个数据接口指定类型时,需要分为两种情况。 + +### 情况 1 + +当响应数据不需要再调用`transformData`转换,直接通过泛型指定类型 + +```typescript +interface Todo { + title: string; + time: string; + done: boolean; +} +const Get = alovaInstance.Get('/todo/list'); +const { data } = useRequest(Get); +// vue: data的类型为Ref +// react: data的类型为Todo[] +// svelte: data的类型为Writable +``` + +### 情况 2 + +当响应数据需要再调用`transformData`转换,那就需要在转换函数参数中指定类型,然后它的返回值类型将会作为响应数据类型。 + +```typescript +interface Todo { + title: string; + time: string; + done: boolean; +} +const Get = alovaInstance.Get('/todo/list', { + // 将类型写到data参数中,而headers会自动推断,可以不用指定类型 + transformData(data: Todo[], headers) { + return data.map(item => ({ + ...item, + status: item.done ? '已完成' : '未完成' + })); + } +}); + +const { data } = useRequest(Get); +// vue: data的类型为Ref<(Todo & { status: string })[]> +// react: data的类型为(Todo & { status: string })[] +// svelte: data的类型为Writable<(Todo & { status: string })[]> +``` + +:::warning 注意 + +响应数据是经过全局响应拦截器转换后的,因此设置类型时也应该设置为转换后的类型。 + +::: + +## 根据请求适配器推断的类型 + +因为 alova 支持自定义请求适配器,而不同的适配器的请求配置对象、响应对象、响应头都可能不同,因此全局的`beforeRequest`、`responded`拦截器,以及 method 实例创建时的配置对象的类型,都会根据请求适配器提供的类型自动推断,我们先来看这几个类型。 + +如果你正在使用 [**GlobalFetch**](https://github.com/alovajs/alova/blob/main/src/predefine/GlobalFetch.ts),alova 将会使用`fetch api`的类型自动推断,fetch api 的类型如下。 + +```typescript +declare function fetch(input: RequestInfo | URL, init?: RequestInit): Promise; +``` + +### method 实例的配置类型 + +method 配置类型将被自动推断为: + +```typescript +// AlovaMethodCommonConfig为统一的请求参数和行为参数 +// highlight-start +const methodConfig: AlovaMethodCommonConfig & RequestInit = { + // highlight-end + // ... +}; +alovaInstance.Get('/api/user', methodConfig); +``` + +### 全局响应拦截器参数类型 + +responded 拦截器的类型将被自动推断为: + +```typescript +createAlova({ + // ... + // highlight-start + responded: (response: Response, method: Method) => { + // highlight-end + // ... + } +}); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-combine-framework/README.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/README.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-combine-framework/README.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/README.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-combine-framework/_category_.json b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/_category_.json similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-combine-framework/_category_.json rename to i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/_category_.json diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/01-mode.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/01-mode.md new file mode 100644 index 000000000..62da7b2db --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/01-mode.md @@ -0,0 +1,218 @@ +--- +title: 缓存模式 +sidebar_position: 10 +--- + +import MemoryCache from '@site/example-links/MemoryCache'; +import StoragePlaceholder from '@site/example-links/StoragePlaceholder'; +import StorageRestore from '@site/example-links/StorageRestore'; + +缓存模式可在全局或请求级等不同粒度下设置。全局设置时,所有由相同 alova 实例创建的 method 实例都会继承该设置。 + +:::info 注意 + +是否使用缓存模式,以及使用哪种缓存模式需要根据场景而定,下面在单独介绍不同缓存模式时将会提及它们的使用场景。 + +::: + +## 内存模式(默认) + +内存模式将缓存放在内存中,这意味着刷新页面缓存即失效,是最常用的缓存模式。 + +内存模式一般用于解决短时间内(几分钟或几秒钟)频繁请求相同数据带来的性能消耗,例如当你在写 todo 详情页的时候,你可能会想到用户会频繁在 todo 列表中点击查看详情,如果用户重复查看某条详情时不再重复请求接口,并且能立即返回数据,提升了响应速度的同时也减小了服务器压力。此时我们就可以为某个 todo 详情 method 实例设置响应数据缓存。 + +```javascript +alovaInstance.GET('/todo/list', { + // ... + // highlight-start + localCache: { + // 设置缓存模式为内存模式 + mode: 'memory', + + // 单位为毫秒 + // 当设置为`Infinity`,表示数据永不过期,设置为0或负数时表示不缓存 + expire: 60 * 10 * 1000 + } + // highlight-end +}); +``` + +内存模式为默认模式,你可以这样简写 + +```javascript +alovaInstance.GET('/todo/list', { + // ... + // highlight-start + localCache: 60 * 10 * 1000 + // highlight-end +}); +``` + +> GET 请求将默认设置 300000ms(5 分钟)的内存缓存时间,开发者也可以自定义设置。 + +> 如果你需要全局统一设置缓存模式,见本节底部的 [全局设置缓存模式](#全局设置缓存模式) + +### 内存缓存模式示例 + + + +## 缓存占位模式 + +这个缓存模式用于,当你不希望应用每次进入时都显示 Loading 图标,而希望使用旧数据替代时,你可以使用缓存占位模式,它的体验比 Loading 更好。 + +缓存占位模式下,`data`将立即被赋值为上次缓存的旧数据,你可以判断如果有旧数据则使用它替代 Loading 展示,同时它将发送请求获取最新数据并更新缓存,这样就达到了既快速展示实际数据,又获取了最新的数据。 + +在 method 实例上设置: + +```javascript +const todoListGetter = alovaInstance.Get('/todo/list', { + // ... + // highlight-start + localCache: { + // 设置缓存模式为持久化占位模式 + mode: 'placeholder', + // 缓存时间 + expire: 60 * 10 * 1000 + } + // highlight-end +}); +``` + +> 如果你需要全局统一设置缓存模式,见本节底部的 [全局设置缓存模式](#全局设置缓存模式) + +### 缓存占位模式示例 + + + +## 恢复模式 + +此模式下,服务端缓存数据将持久化,如果过期时间未到即使刷新页面缓存也不会失效,它一般用于一些需要服务端管理,但基本不变的数据,如每年的节假日具体日期有所不同,但不会再变动,这种场景下我们只需设置缓存过期时间为今年的最后一刻即可。 + +在 method 实例上设置: + +```javascript +const todoListGetter = alovaInstance.Get('/todo/list', { + // ... + // highlight-start + localCache: { + // 设置缓存模式为持久化模式 + mode: 'restore', + // 缓存时间 + expire: 60 * 10 * 1000 + } + // highlight-end +}); +``` + +:::warning 注意 + +当 request body 是**FormData**、**Blob**、**ArrayBuffer**、**URLSearchParams**、**ReadableStream**等特殊数据时,将会被认为你是有意图和服务端通信的,在这种情况下不会进行缓存。 + +::: + +> 如果你需要全局统一设置缓存模式,见本节底部的 [全局设置缓存模式](#全局设置缓存模式) + +### 恢复模式示例 + + + +### 恢复模式下数据有变怎么办? + +当设置了恢复模式的 method 实例,可能由于接口数据变动,或前端处理响应数据的逻辑变动,此时需要在发布应用后让用户重新缓存变动后的数据,此时你可以通过`tag`属性设置缓存标签,每一份持久化数据都包含一个`tag`标识,当`tag`改变后原有的持久化数据将会失效,并重新获取新的数据,并用新的`tag`进行标识。 + +```javascript +const todoListGetter = alovaInstance.Get('/todo/list', { + // ... + localCache: { + mode: 'restore', + expire: 60 * 10 * 1000, + + // highlight-start + // 新增或修改tag参数,已缓存的数据将失效 + // 建议使用版本号的形式管理 + tag: 'v1' + // highlight-end + } +}); +``` + +## 全局设置缓存模式 + +:::info 版本要求 + +v1.3.0+ + +::: + +以上设置均是在`Method`上单独设置缓存模式的,如果你需要全局设置缓存模式,可以按如下方式做: + +```javascript +const alovaInstance = createAlova({ + // ... + // highlight-start + localCache: { + // 统一设置POST的缓存模式 + POST: { + mode: 'placeholder', + expire: 60 * 10 * 1000 + }, + // 统一设置HEAD请求的缓存模式 + HEAD: 60 * 10 * 1000 + } + // highlight-end +}); +``` + +此后,通过`alovaInstance`实例创建的 method 实例,都将默认使用这份缓存设置,同时也可以在 method 实例中覆盖它。 + +> 注意:当全局设置了缓存模式后,原有的 5 分钟 GET 缓存模式将被覆盖。 + +## 全局关闭缓存模式 + +如果在你的项目中不希望使用任何请求缓存,可以在全局将它关闭,如果希望只在特定的几个请求中使用,也可以全局关闭它,并在指定的 method 实例中设置即可。 + +```javascript +const alovaInstance = createAlova({ + // ... + // highlight-start + // 设置为null即可全局关闭全部请求缓存 + localCache: null + // highlight-end +}); +``` + +## 过期时间类型 + +过期时间有两种类型可供选择,分别为 **相对时间** 和 **绝对时间** + +### 相对时间 + +即在保存缓存数据时开始,过期的时长,以 **毫秒** 为单位,以上示例均为此类型。 + +```javascript +localCache: 60 * 10 * 1000; +``` + +```javascript +localCache: { + expire: 60 * 10 * 1000, +} +``` + +### 绝对时间 + +以一个具体时间点为过期时间,缓存将在设定的时间点过期 + +```javascript +localCache: new Date('2030-01-01'); +``` + +```javascript +localCache: { + expire: new Date('2030-01-01'); +} +``` + +## 响应自动维护说明 + +响应数据缓存的 key 是由 method 实例的请求方法(method)、请求地址(url)、请求头参数(headers)、url 参数(params)、请求体参数(requestBody)组合作为唯一标识,任意一个信息或位置不同都将被当做不同的 key。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/02-auto-invalidate.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/02-auto-invalidate.md new file mode 100644 index 000000000..fbdf57836 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/02-auto-invalidate.md @@ -0,0 +1,91 @@ +--- +title: 自动失效 +sidebar_position: 20 +--- + +有这样一个场景,当用户点开 todo 列表中的某一项,进入 todo 详情页并对它执行了编辑,此时我们希望上一页中的 todo 列表数据也更新为编辑后的内容,通常的做法是通过事件来触发上一页的内容更新,这样增加了维护成本。而`alova`提供了 3 种方式,可以很优雅地达到这个目的: + +1. 使用`useFetcher`立即重新请求最新的数据,它将在[数据拉取](/tutorial/advanced/use-fetcher)章节中讲解; +2. 更新缓存,这种方式将在后面的[缓存设置与查询](/tutorial/cache/set-and-query)章节中详细讲解; +3. 让这个响应缓存失效,当再次请求时将会因缓存失效而重新请求数据。这也是这个章节要讲解的内容。 + +自动失效缓存是在目标缓存中设置失效源规则,只要匹配规则都可以让目标缓存自动失效,这在很多时候省去了手动清除缓存的麻烦。 + +## 使用场景 + +当目标缓存与失效源是一对一或一对多时,设置自动失效规则会很方便。 + +```mermaid +flowchart + M1[method1失效源指向] --> T1[目标缓存] + M11[method1失效源指向] --> T2[目标缓存] + M2[method2失效源指向] --> T2[目标缓存] + MN[methodN失效源指向] --> T2[目标缓存] +``` + +## 设置自动失效规则 + +设置这个规则很简单,你可以在创建一个带缓存的 Method 实例时,为它设置`hitSource`参数即可。 + +### 失效源设置为 method 实例 + +以一个固定的 method 实例作为失效源,只要此 method 实例或它的克隆实例请求成功,目标缓存将被自动清除。 + +```javascript +alova.Get('/todo/1', { + // ... + hitSource: alova.Post('/todo', {}) +}); +``` + +### 通过 method 名称匹配失效源 + +和 method 匹配器一样,你可以在 hitSource 中指定 method 的名称来匹配失效源,多个失效源可以设置为同一个名称,带有这个名称的 method 实例请求成功时,目标缓存将被自动清除。 + +```javascript +const methodSubmitTodo = data => + alova.Post('/todo', data, { + name: 'submitTodo' + }); + +alova.Get('/todo/1', { + // ... + // 匹配method实例名称为submitTodo的失效源 + hitSource: 'submitTodo' +}); +``` + +### 通过 method 名称正则表达式匹配失效源 + +如果 method 实例名称不固定时,你可以在 hitSource 中指定一个正则表达式来匹配 method 名称,被匹配的 method 实例在请求成功时,目标缓存将被自动清除。 + +```javascript +const methodSubmitTodo = data => + alova.Post('/todo', data, { + name: 'prefix-submitTodo' + }); + +alova.Get('/todo/1', { + // ... + // 匹配method实例名称为prefix开头的所有实例 + hitSource: /^prefix/ +}); +``` + +### 组合设置失效源 + +如果你希望使用以上的多种规则匹配失效源,可以将 hitSource 指定为一个数组,数组项为以上 3 种规则的任意一种,满足数组任意一项规则的 method 实例将被匹配。 + +```javascript +alova.Get('/todo/1', { + // ... + // 满足数组中任意一项匹配规则的method实例请求成功时,此缓存将失效 + hitSource: [alova.Post('/todo', {}), 'submitTodo', /^prefix/] +}); +``` + +## hitSource 数据类型 + +```typescript +type hitSource = string | RegExp | Method | (string | RegExp | Method)[]; +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/03-manually-invalidate.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/03-manually-invalidate.md new file mode 100644 index 000000000..512468e41 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/03-manually-invalidate.md @@ -0,0 +1,102 @@ +--- +title: 手动失效 +sidebar_position: 30 +--- + +通常,自动失效缓存更加简洁,并且推荐优先使用它来失效缓存,当自动失效缓存不满足需求时,你还可以通过调用`invalidateCache`来失效缓存。 + +## 使用 method 实例失效缓存 + +在 `invalidateCache` 函数中传入一个 method 实例,它将查找此实例下的缓存进行失效。 + +在下面的例子中,当提交成功后,将使这条 todo 详情数据缓存失效。 + +```javascript +// 获取 id 为 1 的 todo 详情数据 +const getTodoDetail = id => + alovaInstance.Get(`/todo/${id}`, { + localCache: 1000000 + }); +const { loading, data } = useRequest(getTodoDetail(1)); +``` + +```javascript +// 提交数据并让 id 为 1 的 todo 详情数据失效。 +const { + // ... + send, + onSuccess +} = useRequest(createTodoPoster, { immediate: false }); + +// highlight-start +// 提交成功后失效缓存 +onSuccess(() => { + invalidateCache(getTodoDetail(1)); +}); +// highlight-end + +const handleSubmit = () => { + send({ + title: 'new todo', + content: 'new todo content' + }); +}; +``` + +## 批量失效缓存 + +在下面的例子中,我们通过指定缓存的名称或名称的正则表达式来批量失效缓存。 + +```javascript +// 名称为todoList的method的缓存将失效 +invalidateCache('todoList'); + +// 名称符合以下正则表达式的method的缓存将失效 +invalidateCache(/^todoList/); +``` + +## 动态失效缓存 + +可能有时候你并不确定需要失效哪个缓存数据,我们可以使用 [method 匹配器](/tutorial/advanced/method-matcher) 来动态查找对应的 method 实例。以下例子展示了如何让名称为 todoList 的前 5 个 method 实例的缓存失效。 + +```javascript +const getTodoList = currentPage => { + return alovaInstance.Get('/todo/list', { + // highlight-start + // 先为method实例设置名称,用于在无法直接指定Method实例时,过滤出需要的Method实例 + name: 'todoList', + // highlight-end + params: { + currentPage, + pageSize: 10 + } + }); +}; + +const { + // ... + send, + onSuccess +} = useRequest(createTodoPoster, { immediate: false }); +// 提交成功后,固定使第一页的todo数据缓存失效 +onSuccess(() => { + // highlight-start + // 失效名称为todoList的前5个Method实例的缓存 + invalidateCache({ + name: 'todoList', + filter: (method, index, ary) => { + return index < 5; + } + }); + // highlight-end +}); +``` + +> 更多 method 匹配器的使用方法见 [method 匹配器](/tutorial/advanced/method-matcher) + +## 失效所有缓存 + +```javascript +// 当不传任何参数时,失效所有响应缓存 +invalidateCache(); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/04-force-request.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/04-force-request.md new file mode 100644 index 000000000..ef4926f78 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/04-force-request.md @@ -0,0 +1,75 @@ +--- +title: 强制请求 +sidebar_position: 40 +--- + +强制请求是指绕过缓存的检查触发请求发送的机制,当需要在一定条件下获取最新的数据时很有用。 + +## 在 method 中强制请求 + +通过调用 method 实例的 send 函数,并传入 true 来强制请求。 + +```javascript +const response = await alovaInstance.Get('/api/user').send(true); +``` + +## 在 useHook 中强制请求 + +在`useRequest/useWatcher/useFetcher`三个核心 hook 中,都支持强制请求参数。 + +```javascript +// useRequest +useRequest(todoListGetter, { + // highlight-start + force: true + // highlight-end +}); + +// useWatcher +useWatcher(todoListGetter, [page], { + // highlight-start + force: true + // highlight-end +}); + +// useFetcher +useFetcher({ + // highlight-start + force: true + // highlight-end +}); +``` + +### 动态设置 force 值 + +实际情况中,我们经常需要根据不同情况来设置是否需要强制发送请求,此时可以将 force 设置为一个函数,此函数也将接收来自 `send` 函数传入的参数。在之前的[接收参数](/tutorial/combine-framework/receive-params)中已讲解过。 + +```javascript +useRequest(todoListGetter, { + // highlight-start + force: isForce => { + return isForce; + } + // highlight-end +}); + +// useWatcher +useWatcher(todoListGetter, [page], { + // highlight-start + force: isForce => { + return isForce; + } + // highlight-end +}); + +// useFetcher +useFetcher({ + // highlight-start + force: isForce => { + return isForce; + } + // highlight-end +}); +``` + +`useFetcher`是一个数据拉取的 useHook,将在后面的[数据拉取](/tutorial/advanced/use-fetcher)中讲解。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/05-set-and-query.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/05-set-and-query.md new file mode 100644 index 000000000..26d210dfb --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/05-set-and-query.md @@ -0,0 +1,284 @@ +--- +title: 更新与查找缓存 +sidebar_position: 50 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +缓存也支持更新和查找,在[缓存模式](/tutorial/cache/mode)中我们提到过,每份缓存数据是以发送请求的 method 实例作为 key 进行保存的,因此在手动更新缓存时也将使用 method 实例来查找对应的缓存数据。 + +## 更新静态缓存数据 + + + + +```html + + +``` + + + + +```jsx +import { setCache } from 'alova'; +import { useState } from 'react'; + +const getTodoListByDate = dateList => + alovaInstance.Get('/todo/list/dates', { + params: { dateList } + }); + +const App = () => { + // 初始化时批量获取5天的数据 + const [dates, setDates] = useState(['2022-05-01', '2022-05-02', '2022-05-03', '2022-05-04', '2022-05-05']); + const { + // ... + onSuccess + } = useWatcher(() => getTodoListByDate(dates.join()), [dates], { + immediate: true + }); + onSuccess(({ data: todoListDates }) => { + if (todoListDates.length <= 1) { + return; + } + + // highlight-start + // 默认情况下,这5天的数据会一起缓存到一个key中 + // 为了让后续请求某一天的数据时也能命中缓存,我们可以将5天的数据拆解为按天,并通过setCache一一手动设置响应缓存 + // setCache的第一个参数为method实例对象,它用于指定缓存的key + // 第二个参数为缓存数据 + todoListDates.forEach(todoDate => { + setCache(getTodoListByDate(todoDate.date), [todoDate]); + }); + // highlight-end + }); + + // highlight-start + const handleTodolistToggle = () => { + // 此时再在切换日期为5月1日时,它将会命中我们手动设置的响应缓存。 + // dates值正在被useWatcher监听,因此改变它就可以自动触发请求 + setDates(['2022-05-01']); + }; + // highlight-end + + return ; +}; +``` + + + + +```html + + +``` + + + + +```html + + +``` + + + + +## 动态更新缓存数据 + +你也可以在`setCache`中传入一个回调函数来动态计算缓存数据,并返回需要更新的缓存数据。 + +```javascript +setCache(getTodoListByDate('2022-10-01'), oldCache => { + // 返回需要缓存的数据 + return { + ...oldCache, + expire: isAfter('2022-10-01', new Date()) + }; +}); +``` + +同样的,你也可以通过 [method 匹配器](/tutorial/advanced/method-matcher) 动态查找 method 实例。 + +```javascript +setCache( + { + name: 'todoList', + filter: (method, index, ary) => { + return index < 5; + } + }, + 'newCache' +); +``` + +## 中断缓存更新 + +有时候你需要动态判断是否需要更新缓存,如果在`setCache`的回调函数中未返回数据,或返回了`undefined`,此时将不更新原缓存数据 + +```javascript +setCache(getTodoListByDate('2022-10-01'), oldCache => { + const isExpired = isAfter('2022-10-01', new Date()); + if (!isExpired) { + return; // 中断缓存更新 + } + return null; // 将缓存更新为null +}); +``` + +## 查询缓存 + +同时,我们也提供了缓存查询方法。 + +```javascript +import { queryCache } from 'alova'; + +const cacheData = queryCache(getTodoListByDate('2022-10-01')); +``` + +你也可以通过 [method 匹配器](/tutorial/advanced/method-matcher) 动态查找 method 实例。 + +```javascript +const cacheData = queryCache({ + name: 'todoList', + filter: (method, index, ary) => { + return index < 5; + } +}); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/06-controlled-cache.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/06-controlled-cache.md new file mode 100644 index 000000000..37e6c3ed3 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/06-controlled-cache.md @@ -0,0 +1,83 @@ +--- +title: 受控的缓存 +sidebar_position: 60 +--- + +:::info 版本要求 + +v2.1.0+ + +::: + +在发送一个请求时,默认会先检查是否存在匹配的缓存数据,匹配到则会使用它作为响应数据进行返回,如果在一些场景下,用户需要使用自定义的缓存就必须先使用`setCache`同步设置缓存数据才能行得通,无疑加大了用户的负担,这是一种不受控的缓存。 + +如果你想要在不受控的缓存下使用**IndexedDB**自定义管理缓存数据,你可能会先为即将发送的请求预先设置命中的缓存,像这样: + +```javascript +const getFile = fileName => { + const fileGetter = alovaInstance.GET(`/file/${fileName}`); + const tx = db.transaction(['files']); + const getRequest = tx.objectStore('files').get(fileName); + getRequest.onsuccess = ({ result }) => { + setCache(fileGetter, result); + }; + return fileGetter; +}; +``` + +**❌ 但并不推荐以上的写法**,原因如下: + +1. 每次调用`getFile`都会设置一次缓存,但 fileGetter 不一定用于发送请求; +2. IndexedDB 是异步接口,如果匹配缓存的步骤发生在 IndexedDB 触发 onsuccess 之前,那么就不会匹配到缓存数据,它们的顺序是不可预知的; +3. 自定义的缓存管理任务和 method 是分开的,但实际上它们应该聚合在一起; + +在这种情况下,你可以使用受控的缓存来解决上面的问题,使用受控缓存也很简单,可以在 method 中的 localCache 设置为异步或同步函数,在这个函数中返回自定义数据作为命中的缓存数据。 + +```javascript +const getFile = fileName => + alovaInstance.GET(`/file/${fileName}`, { + // 受控缓存函数支持异步和同步函数 + localCache() { + return new Promise((resolve, reject) => { + const tx = db.transaction(['files']); + const getRequest = tx.objectStore('files').get(fileName); + getRequest.onsuccess = resolve; + getRequest.onerror = reject; + }); + } + }); +``` + +## 不使用缓存 + +如果你希望继续发送请求,可以在`localCache`中返回 `undefined` 或不返回任何数据,这在自定义管理缓存时未命中缓存的情况下很有用。 + +## 使用 transformData 设置缓存 + +由于 `transformData` 函数具有以下两个特性: + +- 只有在响应时才被触发,而命中响应缓存时不会触发; +- 支持异步函数; + +因此,你还可以配合它保存自定义的缓存,例如以文件为响应数据的缓存场景下,可以配合 IndexedDB 进行文件数据的缓存。 + +```javascript +const fileGetter = alovaInstance.Get('/file/file_name', { + // 使用IndexedDB缓存文件 + async transformData(fileBlob) { + await new Promise((resolve, reject) => { + const tx = db.transaction(['files'], 'readwrite'); + const putRequest = tx.objectStore('files').put({ + file: fileBlob + }); + putRequest.onsuccess = resolve; + putRequest.onerror = reject; + }); + return fileBlob; + } +}); +``` + +## 注意事项 + +在 usehooks 中使用时,在`localCache`函数中抛出错误将会触发`onError`,使用 method 实例直接发起请求时,将会返回一个 reject 状态的 promise 实例。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/README.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/README.md new file mode 100644 index 000000000..b0d7c75bb --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/README.md @@ -0,0 +1,9 @@ +--- +title: 响应缓存 +--- + +响应缓存是一个将服务端返回的数据缓存到客户端的技术,在可重复利用服务端数据时避免重复发送请求,既能立即响应用户请求,也能节省服务端资源。根据不同的缓存场景,alova 提供了 3 种模式,分别为**内存模式、缓存占位模式、恢复模式**,选择适合你的使用即可。 + +此外,使用缓存操作 API,你还可以自由添加、修改和删除缓存,以及自定义缓存匹配规则。 + +接下来,让我们从缓存模式开始理解吧! diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/_category_.json b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/_category_.json new file mode 100644 index 000000000..2360c70d8 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/_category_.json @@ -0,0 +1,3 @@ +{ + "label": "Response Cache" +} diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/01-sensorless-data-interaction/02-virtual-data.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/02-virtual-data.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/01-sensorless-data-interaction/02-virtual-data.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/02-virtual-data.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/01-sensorless-data-interaction/03-start-silent-factory.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/03-start-silent-factory.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/01-sensorless-data-interaction/03-start-silent-factory.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/03-start-silent-factory.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/01-sensorless-data-interaction/04-conservative-request.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/04-conservative-request.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/01-sensorless-data-interaction/04-conservative-request.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/04-conservative-request.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/01-sensorless-data-interaction/05-modify-response.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/05-modify-response.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/01-sensorless-data-interaction/05-modify-response.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/05-modify-response.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/06-request-retry.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/06-request-retry.md new file mode 100644 index 000000000..fb09fe811 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/06-request-retry.md @@ -0,0 +1,144 @@ +--- +title: 步骤3-设置请求重试 +sidebar_position: 60 +--- + +当一个请求进入静默队列后是可以为它设置请求重试参数,来保证它的请求成功率,这在行为模式设置为 **queue** 和 **silent** 时都有效,不同的是,**silent** 行为下的请求默认是持久化的,请求成功前即使刷新也将继续发送请求,而 **queue** 行为下的请求不会被持久化,刷新后将被清除。 + +## 最大重试次数 + +设置最大重试次数,默认不重试。 + +```javascript +useSQRequest(createOrEditTodo, { + // ... + // highlight-start + // 重试次数为3次 + maxRetryTimes: 3 + // highlight-end +}); +``` + +## 请求延迟时间 + +默认情况下,每次间隔 1000ms 重试,我们可以在避让策略中自定义设置每次重试的延迟时间。 + +```javascript +useSQRequest(createOrEditTodo, { + // ... + maxRetryTimes: 3, + // highlight-start + // 每次延迟2000ms进行请求 + backoff: { + delay: 2000 + } + // highlight-end +}); +``` + +如果需要按规则增长的延迟时间,可以再为它设置一个增长倍数,延迟时间将按重试次数指数增长。 + +```javascript +useSQRequest(createOrEditTodo, { + // ... + maxRetryTimes: 3, + backoff: { + delay: 2000, + // highlight-start + // multiplier设置为2时,第一次重试延迟为2秒,第二次为4秒,第三次为8秒,以此类推 + multiplier: 2 + // highlight-end + } +}); +``` + +还不够?你甚至还可以为每次延迟时间增加随机抖动值,让它看着不那么规律 + +```javascript +useSQRequest(createOrEditTodo, { + // ... + maxRetryTimes: 3, + backoff: { + delay: 2000, + multiplier: 2, + // highlight-start + /** + * 延迟请求的抖动起始百分比值,范围为0-1 + * 当只设置了startQuiver时,endQuiver默认为1 + * 例如设置为0.5,它将在当前延迟时间上增加50%到100%的随机时间 + * 如果endQuiver有值,则延迟时间将增加startQuiver和endQuiver范围的随机值 + */ + startQuiver: 0.5, + + /** + * 延迟请求的抖动结束百分比值,范围为0-1 + * 当只设置了endQuiver时,startQuiver默认为0 + * 例如设置为0.8,它将在当前延迟时间上增加0%到80%的随机时间 + * 如果startQuiver有值,则延迟时间将增加startQuiver和endQuiver范围的随机值 + */ + endQuiver: 0.8; + // highlight-end + } +}); +``` + +## 重试判定规则 + +默认情况下,只要请求失败都将会重试,请求失败分为以下情况: + +1. 请求错误,并且未被全局的`onError`钩子捕获错误; +2. 请求成功,但在全局的`onSuccess`钩子中抛出了错误; + +但实际情况下,并不是所有的请求都需要重试,例如当遇到服务端错误,或网络断开时不应该重试,此时就需要设置重试判定规则。当请求失败时通常会获得一个 `Error` 实例,我们可以设置正则表达式对 `error.message` 或 `error.name` 进行匹配,匹配通过则不再重试。 + +```javascript +useSQRequest(createOrEditTodo, { + // ... + // highlight-start + // 当抛出的错误name为500,或者错误的message匹配network error时不再重试 + retryError: { + name: /^500$/, + message: /network error/i + } + // highlight-end +}); +``` + +你也可以设置其中一个匹配规则,当只设置 `message` 的匹配规则时,可以直接简写为正则表达式。 + +```javascript +// 只设置匹配错误的name +useSQRequest(createOrEditTodo, { + // ... + retryError: { + name: /^500$/ + } +}); + +// 只设置匹配错误的message +useSQRequest(createOrEditTodo, { + // ... + retryError: /network error/i +}); +``` + +为了不污染错误信息,通常情况下我们将会把服务端返回的错误代码放在 `error.name` 中,当然,你也可以拼接到 `error.message` 中,以 Response 的错误处理示例如下: + +```javascript +const alovaInst = createAlova({ + // ... + responded: { + onSuccess(response) { + // 500错误时抛出错误 + if (response.status === 500) { + const error = new Error(response.statusText); + error.name = response.status; + throw error; + } + return response.json(); + } + } +}); +``` + +下一步,将会使用到保存的操作记录,对列表数据进行数据补偿,来达到最新状态。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/01-sensorless-data-interaction/07-data-compensation.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/07-data-compensation.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/01-sensorless-data-interaction/07-data-compensation.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/07-data-compensation.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/01-sensorless-data-interaction/08-edit-item.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/08-edit-item.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/01-sensorless-data-interaction/08-edit-item.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/08-edit-item.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/01-sensorless-data-interaction/09-what-more.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/09-what-more.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/01-sensorless-data-interaction/09-what-more.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/09-what-more.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/README.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/README.md new file mode 100644 index 000000000..ff66e617e --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/README.md @@ -0,0 +1,146 @@ +--- +title: 无感数据交互 - 概览 +--- + +无感数据交互是指用户在与应用进行交互时,无需等待即可立即展示相关内容,或者提交信息时也无需等待即可展示操作结果,就像和本地数据交互一样,从而大幅提升应用的流畅性,它让用户感知不到数据传输带来的卡顿。 + +这并不是新鲜事,早在 2015 年以前就已经有乐观更新的概念了,它是指在服务端响应前将提交结果展示到界面中,它是建立在大部分的提交都成功的假设上,与之相对的是保守更新,即服务端响应前将展示等待状态,直到请求完成。在处理失败方面,目前的乐观更新方案通常是通过回退来处理,例如下面的示例代码: + +```javascript +const list = []; +const data = {}; +addTodo(data).catch(() => { + list = list.filter(item => item !== data); +}); +list.push(data); +``` + +这可能会导致以下问题: + +1. 回退将增加用户的理解成本和操作成本; +2. 请求时序问题; +3. 如果后续的请求存在对本次提交依赖,则本次失败将导致后续的请求变得没有意义; +4. 可能丢失请求; + +经过数个月的方案设计和不断迭代,alova 已经在这方面走出了一大步,在我们的方案中对以上存在的问题进行了解决,可以更稳定地保证请求成功,虽然还存在技术限制,但在许多场景中得到了应用。在我们的技术方案中,可以更高限度地降低网络波动带来的问题,你的应用在高延迟网络甚至是断网状态下依然可用,在刷新页面后也依然能保持最新状态的数据。 + +## 应用场景 + +无感数据交互虽然不能大规模地使用,但在一定场景下它又是非常合适的,在探索中,我们发现了至少包含但不限于以下几个场景,供你参考。 + +### 编辑器类应用 + +笔记类应用如印象笔记,画布编辑类应用如 MasterGO,它们分别需要实现以下需求: + +1. 进入笔记或图纸列表时将全量拉取列表数据,下次进入将使用本地的缓存数据; +2. 编辑过程中实时同步到服务端,且同步过程发生在后台,不会影响用户正常使用; +3. 网络差或断网状态下也可以继续使用; + +:::info 示例 + +我们提供了一个[笔记应用示例](/tutorial/example/silent-submit-notes),你可以进入体验。 + +::: + +### 设置模块 + +由常用的开关、选择器组成的设置模块,需实现的需求是,用户操作后实时同步到服务端,同时不再展示提交状态,而是直接展示操作后的最新状态。 + +:::info 示例 + +我们提供了一个[设置页示例](/tutorial/example/silent-submit-setting),你可以进入体验。 + +::: + +### 简单的列表管理 + +我们将创建列表项时填写的数据足够用于列表页的展示,称为简单的列表,例如一个学生列表页展示学生的姓名、性别、年级三个数据,这三个数据在创建学生时都需要填写。在简单列表中将实现以下需求: + +1. 在新增、编辑和删除列表项时立即在列表页中展示最新状态,无需在提交完成后才迟迟展示,同时也不受网络波动限制; +2. 刷新页面时,列表页始终保持最新状态; + +:::info 示例 + +我们提供了一个[简单列表页示例](/tutorial/example/silent-submit-simple-list),你可以进入体验。 + +::: + +### 复杂的列表管理 + +复杂列表是指,创建列表项时填写的数据不足以在列表页用于展示,而需要根据服务端的计算产生额外的数据,例如一个 Todo 列表页除了展示基本信息外,还需要列出具体的执行日期,而在创建页只指定了执行日期范围和相关规则,因此执行日期由服务端根据日期范围和规则统一计算生成。 + +在复杂列表中将实现以下需求: + +1. 在新增、编辑和删除列表项时立即在列表页中展示最新状态,并在服务端响应后将服务端计算的数据更新到此列表项中; +2. 刷新页面时,列表页始终保持最新状态; + +:::info 示例 + +复杂列表示例敬请期待... + +::: + +### 自由模式 + +在以上的几种场景下,你可能希望根据一个条件来判断当前是使用无感交互策略还是最常见的保守请求策略,需求如下: + +1. 当网络状态良好时,或付费用户将使用无感交互策略,而网络波动大时,或免费用户则不能享受到无感交互策略; +2. 策略自由切换; + +:::info 示例 + +在以上的示例中你都可以体验自由切换策略 + +::: + +## 不推荐的应用场景 + +### 信息共享类 + +提交的信息需要同步给其他人,例如订单信息,这类信息具有高实时性的要求,我们应该确保提交成功。 + +### 复杂的数据交互类 + +复杂的数据交互是指,数据的编辑和筛选混合进行,例如对某个列表混合进行新增、编辑、删除和筛选,在这种情况下 alova 目前还无法很好地支持,在后续的版本中也将尝试解决这个难题。 + +## 技术方案 + +在无感数据交互的技术方案上,alova 分别实现了数据预拉取和静默提交,接下来我们来了解这两种技术方案。 + +:::info + +阅读前请确保已经掌握了以下章节内容 + +- [基础学习](/tutorial/getting-started) + +::: + +### 数据预拉取 + +在 html 中你可能见过这样的标签``,它告诉浏览器在闲时去预加载样式文件,并放在缓存中,当真正需要使用的时候从缓存中取出即可,alova 也使用了类似的方案,通过[useFetcher](/tutorial/advanced/use-fetcher)来预拉取需要的数据,它将保存在本地缓存中。你可以在任意情况下预判用户的需要阅读的内容,然后预先拉取对应的内容,如在流程类的页面中可以预加载下一页的内容,又或者,用户在某个按钮上停留了 200ms,我们可以预先拉取下一个界面需要的数据,这点类似于**Next.js**的页面预加载。 + +我们提供了一个[预加载的示例](/tutorial/example/prefetch),你可以进入体验。 + +### 静默提交 + +静默提交是一种提交即响应的机制,方案中将保证提交的完成,因此可以将它看作更安全的乐观更新方案。静默提交主要通过**静默队列**来持久化请求信息,以及保证请求时序问题,通过**虚拟数据**来作为服务端响应数据的占位符,当请求完成后替换为实际的响应数据,通过这两项技术实现了本地化的数据创建,并对新建数据进行编辑、删除等操作,即使创建的数据还未真正在服务端提交成功。为了让开发成本降到最低,这些在 alova 中都是自动完成的。 + +### 静默队列 + +静默队列用于保证请求时序问题,我们可以任意创建队列,进入队列的请求都将会以**SilentMethod**实例的形式保存在队列中,每个**SilentMethod**除了包含请求信息外,还包含静默提交的相关配置,如*唯一 id*、*错误重试参数*等。队列内的请求只有在前一个响应后才会发起下一个请求,从而保证队列内的请求时序。你可以将有依赖关系的请求放到同一个队列中,这样也可以保证数据的一致性。 + +![静默队列](https://user-images.githubusercontent.com/29848971/220057005-dd467392-4a43-45a7-90dc-999dd1d95531.png) + +在方案中,还分别提供了`queue`、`silent`、`static`三种行为模式,用于区分一个请求需要以怎样的行为进行请求。 + +- queue:请求将进入静默队列,但不会被持久化,将等待前面的请求完成才发送请求,响应回调将会在响应后触发,一般用于需要依赖前项请求的数据获取; +- silent:请求将进入静默队列,且会被持久化,然后立即触发响应回调,这种行为模式下,onSuccess 将接收到虚拟数据,onError 永远不会被触发,在进行提交即响应的场景下需使用此模式; +- static:请求不会进入静默队列,也不会被持久化,它会立即发出请求,在禁用静默提交时可使用此模式; + +### 虚拟数据 + +在提交即响应的机制中,虚拟数据起到了重要的作用,它表示在服务端真正响应之前,作为响应数据的替代数据进行占位,并通过追查机制,虚拟数据即使分布在应用各个位置,也能在响应后自动替换为实际的响应数据。同时在静默队列中也起到了重要作用,它可以标识队列内请求的依赖关系,并在依赖项响应后将依赖数据替换为实际数据,例如创建一条数据时将返回这条数据的 id,当服务端还未响应时,用户又进行了删除操作,需要将 id 作为删除标识,此时删除请求将依赖创建请求。在创建请求响应前,虚拟数据将作为 id 占位符作为删除的参数,并在创建请求响应后替换虚拟数据 id,至此就可以完成删除的请求。 + +![虚拟数据](https://user-images.githubusercontent.com/29848971/220005505-d30b7ae2-ddd0-4080-81a4-65c9be2cb0bd.png) + +接下来,我们将会具体了解虚拟数据的特性。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/_category_.json b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/_category_.json new file mode 100644 index 000000000..68d7d2cf7 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/_category_.json @@ -0,0 +1,3 @@ +{ + "label": "无感数据交互" +} diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/02-usePagination.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/02-usePagination.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/02-usePagination.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/02-usePagination.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/03-useForm.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/03-useForm.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/03-useForm.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/03-useForm.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/04-tokenAuthentication.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/04-tokenAuthentication.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/04-tokenAuthentication.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/04-tokenAuthentication.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/05-useUploader.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/05-useUploader.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/05-useUploader.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/05-useUploader.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/06-useAutoRequest.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/06-useAutoRequest.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/06-useAutoRequest.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/06-useAutoRequest.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/07-useBreakpointUploader.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/07-useBreakpointUploader.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/07-useBreakpointUploader.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/07-useBreakpointUploader.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/08-useCaptcha.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/08-useCaptcha.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/08-useCaptcha.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/08-useCaptcha.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/09-actionDelegationMiddleware.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/09-actionDelegationMiddleware.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/09-actionDelegationMiddleware.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/09-actionDelegationMiddleware.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/10-useSerialRequest.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/10-useSerialRequest.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/10-useSerialRequest.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/10-useSerialRequest.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/11-useSerialWatcher.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/11-useSerialWatcher.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/11-useSerialWatcher.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/11-useSerialWatcher.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/12-useRetriableRequest.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/12-useRetriableRequest.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/12-useRetriableRequest.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/12-useRetriableRequest.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/13-useSSE.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/13-useSSE.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/13-useSSE.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/13-useSSE.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/14-rateLimitMiddleware.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/14-rateLimitMiddleware.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/14-rateLimitMiddleware.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/14-rateLimitMiddleware.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/README.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/README.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/README.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/README.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/_category_.json b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/_category_.json similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-strategy/_category_.json rename to i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/_category_.json diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/01-use-fetcher.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/01-use-fetcher.md new file mode 100644 index 000000000..696148d51 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/01-use-fetcher.md @@ -0,0 +1,307 @@ +--- +title: 数据拉取 +sidebar_position: 10 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +当你有以下需求时: + +1. 预加载后续流程中将会使用到的数据并存放在缓存中,让用户不再等待数据加载的过程; +2. 便捷地实现跨页面更新数据(类似全局状态),例如修改 todo 列表的某一项后重新拉取最新数据,响应后将刷新界面。 + +`useFetcher`就是用于实现以上场景的 hook,通过它获取的响应数据不能直接接收到,但通过它拉取的数据除了会更新缓存外还会更新对应的状态,从而重新渲染视图。 + +## 预加载数据 + +我们来实现一个分页列表中,自动预加载下一页数据,在预加载数据前请确保使用的 Method 实例已开启了缓存。 + + + + +```html + + + +``` + + + + +```jsx +import { useState } from 'react'; + +// method实例创建函数 +const getTodoList = currentPage => { + return alovaInstance.Get('/todo/list', { + localCache: 60000, + params: { + currentPage, + pageSize: 10 + } + }); +}; + +const App = () => { + const { + // fetching属性与loading相同,发送拉取请求时为true,请求结束后为false + fetching, + error, + onSuccess, + onError, + onComplete, + + // 调用fetch后才会发送请求拉取数据,可以重复调用fetch多次拉取不同接口的数据 + fetch + } = useFetcher({ + updateState: false + }); + const [currentPage, setCurrentPage] = useState(1); + const { data, onSuccess } = useWatcher(() => getTodoList(currentPage), [currentPage], { + immediate: true + }); + + // 在当前页加载成功后,传入下一页的method实例,即可预拉取下一页的数据 + onSuccess(() => { + fetch(getTodoList(currentPage + 1)); + }); + + return ( + <> + {fetching ?
Fetching...
: null} + {/* 列表视图 */} + + ); +}; +``` + +
+ + +```html + + +{#if fetching} +
Fetching...
+{/if} + +``` + +
+ + +```html + + + +``` + + +
+ +:::warning + +以上示例在调用`useFetcher`时设置了`updateState`为 false,这是因为默认情况下 fetch 时会自动触发跨组件更新状态,导致视图重新渲染,在预拉取的数据与当前请求的数据为同一份`data`时可以关闭它,以免影响视图错误。 + +::: + +## 跨模块/组件更新视图 + +下面我们来实现修改一条 todo 数据,并重新拉取最新的 todo 列表数据,让视图更新。我们可能并不知道 todo 列表当前位于第几页,此时在使用`fetch`函数时可以使用[method 匹配器](/tutorial/advanced/method-matcher)来动态拉取当前页的数据。 + +> method 匹配器是用于,在已请求过的 method 实例中,查找符合条件的 method 实例。 + +首先,为 todo 列表的 method 实例设置名称,用于在无法直接指定 Method 实例时,过滤出需要的 Method 实例。 + +```javascript title="api/todoList.js" +const getTodoList = currentPage => { + return alovaInstance.Get('/todo/list', { + // highlight-start + name: 'todoList', + // highlight-end + params: { + currentPage, + pageSize: 10 + } + }); +}; +``` + +然后在`EditTodo`组件中,通过`fetch`函数在已请求过的 Method 实例中,动态查找最后一个 name 为`todoList`进行数据拉取。 + +```javascript title="EditTodo Component" +const { fetch } = useFetcher(); + +// 在事件中触发数据拉取 +const handleSubmit = () => { + // 提交数据... + await fetch({ + name: 'todoList', + filter: (method, index, ary) => { + // 返回true来指定需要拉取的Method实例 + return index === ary.length - 1; + } + }); +}; +``` + +:::warning 注意事项 + +useFetcher 请求完成后只更新缓存,且如果发现这个 Method 实例在之前使用过 useHook 请求过,那么也会更新这个 useHook 创建的`data`状态,从而保证页面数据一致,这是`useFetcher`用于跨模块/组件更新视图的保证。 + +::: + +> 更多 method 匹配器的使用方法见 [method 匹配器](/tutorial/advanced/method-matcher)。 + +## 强制发送请求 + +和`useRequest`和`useWatcher`相同,更多请阅读[强制请求](/tutorial/cache/force-request)。 + +## 绑定响应回调 + +useFetcher 也支持绑定`onSuccess/onError/onComplete`回调函数。 + +```javascript +const { onSuccess, onError, onComplete } = useFetcher(); +``` + +具体请阅读[响应处理](/tutorial/combine-framework/response)。 + +## send 函数参数传递规则 + +与`useRequest`和`useWatcher`不同的是,fetch 函数的自定义参数是从第二个参数开始的,它们也将分别被事件回调和`force`函数接收。 + +```javascript +const { onSuccess, fetch } = useFetcher(); +onSuccess(({ sendArgs }) => { + // sendArgs的值为['test arg'] +}); + +fetch(getTodoList(), 'test arg'); +``` + +具体请阅读[send 函数参数传递规则](/tutorial/combine-framework/receive-params)。 + +## useRequest 与 useFetcher 对比 + +1. useFetcher 不返回`data`字段,预拉取的数据将保存在缓存中,以及更新对应位置的状态数据; +2. 将`loading`改名为了`fetching`; +3. 没有`send`函数,但多了一个`fetch`函数,可以重复利用 fetch 函数拉取不同接口的数据,此时你可以使用 `fetching` 和 `error` 状态统一渲染视图,从而达到统一处理的目的; diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/02-update-across-components.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/02-update-across-components.md new file mode 100644 index 000000000..005dba6f1 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/02-update-across-components.md @@ -0,0 +1,102 @@ +--- +title: 跨组件更新状态 +sidebar_position: 20 +--- + +有这个一个场景,当用户点开 todo 列表中的某一项,进入 todo 详情页并对它执行了编辑,此时我们希望上一页中的 todo 列表数据,在不重新情况的情况下也更新为编辑后的内容,`useFetcher`就不再适用了。 + +此时可以使用`updateState`来更新任意模块/页面下的已存在的响应状态,它可以查找并修改其他模块内的响应式状态。 + +[这里有个`updateState`的 示例](/tutorial/example/update-state) + +## 使用 method 实例查找响应状态 + +当确定更新的响应状态对应的 method 实例时,你可以在`updateState`中传入此 method 实例,它将查找这个实例下是否存在对应的响应状态,并在回调函数中提供给你进行修改,最后将修改后的数据返回即可。 + +```javascript +import { updateState } from 'alova'; + +// 正在编辑的todo项 +const editingTodo = { + id: 1, + title: 'todo1', + time: '09:00' +}; + +const { send, onSuccess } = useRequest(createTodoPoster, { immediate: false }); +onSuccess(() => { + // highlight-start + // 固定修改第一页的todo数据数据 + // updateState将返回是否更新成功 + const updated = updateState(getTodoList(1), todoList => { + return todoList.map(item => { + if (item.id === editingTodo.id) { + return { + ...item, + ...editingTodo + }; + } + return item; + }); + }); + // highlight-end +}); +``` + +:::warning 注意 + +1. 通过`updateState`更新状态时,如果检测到缓存(内存缓存和持久化缓存)也将会更新新的数据更新缓存。 +2. 只有当使用 useRequest、useWatcher 发起过请求时,alova 才会管理 hook 返回的状态,原因是响应状态是通过一个 Method 实例来生成 key 并保存的,但在未发起请求时 Method 实例内的 url、params、query、headers 等参数都还不确定。 + +::: + +## 动态更新响应状态 + +可能有时候你并不确定需要更新 method 下的响应状态,但却知道以什么方式来找到需要失效的缓存数据,我们可以使用 [method 匹配器](/tutorial/advanced/method-matcher) 来动态查找对应的 method 实例。以下例子展示了为名称为 todoList 的 method 实例对应的列表添加一条数据。 + +```javascript +updateState('todoList', todoListRaw => { + todoListRaw.push({ + title: 'new todo', + time: '10:00' + }); + return todoListRaw; +}); +``` + +关于 [method 匹配器](/tutorial/advanced/method-matcher) 将在后面的章节中详细介绍。 + +## 监听匹配事件 + +在动态更新响应状态时,有时候你可能想要匹配到 method 实例时做一些处理,或者想要获取匹配的 method 实例,`updateState`还可以传入第三个参数来设置匹配事件来达到这些目的。 + +```javascript +updateState( + 'todoList', + todoListRaw => { + // ... + }, + { + // 匹配到method实例时调用,参数为匹配到的method实例 + onMatch: method => { + // ... + } + } +); +``` + +:::warning ⚠️ 请确保组件未销毁 + +`updateState`默认会查找由 alova 的 useHooks 发送请求时所创建的响应状态,但由于防止内存溢出,一个组件的销毁同时也会回收它内部创建的所有状态,因此在使用`updateState`时请确保你希望更新的响应状态对应的容器组件未被销毁,否则将无法查找到对应的响应状态而导致更新失败。 + +这个问题常常出现在跨页面更新状态,我们容易忽略的是,在默认情况下,当页面跳转时上一个页面已被销毁,因此,如果你希望跨页面更新状态,这里有两个建议: + +1. 将页面组件持久化,以保证被更新的状态还可以被查找到; +2. 使用 [setCache](/tutorial/cache/set-and-query) 替代`updateState`,其原理是,当上一个页面的请求存在缓存时,更新它的缓存以保证再次创建页面时,所触发的请求可以命中更新后的缓存,达到同样的效果。 + +::: + +## 注意事项 + +1. 在实际使用中,不管是使用`useRequest`还是`useWatcher`发送请求时,你都可以调用`send`函数来指定不同参数重复发送请求,这些 use hook 返回的响应状态会被多个 method 实例引用,因此你可以选择任意一个 method 实例都可以匹配到同一个响应状态值; +2. 当动态查找更新响应状态时,method 匹配器找到了多个 method 实例,将会以第一个实例为准; diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/03-method-matcher.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/03-method-matcher.md new file mode 100644 index 000000000..50ceec7ee --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/03-method-matcher.md @@ -0,0 +1,141 @@ +--- +title: method匹配器 +sidebar_position: 30 +--- + +method 匹配器是一个在已请求的 method 快照列表中动态查找 method 实例的方法。它一般用于,开发者不确定具体使用哪个 method 时,可以使用 method 匹配器按一定的规则查找。 + +## 匹配规则 + +当使用 method 实例请求时,它将被作为快照保存起来,method 匹配器依据 method 实例设置的`name`属性在这些 method 快照中进行查找,多个匹配器允许设置相同的`name`。 + +method 实例匹配类型如下: + +```typescript +type MethodFilter = + | string + | RegExp + | { + name: string | RegExp; + filter: (method: Method, index: number, methods: Method[]) => boolean; + + // 可选参数,如果传入alova对象则只匹配此alova所创建的Method实例,否则匹配所有alova实例的Method实例 + alova?: Alova; + }; +``` + +在以下函数中都可以使用 method 实例匹配器。 + +- [setCache](/tutorial/cache/set-and-query) +- [queryCache](/tutorial/cache/set-and-query) +- [invalidateCache](/tutorial/cache/manually-invalidate) +- [updateState](/tutorial/advanced/update-across-components) +- [useFetcher.fetch](/tutorial/advanced/use-fetcher) + +## 通过 name 属性匹配 + +通过传入完整的实例名称进行匹配,它的匹配结果是一个数组。 + +```javascript +// 每次调用getTodoList时都会生成一个新的method实例,它们的name是相同的 +const getTodoList = currentPage => + alova.Get('/todo/list', { + // highlight-start + name: 'todoList' + // highlight-end + // ... + }); + +// 以下表示让name为'todoList'的所有Method实例的缓存失效 +invalidateCache('todoList'); +``` + +## 通过正则表达式匹配 + +通过传入正则表达式进行匹配,method 实例的 name 符合正则表达式的都将匹配,它的结果也是一个数组。 + +```javascript +// 以下表示让name为以'todo'开头的所有Method实例的缓存失效 +invalidateCache(/^todo/); +``` + +## 过滤匹配结果 + +通过指定`filter`来进一步过滤不满足条件的 method 实例,filter 函数使用与 Array.prototype.filter 相同,返回 true 表示匹配成功,返回 false 表示失败,详见上面的类型声明。 + +让我们来看几个例子。 + +**让特定名称的最后一个 method 实例的缓存失效** + +```javascript +invalidateCache({ + name: 'todoList', + filter: (method, index, methods) => index === methods.length - 1 +}); +``` + +**设置由`alovaInst`创建的,特定名称的最后一个 method 实例的缓存** + +```javascript +setCache( + { + name: /^todo/, + filter: (method, index, methods) => index === methods.length - 1, + + // 如果传了alova参数,那么只匹配由此alova实例创建的Method实例,否则会在所有Method实例中匹配 + alova: alovaInst + }, + newCache +); +``` + +**重新拉取 todo 列表最后一次请求的数据** + +```javascript +const { fetch } = useFetcher(); +fetch({ + name: 'todoList', + filter: (method, index, methods) => index === methods.length - 1 +}); +``` + +> alova 参数可以进一步缩小匹配范围。 + +## 在不同函数中使用的区别 + +### invalidateCache + +应用所有匹配的 Method 实例集合,即失效所有匹配的 Method 实例对应的缓存。 + +### setCache + +应用所有匹配的 Method 实例集合,当传入静态数据时所有的 Method 实例缓存设置为相同值,传入回调函数时将循环调用此函数,并将返回值作为缓存数据。 + +### updateState + +应用第一个匹配的 Method 实例。 + +### fetch + +应用第一个匹配的 Method 实例,即只会拉取一次数据。 + +## 限制实例快照 + +`[v2.20.0+]`默认情况下,会保存 1000 个 method 实例快照,否则在频繁的请求场景下可能导致内存溢出,你也可以根据需要调整限制数量。 + +```js +import { globalConfig } from 'alova'; + +globalConfig({ + // 限制保存500个实例快照 + limitSnapshots: 500 +}); +``` + +当设置为 0 时将不再保存实例快照,此时也将无法使用 method 匹配器。 + +```js +globalConfig({ + limitSnapshots: 0 +}); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/04-middleware.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/04-middleware.md new file mode 100644 index 000000000..e6870112a --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/04-middleware.md @@ -0,0 +1,405 @@ +--- +title: 请求中间件 +sidebar_position: 40 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +请求中间件是一个异步函数,它提供了强大的,几乎能控制一个请求的所有行为的能力。如果你只是使用 alova,那你应该很可能不需要使用请求中间件,因为它主要用于完成自定义的请求策略,无论简单还是复杂的请求策略,可能你都会用上它,接下来我们看下它到底有什么神通。 + +## 中间件函数 + +请求中间件是一个异步函数,你可以在`useRequest`、`useWatcher`、`useFetcher`中定义请求中间件。以下是一个简单的请求中间件,它在请求前和请求后分别打印了一些信息,没有改变任何请求行为。 + +```javascript +useRequest(todoList, { + async middleware(_, next) { + console.log('before request'); + await next(); + console.log('after requeste'); + } +}); +``` + +这里有几点你需要知道的,有关`next`函数调用的问题,这个函数也是一个异步函数,调用它可以继续发送请求,此时将会把 _loading_ 状态设置为 true,然后发送请求。next 的返回值是带有响应数据的 Promise 实例,你可以在中间件函数中操纵返回值。 + +## 控制响应数据 + +中间件函数的返回值将作为本次请求的响应数据参与后续的处理,如果中间件没有返回任何数据但调用了 `next`,则会将本次请求的响应数据参与后续处理。 + +```javascript +// 将会以修改后的result作为响应数据 +useRequest(todoList, { + async middleware(_, next) { + const result = await next(); + result.code = 500; + return result; + } +}); + +// 将会以本次请求的响应数据参与后续处理 +useRequest(todoList, { + async middleware(_, next) { + await next(); + } +}); + +// 将会以字符串abc作为响应数据 +useRequest(todoList, { + async middleware(_, next) { + await next(); + return 'abc'; + } +}); +``` + +这里还有一个特例,当既没有调用 `next`,又没有返回值时,将不再执行后续的处理,这表示*onSuccess*、_onError_、*onComplete*响应事件不会被触发。 + +```javascript +useRequest(todoList, { + async middleware() {} +}); +``` + +## 更改请求 + +有时候你想要更改请求,此时可以在 `next` 中指定另一个 method 实例,在发送请求时就会将这个 method 中的信息进行请求,同时你还可以通过 `next` 设置是否强制请求来穿透缓存,这也很简单。 + +```javascript +useRequest(todoList, { + async middleware(_, next) { + await next({ + // 更改请求的method实例 + method: newMethodInstance, + + // 本次是否强制请求 + force: true + }); + } +}); +``` + +## 控制错误 + +### 捕获错误 + +在中间件中,可以捕获 `next` 中产生的请求错误,捕获后,全局的`onError`钩子不再触发。 + +```javascript +useRequest(todoList, { + async middleware(_, next) { + try { + await next(); + } catch (e) { + console.error('捕获到错误', e); + } + } +}); +``` + +### 抛出错误 + +当然,也可以在中间件中抛出一个自定义错误,即使请求正常也将会进入请求错误的流程。 + +```javascript +// 未发出请求,同时还会触发全局的以及请求级的onError,如果是通过`method.send`发送的请求将返回reject的promise实例 +useRequest(todoList, { + async middleware(_, next) { + throw new Error('error on before request'); + await next(); + } +}); + +// 请求成功后,将触发全局的以及请求级的onError,如果是通过`method.send`发送的请求将返回reject的promise实例 +useRequest(todoList, { + async middleware(_, next) { + await next(); + throw new Error('error on after request'); + } +}); +``` + +## 控制响应延迟 + +在中间件中我们可以延迟响应,也可以提前响应,在提前的情况下,虽然获取不到响应数据,但可以返回一些其他的数据作为响应数据参与后续的处理。 + +```javascript +// 延迟1秒响应 +useRequest(todoList, { + async middleware(_, next) { + await new Promise(resolve => { + setTimeout(resolve, 1000); + }); + return next(); + } +}); + +// 立即响应,并使用字符串abc作为响应数据 +useRequest(todoList, { + async middleware(_, next) { + return 'abc'; + } +}); +``` + +## 不止于此 + +**至此,我们所提及的都是中间件的第二个参数 `next` 的使用,那第一个参数是做什么的呢?** + +中间件第一个参数中包含了本次请求的一些信息,以及对`loading`、`data`和`onSuccess`等 useHook 中返回的状态和事件的控制函数。我们接着往下看! + +## 包含的请求信息 + + + + +以下为 useRequest 和 useWatcher 的中间件所包含的请求信息 + +```javascript +async function alovaFrontMiddleware(context, next) { + // 本次请求的method实例 + context.method; + + // send函数发送的参数数组,默认为[] + context.sendArgs; + + // 本次请求命中的缓存数据 + context.cachedResponse; + + // useHook的配置集合 + context.config; + + // useHook返回的各项状态,包含以下属性 + // loading、data、error、downloading、uploading,以及通过managedStates管理的额外状态 + context.frontStates; + // ... +} +``` + + + + +以下为 useFetcher 的中间件所包含的请求信息 + +```javascript +async function alovaFetcherMiddleware(context, next) { + // 本次请求的method实例 + context.method; + + // 由useFetcher的fetch传入的参数组,默认为[] + context.fetchArgs; + + // 本次请求命中的缓存数据 + context.cachedResponse; + + // useHook的配置集合 + context.config; + + // useHook返回的各项状态,包含以下属性 + // fetching、error、downloading、uploading + context.fetchStates; + // ... +} +``` + + + + +接下来,我们再来看看有哪些控制能力。 + +## 修改响应式数据 + +使用`context.update`修改响应式数据。 + + + + +```javascript +async function alovaFrontMiddleware(context, next) { + context.update({ + // 提前修改loading状态为true + loading: true, + + // 修改data值,如设置自定义的初始化数据 + data: { + /* ... */ + } + }); + // ... +} +``` + + + + +```javascript +async function alovaFetcherMiddleware(context, next) { + context.update({ + // 提前修改fetching状态为true + fetching: true, + + // 修改error的值 + error: new Error('custom midleware error') + }); + // ... +} +``` + + + + +## 装饰事件 + +你还可以在中间件中装饰*onSuccess*、_onError_、*onComplete*回调函数,让它们变得更丰富,例如改变回调函数的参数,又或者接收回调函数的返回值,实现更多的功能。 + +你可以使用`decorateSuccess`、`decorateError`、`decorateComplete`函数来装饰回调函数。下面将成功回调作为示例,它装饰了 3 处地方: + +1. 为 event 对象新增了`custom`属性; +2. 为成功回调函数新增了第二个参数,值为`extra data`; +3. 接收第二个成功回调函数的值,并打印它; + +```javascript +const { onSuccess } = useRequest(todoList, { + // ... + async middleware(context, next) { + // 装饰成功回调函数,以下函数参数解释: + // handler: 绑定的回调函数 + // event: 回调函数对应的事件对象 + // index: 回调函数下标,表示当前正在执行第几个回调函数 + // length: 回调函数绑定个数 + context.decorateSuccess((handler, event, index, length) => { + event.custom = 1; + const received = handler(event, 'extra data'); + if (index === 1) { + console.log(`接收到第${index + 1}个回调函数的返回值:`, received); + // [打印信息] 接收到第2个回调函数的返回值:I'm second handler + } + }); + // ... + } +}); +onSuccess((event, extra) => { + console.log(event.custom); // 1 + console.log(extra); // extra data +}); +onSuccess((event, extra) => { + return "I'm second handler"; +}); +``` + +`decorateError`、`decorateComplete`的用法与`decorateSuccess`相同。 + +## 中断或重复发送请求 + +在中间件中还可以接收到 use hooks 返回的`abort`和`send`函数(useFetcher 中为`fetch`),你还可以在触发一次请求意图时,发送多个请求。 + +典型的使用例子是请求重试,发送一次请求后如果请求失败将自动按一定策略再次请求,重试成功后再触发`onSuccess`。以下为简单的请求重试示例代码。 + + + + +```javascript +async function alovaFrontMiddleware(context, next) { + return next().catch(error => { + if (needRetry) { + setTimeout(() => { + context.send(...context.sendArgs); + }, retryDelay); + } + return Promise.reject(error); + }); +} +``` + + + + +```javascript +async function alovaFetcherMiddleware(context, next) { + return next().catch(error => { + if (needRetry) { + setTimeout(() => { + context.fetch(context.method, ...context.fetchArgs); + }, retryDelay); + } + return Promise.reject(error); + }); +} +``` + + + + +如果需要在中间件内中断请求,可以调用`context.abort()`。 + +## 受控的加载状态 + +在上面内容中,我们知道了可以通过`context.update`自定义修改响应式数据,不过当你在修改加载状态值(`loading`或`fetching`)时将会有所阻碍,因为在正常情况下,加载状态值会在调用`next`时自动设置为 true,在响应流程中自动设置 false,这将覆盖通过`context.update`修改的加载状态值,此时我们可以开启受控的加载状态,开启后,在`next`函数和响应流程将不再修改加载状态值,而由我们完全控制。 + +我们还是以请求重试为例,我们希望在触发一次请求意图开始,经过请求重试直到请求结束为止,加载状态一直保持为 true。 + + + + +在 useRequest 和 useWatcher 的中间件中,使用`context.controlLoading`开启自定义控制加载状态。 + +```javascript +async function alovaFrontMiddleware(context, next) { + context.controlLoading(); + + // 请求开始时设置为true + context.update({ loading: true }); + return next() + .then(value => { + // 请求成功后设置为false + context.update({ loading: false }); + return value; + }) + .catch(error => { + if (needRetry) { + setTimeout(() => { + context.send(...context.sendArgs); + }, retryDelay); + } else { + // 不再重试时也设置为false + context.update({ loading: false }); + } + return Promise.reject(error); + }); +} +``` + + + + +在 useFetching 的中间件中,使用`context.controlFetching`开启自定义控制加载状态。 + +```javascript +async function alovaFetcherMiddleware(context, next) { + context.controlFetching(); + + // 请求开始时设置为true + context.update({ fetching: true }); + return next() + .then(value => { + // 请求成功后设置为false + context.update({ fetching: false }); + return value; + }) + .catch(error => { + if (needRetry) { + setTimeout(() => { + context.fetch(context.method, ...context.fetchArgs); + }, retryDelay); + } else { + // 不再重试时也设置为false + context.update({ fetching: false }); + } + return Promise.reject(error); + }); +} +``` + + + diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/05-custom-method-key.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/05-custom-method-key.md new file mode 100644 index 000000000..4457c40a1 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/05-custom-method-key.md @@ -0,0 +1,26 @@ +--- +title: 自定义method key +sidebar_position: 50 +--- + +:::info version required + +v2.20.0+ + +::: + +method key 用来标识一切与 method 实例关联的数据,有很大的作用,例如: + +- 关联响应数据的缓存 +- 标识共享请求 +- 关联 useRequest 等 useHook 返回的状态值 + +在默认情况下,method key 由 method 实例的相关请求参数生成,它可以准确标识一个请求。 + +但有时候你希望改变它,让以上三个情况在不同的请求中也可以被识别为同一个 method。 + +```javascript +// method key在创建时生成,可以通过__key__自定义它 +const methodInst = alovaInstance.Get('/api/user', {}); +methodInst.__key__ = 'my-custom-method-key'; +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/06-error-logger.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/06-error-logger.md new file mode 100644 index 000000000..98dd64467 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/06-error-logger.md @@ -0,0 +1,51 @@ +--- +title: 错误日志 +sidebar_position: 60 +--- + +:::info 版本要求 + +v2.6.0+ + +::: + +为了方便调试,当在使用 use hooks 请求或响应处理错误时会默认在控制台打印错误日志,如果你在一些情况下(例如生产环境)不希望打印错误信息或自定义控制打印错误信息,alova 也提供了对它们的支持。 + +## 关闭打印错误日志 + +可在创建 alova 实例时将`errorLogger`设置为`false或null`关闭日志打印。 + +```javascript +const alovaInstance = createAlova({ + // ... + errorLogger: false +}); +``` + +你也可以根据不同的环境动态开启与关闭。 + +```javascript +const alovaInstance = createAlova({ + // ... + // 在开发环境开启错误日志 + errorLogger: process.env.NODE_ENV === 'development' +}); +``` + +## 自定义打印错误日志 + +错误日志默认通过`console.error`进行打印,如果你的项目环境中不支持`console.error`,或者希望收集错误信息,可以将`errorLogger`指定为一个函数自定义处理错误日志。 + +```javascript +const alovaInstance = createAlova({ + // ... + /** + * 自定义的错误日志函数 + * @param error 错误对象 + * @param method 当前的method实例 + */ + errorLogger(error, method) { + reportError(`${method.url}: ${error.message}`); + } +}); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/07-cache-logger.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/07-cache-logger.md new file mode 100644 index 000000000..c871a76df --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/07-cache-logger.md @@ -0,0 +1,60 @@ +--- +title: 缓存命中日志 +sidebar_position: 70 +--- + +:::info 版本要求 + +v2.8.0+ + +::: + +在使用接口缓存时为了便于调试,当请求命中缓存而未发出网络请求时,将默认在控制台打印出命中的缓存信息,这可以解决在使用缓存时的一些困惑。 + +如果你在一些情况下(例如生产环境)不希望打印缓存信息或自定义控制打印缓存信息,alova 也提供了对它们的支持。 + +## 关闭打印缓存命中日志 + +可在创建 alova 实例时将`cacheLogger`设置为`false或null`关闭控制台打印。 + +```javascript +const alovaInstance = createAlova({ + // ... + cacheLogger: false +}); +``` + +你也可以根据不同的环境动态开启与关闭。 + +```javascript +const alovaInstance = createAlova({ + // ... + // 在开发环境开启缓存命中日志 + cacheLogger: process.env.NODE_ENV === 'development' +}); +``` + +## 自定义打印缓存命中日志 + +缓存日志默认通过`console.log`进行打印,如果你的项目环境中不支持`console.log`或其他目的,可以将`cacheLogger`指定为一个函数自定义处理缓存命中的日志。 + +```javascript +const alovaInstance = createAlova({ + // ... + /** + * 自定义的缓存命中日志函数 + * @param response 命中的缓存数据 + * @param method 当前的method实例 + * @param cacheMode 缓存模式 memory或restore + * @param tag restore模式下的tag,只有在对应的缓存设置了tag时有值 + */ + cacheLogger(response, method, cacheMode, tag) { + saveHitCache({ + response, + method, + cacheMode, + tag + }); + } +}); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/08-manage-extra-states.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/08-manage-extra-states.md new file mode 100644 index 000000000..86d4741a8 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/08-manage-extra-states.md @@ -0,0 +1,199 @@ +--- +title: 管理额外的状态 +sidebar_position: 80 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +在之前的[跨页面/模块更新响应状态](/tutorial/advanced/update-across-components)章节中,介绍了如何跨页面或模块更新响应状态,但在此章节中我们只介绍了通过`updateState`更新在`useRequest`和`useWatcher`中返回的`data`状态,data 的值总是和响应数据一致,但在很多情况下我们会使用额外的状态来展示(如状态 A)数据,并在请求成功后将 data 数据附加到额外的状态 A 中,如下拉加载的分页方案。在这种情况下,我们就需要将额外的状态 A 进行管理,便于实现跨页面/模块更新它。 + +## 更新单个状态 + +可以在 use hook 调用时通过`managedStates`管理额外的状态,并在其他模块/页面中调用`updateState`时,自动指定状态名称来更新它。 + + + + +```javascript title="A.vue" +const todoList = page => + alova.Get('/todo', { + name: 'todoList' + }); + +const allTodo = ref([]); +useRequest(todoList, { + // ... + + // highlight-start + // 将allTodo作为额外的状态进行管理 + managedStates: { + allTodo + } + // highlight-end +}); +``` + +```javascript title="B.vue" +const handleSuccess = () => { + // highlight-start + // 传入一个对象并指定状态名来查找 + updateState('todoList', { + allTodo: allTodoData => { + // 新增一条todo项 + allTodoData.push({ + title: 'new todo', + time: '10:00' + }); + return allTodoData; + } + }); + // highlight-end +}; +``` + + + + + +```javascript title="A.jsx" +const PageA = () => { + const todoList = page => + alova.Get('/todo', { + name: 'todoList' + }); + + const [allTodo, setAllTodo] = allTodoState = useState([]); + useRequest(todoList, { + // ... + + // highlight-start + // 将allTodo作为额外的状态进行管理 + managedStates: { + allTodo: allTodoState + } + // highlight-end + }); + + return ( + // ... + ); +} +``` + +```javascript title="B.jsx" +const PageB = () => { + // ... + const handleSuccess = () => { + // highlight-start + // 传入一个对象并指定状态名来查找 + updateState('todoList', { + allTodo: allTodoData => { + // 新增一条todo项 + allTodoData.push({ + title: 'new todo', + time: '10:00' + }); + return allTodoData; + } + }); + // highlight-end + }; + + return ( + // ... + ); +} +``` + + + + + +```javascript title="A.svelte" +// a.svelte +const todoList = page => + alova.Get('/todo', { + name: 'todoList' + }); + +const allTodo = ref([]); +useRequest(todoList, { + // ... + + // highlight-start + // 将allTodo作为额外的状态进行管理 + managedStates: { + allTodo + } + // highlight-end +}); +``` + +```javascript title="B.svelte" +const handleSuccess = () => { + // highlight-start + // 传入一个对象并指定状态名来查找 + updateState('todoList', { + allTodo: allTodoData => { + // 新增一条todo项 + allTodoData.push({ + title: 'new todo', + time: '10:00' + }); + return allTodoData; + } + }); + // highlight-end +}; +``` + + + + +:::info 说明 + +不支持管理额外状态。 + +::: + + + + +## 更新多个状态 + +在上面的例子中我们实现了跨页面对单个`allTodo`状态进行更新,实际上,通过`updateState`的对象描述方式可以同时更新任意多个状态。 + +```javascript +updateState('todoList', { + state1: state1Data => { + // ... + }, + state2: state2Data => { + // ... + }, + state3: state3Data => { + // ... + } + // ... +}); +``` + +需要注意的是,以上 3 个额外的状态在更新前,需要通过`managedStates`属性来管理起来。 + +## data 状态更新的简写 + +当只更新 data 状态时,可以直接传入回调函数即可,而不需要指定为对象。 + +```javascript +updateState('todoList', { + data: dataRaw => { + // ... + } +}); + +// 以下为简写 +updateState('todoList', dataRaw => { + // ... +}); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/09-ssr.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/09-ssr.md new file mode 100644 index 000000000..529595d09 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/09-ssr.md @@ -0,0 +1,228 @@ +--- +title: 服务端渲染(SSR) +sidebar_position: 90 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info 版本要求 + +2.8.0+ + +::: + +## 概述 + +尽管 alova 的定位并不是在 nodejs 中进行请求,但为了可以结合 UI 框架的服务端渲染([Nuxt3.x](https://nuxt.com/) / [Nextjs](https://nextjs.org/) / [sveltekit](https://kit.svelte.dev/)),我们也对它做了适配。尽管例如`Nuxt3.x`、`Sveltekit`中提供了内置的请求功能,但如果你选择使用 alova 的话,你可以同时在服务端和客户端中使用 alova 管理请求,而不是服务端和客户端分别使用不同的请求方案来管理它们。 + +这里有一些在 SSR 中使用 alova 需要注意的地方,以及不同 UI 框架的 SSR 中的使用示例。 + +## 在服务端调用接口 + +SSR 中经常需要在服务端获取数据并渲染成 HTML,这种情况下我们不能使用 alova 的 use hooks(也无需使用)来获取数据,以下我们将分别对支持的 SSR 框架进行展示。 + +### Nuxt3.x + +在 Nuxt3.x 中提供了`useAsyncData`在服务端初始化页面数据,同时还提供了`useFetch`和`$fetch`请求函数,这些可以同时在服务端和客户端使用的请求函数真的很方便。尽管如此,如果你希望在 nuxt 中使用 alova 的话,你可以使用 **useAsyncData + alova.Method** 组合的方式完成服务端数据获取,这与你平时使用`useAsyncData`没什么区别。 + +```html + +``` + +### Nextjs + +Nextjs 提供了固定的服务端初始化页面数据的函数,如`getStaticProps`、`getServerSideProps`等,可以在函数中[直接使用 method 实例](/tutorial/getting-started/quick-start)调用接口。 + +```jsx +const todoListGetter = alovaInstance.Get('/todo/list', { + headers: { + 'Content-Type': 'application/json;charset=UTF-8' + } +}); + +export const getServerSideProps = async ctx => { + const list = await todoListGetter.send(); + return { + props: { + list + } + }; +}; +export default function App(props) { + return props.list.map(item => ( +
+ {item.title} + {item.time} +
+ )); +} +``` + +### Sveltekit + +Sveltekit 中也提供了`load`函数进行服务端的页面数据初始化,你同样可以在函数中[直接使用 method 实例](/tutorial/getting-started/quick-start)调用接口。例如在`+page.server.js`中调用接口。 + +```javascript title=+page.server.js +const todoListGetter = alovaInstance.Get('/todo/list', { + headers: { + 'Content-Type': 'application/json;charset=UTF-8' + } +}); + +/** @type {import('./$types').PageServerLoad} */ +export async function load({ params }) { + return { + list: todoListGetter.send() + }; +} +``` + +## 在 SSR 中使用 usehooks + +由于每个 SSR 框架都有各自的在服务端中初始化数据的方式,因此在 SSR 中生成 html 时,组件中的`useRequest`和`useWatcher`即使将`immediate`设置为`true`也不会发起请求,因为这更像是客户端初始化数据。 + +不过,如果你需要像客户端中一样初始化页面的数据,也可以设置`immediate`为`true`,当页面在浏览器中运行时,你可以和往常一样使用 alova 的所有功能。 + +## 注意事项 + +### 客户端和服务端的缓存可能不一致 + +如果你使用了 alova 的缓存功能,这里可能需要注意的是,客户端和服务端的缓存并不是共享的,这意味着如果你在初始化页面时直接使用了**usehooks**获取数据,你可能会遇到客户端和服务端渲染不一致的问题,尽管很少人这样做。 + +请看以下代码片段。 + + + + +```html + + + +``` + + + + +```jsx +function App(props) { + const { loading, data } = useRequest(alovaGetter); + return ( + <> + {loading ?
loading
: null} +
{data}
+ + ); +} +``` + +
+ + +```html + + +{#if $loading} +
loading
+{/if} +
{{ data }}
+``` + +
+
+ +以下代码假设`alovaGetter`请求在服务端存在缓存,但在客户端不存在。 + +此时在服务端生成时 html 时,由于命中缓存,`loading`为`false`而不显示`
loading
`,但在客户端初始化时由于未命中缓存,`loading`为`true`而导致显示`
loading
`,此时 SSR 框架将会提示两个端渲染不一致。 + +**解决方法** + +1. 尽量将页面数据初始化的工作放在获取函数中,而不是组件中; +2. 如果必须这样做,则可以避免在客户端和服务端使用相同的接口,或者关闭出现问题的接口缓存; +3. 如果也需要缓存,你可以在服务端数据初始化函数中清除服务端的缓存,示例代码如下: + + + + +```html + + + +``` + + + + +```jsx +import { invalidateCache } from 'alova'; + +function App(props) { + const { loading, data } = useRequest(alovaGetter); + return ( + <> + {loading ?
loading
: null} +
{data}
+ + ); +} + +export const getServerSideProps = async () => { + // 在服务端中清除缓存 + invalidateCache(alovaGetter); + return { + props: {} + }; +}; +``` + +
+ + +```javascript title=+page.server.js +import { invalidateCache } from 'alova'; + +/** @type {import('./$types').PageServerLoad} */ +export async function load({ params }) { + // 在服务端中清除缓存 + invalidateCache(alovaGetter); + return {}; +} +``` + + +
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/README.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/README.md new file mode 100644 index 000000000..feaeb352d --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/README.md @@ -0,0 +1,9 @@ +--- +title: 进阶 +--- + +import DocCardList from '@theme/DocCardList'; + +进阶教程可以让你更深入地了解 alova 的一些特性,它们可能并不是常用功能,但可以帮你快速解决更多特殊的请求问题。 + + diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/_category_.json b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/_category_.json new file mode 100644 index 000000000..3462df72e --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/_category_.json @@ -0,0 +1,3 @@ +{ + "label": "Advanced" +} diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/01-manage-apis.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/01-manage-apis.md new file mode 100644 index 000000000..28562ab19 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/01-manage-apis.md @@ -0,0 +1,154 @@ +--- +title: 管理APIs +sidebar_position: 10 +--- + +在一个项目中,我们可能需要使用到成百上千个请求 api,因此管理这些请求 api 变得尤为重要。 + +你可能会像 [快速开始](/tutorial/getting-started/quick-start) 中的代码片段那样编写请求发送的代码,所有请求代码写在一个文件中。 + +```javascript +const { loading, data, error } = useRequest( + alovaInstance.Get('https://api.alovajs.org/profile', { + params: { + id: 1 + } + }) +); +``` + +这只是便于初学者理解,但在实际项目中,我们并不推荐这样做,因为 method 实例的用途不仅用于发送请求,它还可以用于操作缓存和状态,上面的用法会让这些请求 api 变得难以管理,如果你认为不对的话,你可能忘记一点: + +> 响应数据缓存的 key 是由 method 实例的请求方法(method)、请求地址(url)、请求头参数(headers)、url 参数(params)、请求体参数(requestBody)组合作为唯一标识,任意一个信息或位置不同都将被当做不同的 key。 + +因此,在实际项目中应该把 method 实例进行管理,也可以统一管理 alova 实例。 + +## api 文件结构 + +首先,你的项目需要一个统一存放 method 实例和 alova 实例的文件夹,例如叫做`api`,以下为一个常见的 api 管理结构,你也可以使用适合项目的任何结构。 + +``` +|-api +| |-index.js -> 包含所有的alova实例 +| |-methods +| | |-user.js +| | |-article.js +| | |-order.js +| | |-... +|-... +``` + +总之,你的项目应该使用适合的文件夹结构将它们管理起来。 + +> 接下来以 vue 为例展示示例代码 + +## 管理 alova 实例 + +你的项目可能需要和不同的服务器通信,也可能需要在特定的请求中使用特殊的请求方案,或者使用不同的响应拦截器等,这些都需要在项目中创建并维护多个 alova 实例,建议可以使用一个单独的文件来管理它们,例如在上面的 api 管理结构中,将使用`api/index.js`来管理。 + +```javascript title=api/index.js +import { createAlova } from 'alova'; +import VueHook from 'alova/vue'; +import GlobalFetch from 'alova/GlobalFetch'; +import { axiosRequestAdapter } from '@alova/adapter-axios'; + +// user alova instance +export const userAlova = createAlova({ + baseURL: 'https://api-user.alovajs.org', + statesHook: VueHook, + requestAdapter: GlobalFetch(), + async responded(method) { + method.config.headers.token = 'user token'; + } +}); + +// order alova instance +export const orderAlova = createAlova({ + baseURL: 'https://api-order.alovajs.org', + statesHook: VueHook, + requestAdapter: GlobalFetch(), + async responded(method) { + method.config.headers.token = 'order token'; + } +}); + +// upload alova instance +export const uploadAlova = createAlova({ + baseURL: 'https://api-order.alovajs.org', + statesHook: VueHook, + requestAdapter: axiosRequestAdapter() +}); +``` + +## 管理 method 实例 + +我们可以使用不同的 js 文件将 method 实例分类管理,例如上面的 api 管理结构中,将使用`api/methods/user.js`来管理用户信息相关的 method 实例,用`api/methods/order.js`管理订单相关的 method 实例。 + +此外,在上文中提到过一点,method 实例除了用于发送请求外,还可以用于操作缓存和状态,为了确保请求参数的个数和顺序,我们可以使用一个函数来对应一个请求 api,通过传入请求参数的形式来返回对应的 method 实例,只要传入参数是相同的,method 实例的请求信息和参数顺序也是相同的,这样就可以确保用于操作缓存和状态的 method 实例不出错。 + +```javascript title=api/methods/user.js +import { userAlova } from '..'; + +// 获取用户信息 +export const getUserInfo = id => userAlova.Get('/user/' + id); + +// 编辑用户信息 +export const editUserInfo = (name, age, mobile) => + userAlova.Post('/user', { + name, + age, + mobile + }); + +// 移除用户 +export const removeUser = id => userAlova.Delete('/user/' + id); + +// ... +``` + +在**user 组件**中可以直接导入 method 函数进行使用,并且可以在调用`invalidateCache`再次使用 method 函数来失效对应的缓存。 + +```html title=views/user.vue + + +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/02-skills.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/02-skills.md new file mode 100644 index 000000000..c1baa555f --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/02-skills.md @@ -0,0 +1,330 @@ +--- +title: 使用技巧 +sidebar_position: 20 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +以下为 alova 开发者在使用 alova 时,所使用的较好的使用技巧,通过多方收集,将它们整理在此,希望对大家可以更顺畅地使用 alova。 + +## 发送请求 useRequest OR method + +alova 提供的`useRequest`在正常情况只会发送一次请求,并获取响应数据,那为什么不直接使用 method 实例来发送请求呢,这是因为`useRequest`可以帮我们自动管理`loading`、`data`、`error`等可以直接使用的响应式数据,因此,如果你需要使用这些状态时,使用`useRequest`不需要自行维护数据。但相反,你并不需要在整个项目中只适用`useRequest`,例如在只关心获取信息,而不需要使用到`loading`、`error`等的时候,在组件外获取数据的时候,可以使用 method 实例来发送请求。 + +## 同时更新状态和缓存 + +当你编辑完一个列表的某条数据时,不希望再次重新请求更新列表数据,而是手动更新列表数据,很多开发者可能会直接修改列表数据。 + + + + +```html + + +``` + + + + + +```jsx +// ... + +const App = () => { + const { data: listData } = useRequest(getList, { + initialData: [] + }); + + // 直接更新了listData + const handleItemSubmit = item => { + const index = listData.findIndex(({ id }) => id === item.id); + listData.splice(index, 1, item); + }; + + return ( + <> + + + + ); +}; +``` + + + + + +```html + + + +``` + + + + +**❌ 不推荐这样的写法** + +这虽然可以触发界面刷新,但可能会带来另一个问题,就是在列表数据开启了缓存的时候,由于缓存数据未被更新,而导致再次进入这个列表页时命中的缓存依然是原来的数据。 + +因此你可以调用 `updateState` 来更新状态化数据的同时,还会立即更新缓存。 + + + + +```html + + +``` + + + + + +```jsx +// ... + +const App = () => { + const { data: listData } = useRequest(getList, { + initialData: [] + }); + + // 通过updateState更新listData,将会同时更新缓存 + const handleItemSubmit = item => { + updateState(getList(), oldListData => { + const index = oldListData.findIndex(({ id }) => id === item.id); + oldListData.splice(index, 1, item); + return oldListData; + }); + }; + + return ( + <> + + + + ); +}; +``` + + + + + +```html + + + +``` + + + + +## 在 onSuccess 中快速获取 sendArgs + +在实际项目中,经常通过`send`函数传递数据,如果你需要在 onSuccess 等回调函数中使用这些数据,由于它们存在于`event.sendArgs`数组中,此时你可以使用双重解构的方式直接获取到数据。 + +```javascript +onSuccess(({ sendArgs: [content] }) => { + console.log(content); +}); +``` + +## 使用前缀管理同类 method 实例 + +在很多场景下,我们需要同时对多个缓存进行失效处理,例如一个页面的数据来自多个接口,当编辑这个页面的数据时需要同时失效这几个接口的缓存数据,你可以将这几个 method 实例使用相同的前缀来分类它们,并使用这个正则表达式将相同前缀的缓存失效。 + +```javascript +const getData1 = id => alovaInstance.Get('/data1', { + name: `data-${id}-1`, + params: { + id + } +}); +const getData2 = alovaInstance.Get('/data2', { + name: `data-${id}-2`, + params: { + id + } +}); +const getData3 = alovaInstance.Get('/data3', { + name: `data-${id}-3`, + params: { + id + } +}); + +const handleInvalidateCache = id => { + // 同时失效指定id的3个缓存数据 + invalidateCache(new RegExp(`^data-${id}`); +} +``` + +## 模拟数据实践 + +如果你的项目,在开发环境下需要使用模拟数据模拟部分或全部接口,在生产切换回真实的网络请求,你可以通过环境变量来控制。 + +```javascript +const globalFetch = GlobalFetch(); +const mockAdapter = createAlovaMockAdapter([mockGroup1 /** ... */], { + httpAdapter: globalFetch, + delay: 1000 +}); + +export const alovaInst = createAlova({ + baseURL: 'http://xxx', + + // 通过环境变量控制生产环境下,不会将mock相关代码打包进去 + requestAdapter: process.env.NODE_ENV === 'development' ? mockAdapter : globalFetch + // ... +}); +``` + +并且推荐团队内不同的开发者可以根据每次迭代的版本号分别创建不同的模拟接口数据,以便于在团队中管理这些模拟数据,具体可参照 [模拟数据](/tutorial/request-adapter/alova-mock) 章节。 + +## 使用 useRequest 并行请求 + +简单的并行请求,只需要同时调用多个 useRequest 即可。 + +```javascript +const { data: todoList } = useRequest(todoListGetter); +const { data: todoCounter } = useRequest(todoCountGetter); +``` + +但这样的请求只适用于单纯的并行请求,如果你需要在并行请求都完成后再进行某些操作,有以下两种方式可以实现: + +### 方法 1 + +手动创建 promise 对象,并使用`Promise.all`完成效果。 + +```javascript +const { data: todoList, onSuccess: onListSuccess, onError: onListError } = useRequest(todoListGetter); +const { data: todoCounter, onSuccess: onCountSuccess, onError: onCountError } = useRequest(todoCountGetter); + +// 手动创建promise对象 +const listPromise = new Promise((resolve, reject) => { + onListSuccess(resolve); + onListError(reject); +}); +const countPromise = new Promise((resolve, reject) => { + onCountSuccess(resolve); + onCountError(reject); +}); +const [listEvent, countEvent] = await Promise.all([listPromise, countPromise]); +// 并行请求完成,继续处理业务... +``` + +### 方法 2 + +使用`useRequest`函数返回的`send`函数,调用`send`将会返回一个可用的 promise 对象。 + +```javascript +// 先让它们不自动发送请求 +const { send: sendList } = useRequest(todoListGetter, { immediate: false }); +const { send: sendCount } = useRequest(todoCountGetter, { immediate: false }); + +// 利用send函数返回的promise对象 +const parallelRequest = async () => { + const [listResponse, countResponse] = await Promise.all([sendList(), sendCount()]); + // 并行请求完成,继续处理业务... +}; +``` + +## 使用 useRequest 串行请求 + +串行请求也具有两种方式。 + +### 方法 1 + +让第一个请求自动发出,第二个请求在第一个请求的`onSuccess`回调中触发,即可完成串行请求,可通过以下写法完成串行请求: + +```javascript +// +const { data: todoList, onSuccess } = useRequest(todoListGetter); +const { data: todoDetail, send: sendTodoDetail } = useRequest(todoId => todoDetailGetter(todoId), { immediate: false }); + +// 先获取列表,再获取第一个todo的详情 +onSuccess(event => { + sendTodoDetail(event.todoList[0].id); +}); +``` + +### 方法 2 + +使用`useRequest`函数返回的`send`函数,调用`send`将会返回一个可用的 promise 对象。 + +```javascript +// 先让它们不自动发送请求 +const { send: sendList } = useRequest(todoListGetter, { immediate: false }); +const { send: sendTodoDetail } = useRequest(todoId => todoDetailGetter(todoId), { immediate: false }); + +// 利用send函数返回的promise对象 +const serialRequest = async () => { + const todoList = await sendList(); + const todoDetail = await sendTodoDetail(todoList[0].id); + // 串行请求完成,继续处理业务... +}; +``` + +> 串行请求建议直接使用[useSerialRequest](/tutorial/strategy/useSerialRequest)和[useSerialWatcher](/tutorial/strategy/useSerialWatcher)。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/03-manage-cache-by-indexeddb.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/03-manage-cache-by-indexeddb.md new file mode 100644 index 000000000..521109e18 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/03-manage-cache-by-indexeddb.md @@ -0,0 +1,123 @@ +--- +title: 使用IndexedDB管理缓存 +sidebar_position: 30 +--- + +如果你正在开发需要大量使用本地缓存的应用,如图形编辑类应用、文件管理类应用等,低容量的 localStorage 已经无法满足开发需求,此时你可以使用 IndexedDB 配合 alova 进行大容量的本地缓存管理。 + +这一功能主要得益于 alova 的 [受控缓存](/tutorial/cache/controlled-cache) 功能,它可以实现自定义的缓存管理,我们来看看实践步骤。 + +这里有一个[使用 IndexedDB 管理缓存的示例](/tutorial/example/controlled-cache-by-indexeddb) + +我们以自定义管理大图片数据为例。 + +## 创建 IndexedDB 实例 + +首先创建一个 IndexedDB 实例用于操作本地缓存,并导出缓存操作的函数。 + +```javascript title=db.js +const dbVersion = 1; +let dbInstance; +const request = window.indexedDB.open('MyTestDatabase', dbVersion); +request.onupgradeneeded = ({ target }) => { + dbInstance = target.result; + const imgStore = dbInstance.createObjectStore('images', { + autoIncrement: true + }); + imgStore.createIndex('fileName', 'fileName', { + unique: true + }); +}; +request.onerror = () => { + throw new Error('Database open fail'); +}; +request.onsuccess = ({ target }) => { + dbInstance = target.result; +}; + +// 新增数据到IndexedDB +export const addImage2Cache = async (fileName, data) => { + const tx = dbInstance.transaction(['images'], 'readwrite'); + const request = tx.objectStore('images').add({ + fileName, + data + }); + return new Promise((resolve, reject) => { + request.onerror = () => { + reject('data add fail'); + }; + request.onsuccess = ({ result }) => { + resolve(result); + }; + }); +}; + +// 根据fileName获取文件数据 +export const getImageFromCache = async fileName => { + const tx = dbInstance.transaction(['images']); + const request = tx.objectStore('images').index('fileName').get(fileName); + return new Promise((resolve, reject) => { + request.onerror = () => { + reject('data add fail'); + }; + request.onsuccess = ({ target }) => { + resolve(target.result); + }; + }); +}; +``` + +## 保存数据 + +在保存数据时,我们可以在 method 的`transformData`中保存缓存,因为`transformData`只会在网络请求响应时被触发,而命中缓存时不会触发的特性。在示例代码中,将图片 blob 实例转换为 base64 数据,缓存并返回这个 base64 数据。 + +```javascript api.js +import { addImage2Cache } from './db'; + +export const image = fileName => + alovaInst.Get(`/image/${fileName}`, { + // highlight-start + async transformData(imgBlob) { + // 将blob异步转换为base64 + const reader = new FileReader(); + reader.readAsDataURL(imgBlob); + const base64Img = await new Promise(resolve => { + reader.onload = ({ target }) => { + resolve(target.result); + }; + }); + + // 缓存image数据到IndexedDB中 + await addImage2Cache(fileName, base64Img); + return base64Img; + } + // highlight-end + }); +``` + +## 获取数据 + +将这个 method 实例的`localCache`指定为一个异步函数,让缓存转变为受控状态,在这个函数中匹配 IndexedDB 中的缓存,如果匹配则返回它,否则返回`undefined`继续发起请求获取数据。 + +```javascript title=api.js +import { getImageFromCache } from './db'; + +export const image = fileName => + alovaInst.Get(`/image/${fileName}`, { + async transformData(imgBlob) { + // ... + }, + + // highlight-start + async localCache() { + // 获取缓存 + const cache = await getImageFromCache(fileName); + return cache && cache.data; + } + // highlight-end + }); +``` + +这样就基本完成了一个基本的自定义缓存管理,你也可以保存缓存的过期时间,并在`localCache`中匹配到缓存时再判断是否已过期,从而实现缓存过期功能。 + +IndexedDB 只是其中一个异步管理缓存的案例,你也可以连接你的缓存服务器来管理它们。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/04-multiple-servers.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/04-multiple-servers.md new file mode 100644 index 000000000..89cfa40c7 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/04-multiple-servers.md @@ -0,0 +1,20 @@ +--- +title: 多服务器 +sidebar_position: 40 +--- + +如果你的项目需要请求多个服务器,可以创建多个 alova 实例来分别对应不同的服务器,为了便于区分不同的环境,也可以使用环境变量来管理多个服务器的 host。 + +```ts +import { createAlova } from '@alova/core'; + +// 创建user相关的alova实例 +const userAlova = createAlova({ + baseURL: VITE_API_USER +}); + +// 创建order相关的alova实例 +const alova2 = createAlova({ + baseURL: VITE_API_ORDER +}); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/05-middleware.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/05-middleware.md new file mode 100644 index 000000000..d8912f57e --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/05-middleware.md @@ -0,0 +1,29 @@ +--- +title: 常用中间件实践 +sidebar_position: 50 +--- + +## 延迟更新 loading + +当响应非常快的时候,加载状态会出现一次闪烁,这给会用户带来糟糕的体验,延迟更新 loading 可以让加载状态在一段时间后才显示,如果在这段时间内完成响应则不会出现加载状态。我们来实现一个带延迟更新 loading 的 middleware。 + +```javascript +const delayLoadingMiddleware = + (delayTimer = 1000) => + async (ctx, next) => { + // 自行控制loading + ctx.controlLoading(); + + // 延迟特定时间更新 + const timer = setTimeout(() => { + ctx.update({ loading: true }); + }, delayTimer); + await next(); + ctx.update({ loading: false }); + clearTimeout(timer); + }; + +useRequest(methodInstance, { + middleware: delayLoadingMiddleware() +}); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/12-parallel-request.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/12-parallel-request.md new file mode 100644 index 000000000..006742dba --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/12-parallel-request.md @@ -0,0 +1,60 @@ +--- +title: 并行请求 +sidebar_position: 120 +--- + +## 使用 method + +由于 method 是 PromiseLike 实例,通过 method 发送并行请求只需要使用`Promise.all`等待即可。 + +```javascript +const [todoList, todoCounter] = await Promise.all[(todoListGetter, todoCountGetter)]; +``` + +## 使用 useRequest + +简单的并行请求,只需要同时调用多个 useRequest 即可。 + +```javascript +const { data: todoList } = useRequest(todoListGetter); +const { data: todoCounter } = useRequest(todoCountGetter); +``` + +但这样的请求只适用于单纯的并行请求,如果你需要在并行请求都完成后再进行某些操作,有以下两种方式可以实现: + +### 方法 1 + +手动创建 promise 对象,并使用`Promise.all`完成效果。 + +```javascript +const { data: todoList, onSuccess: onListSuccess, onError: onListError } = useRequest(todoListGetter); +const { data: todoCounter, onSuccess: onCountSuccess, onError: onCountError } = useRequest(todoCountGetter); + +// 手动创建promise对象 +const listPromise = new Promise((resolve, reject) => { + onListSuccess(resolve); + onListError(reject); +}); +const countPromise = new Promise((resolve, reject) => { + onCountSuccess(resolve); + onCountError(reject); +}); +const [listEvent, countEvent] = await Promise.all([listPromise, countPromise]); +// 并行请求完成,继续处理业务... +``` + +### 方法 2 + +使用`useRequest`函数返回的`send`函数,调用`send`将会返回一个可用的 promise 对象。 + +```javascript +// 先让它们不自动发送请求 +const { send: sendList } = useRequest(todoListGetter, { immediate: false }); +const { send: sendCount } = useRequest(todoCountGetter, { immediate: false }); + +// 利用send函数返回的promise对象 +const parallelRequest = async () => { + const [listResponse, countResponse] = await Promise.all([sendList(), sendCount()]); + // 并行请求完成,继续处理业务... +}; +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/13-serial-request.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/13-serial-request.md new file mode 100644 index 000000000..54fda3717 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/13-serial-request.md @@ -0,0 +1,49 @@ +--- +title: 串行请求 +sidebar_position: 130 +--- + +## 使用 method + +由于 method 是 PromiseLike 实例,可以使用`await`等待请求成功。 + +```javascript +const todoList = await todoListGetter; +const todoDetail = await todoDetailGetter(todoList[0].id); +``` + +## 使用 useRequest + +串行请求也具有两种方式。 + +### 方法 1 + +让第一个请求自动发出,第二个请求在第一个请求的`onSuccess`回调中触发,即可完成串行请求,可通过以下写法完成串行请求: + +```javascript +// +const { data: todoList, onSuccess } = useRequest(todoListGetter); +const { data: todoDetail, send: sendTodoDetail } = useRequest(todoId => todoDetailGetter(todoId), { immediate: false }); + +// 先获取列表,再获取第一个todo的详情 +onSuccess(event => { + sendTodoDetail(event.todoList[0].id); +}); +``` + +### 方法 2 + +使用`useRequest`函数返回的`send`函数,调用`send`将会返回一个可用的 promise 对象。 + +```javascript +// 先让它们不自动发送请求 +const { send: sendList } = useRequest(todoListGetter, { immediate: false }); +const { send: sendTodoDetail } = useRequest(todoId => todoDetailGetter(todoId), { immediate: false }); + +// 利用send函数返回的promise对象 +const serialRequest = async () => { + const todoList = await sendList(); + const todoDetail = await sendTodoDetail(todoList[0].id); + // 串行请求完成,继续处理业务... +}; +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/01-alova-mock.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/01-alova-mock.md new file mode 100644 index 000000000..d53238bcb --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/01-alova-mock.md @@ -0,0 +1,329 @@ +--- +title: 模拟数据 +sidebar_position: 10 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +此 mock 插件是一个 alova 的请求适配器,与传统的 Proxy 形式不同,你可以很好地控制使用 mock 数据的使用范围,你可以控制全局范围、某一组接口范围,甚至是某一个接口的启用和禁用,这在我们实际的业务场景中是很有用的,每一次的迭代都会新增或修改一组接口,我们希望让之前的功能还是走已开发好的接口,而让新增或修改的接口走模拟数据,此时就可以将每个开发人员针对本次迭代涉及到的接口分为一组,并对它们进行开启或关闭。 + +## 特性 + +- ✨ 与 alova 无缝协作 +- ✨ 模拟请求任意分组,可控制全局、组、以及单个模拟接口的启用和禁用 +- ✨ 与 mockjs 配合使用 +- ✨ 不污染生产环境 + +## 安装 + + + + +```bash +npm install @alova/mock --save +``` + + + + +```bash +yarn add @alova/mock +``` + + + + +以下为使用流程。 + +## 使用 + +### 定义 mock 接口 + +使用`defineMock`定义一组 mock 接口,你可以在每一项模拟接口中直接指定返回响应数据,或指定为回调函数动态计算响应数据。 + +```javascript title=mockGrou1.js +import { defineMock } from '@alova/mock'; + +export default defineMock( + { + // 捕获get请求 + '/todo': [1, 2, 3, 4], + + // rest风格请求 + '/todo/{id}': ({ params }) => { + const id = params.id; + // ... + return { + title: '...', + time: '10:00' + }; + }, + + // 捕获post请求 + '[POST]/todo': ({ query, data }) => { + // ... + return { success: true }; + }, + + // 返回更详细的信息 + '[POST]/todo': ({ query, data }) => { + // ... + return { + status: 403, + statusText: 'unknown error', + responseHeaders: { + // ... + }, + body: { + success: true + } + }; + }, + + // 模拟网络错误 + '[POST]/todo': ({ query, data }) => { + throw new Error('network error'); + }, + + // key前面添加`-`,表示禁用此mock接口 + '-[DELETE]/todo/{id}': ({ params }) => { + // ... + return { success: true }; + } + }, + true +); // 第二个参数表示是否启用本组mock接口,默认为true,可以指定为false关闭 +``` + +### 创建模拟请求适配器 + +在调用`createAlova`时创建一个模拟请求适配器,并将 mock 接口传入即可完成。 + +```javascript +import GlobalFetch from 'alova/GlobalFetch'; +import { createAlovaMockAdapter } from '@alova/mock'; +import mockGroup1 from './mockGroup1'; + +// highlight-start +const mockAdapter = createAlovaMockAdapter([mockGroup1, /** ... */], { + // 全局控制是否启用mock接口,默认为true + enable: true, + + // 非模拟请求适配器,用于未匹配mock接口时发送请求 + httpAdapter: GlobalFetch(), + + // mock接口响应延迟,单位毫秒 + delay: 1000, + + // 是否打印mock接口请求信息 + mockRequestLogger: true, + + // 模拟接口回调,data为返回的模拟数据,你可以用它构造任何你想要的对象返回给alova + // 以下为默认的回调函数,它适用于使用GlobalFetch请求适配器 + // 如果你使用的是其他请求适配器,在模拟接口回调中请自定义返回适合适配器的数据结构 + onMockResponse: data => new Response(JSON.stringify(data)) +}); +// highlight-end + +export const alovaInst = createAlova({ + baseURL: 'http://xxx', + + // 使用mock请求适配器,如果需要切换适配器,请看下面的实践建议 + requestAdapter: mockAdapter, + + statesHook: /** ... */ +}); +``` + +### 路径匹配模式 + +:::info 版本要求 + +1.5.0+ + +::: + +默认情况下,在`defineMock`中定义的路径是一个 url 的完整 pathname,看以下代码片段。 + +```javascript +const alovaInst = createAlova({ + baseURL: 'https://api.alovajs.org' + // ... +}); +alovaInst.Get('/user?id=1').send(); +``` + +示例中的请求路径为`https://api.alovajs.org/user?id=1`时,它的完整 pathname 为`/user`,此时可以匹配到`defineMock`中的`/user`。 + +通常情况下这已经足够了,但是当你的 baseURL 不仅仅是一个域名时。 + +```javascript +const alovaInst = createAlova({ + baseURL: 'https://api.alovajs.org/v1/subname' + // ... +}); +alovaInst.Get('/user?id=1').send(); +``` + +这个示例中的请求路径为`https://api.alovajs.org/v1/subname/user?id=1`,mock 的匹配路径为`/v1/subname/user`,需要将 baseURL 中的`/v1/subname`也一同写上,当接口数量较多时就稍显冗余。 + +此时,你可以在`createAlovaMockAdapter`中设置`matchMode`为`methodurl`,它将只匹配 method 实例中定义的 url,例如上面的实例将会匹配到`/user?id=1`,而不再需要写 baseURL 中的部分,相对的,如果 method 实例中的 url 中带了 get 参数时,也需要将它一同写到`defineMock`的匹配路径中,就像这边的`?id=1`。 + +```javascript +createAlovaMockAdapter([mockGroup1 /** ... */], { + // ... + // highlight-start + matchMode: 'methodurl' + // highlight-end +}); +``` + +## 实践建议 + +### 按每个开发者每次版本分组接口 + +在团队开发场景下,每次版本开发时我们经常只需要对部分未开发好的接口进行模拟请求,并且对之前版本的接口使用测试环境接口,此时为了达到更好的模拟接口管理,可以以开发版本和开发者两个维度将接口分组。 + +例如有两个开发者名为 _August_、_kevin_,他们正在开发 v1.1 产品功能,他们可以这样管理模拟接口。 + +```javascript title=August-v1.1.js +import { defineMock } from '@alova/mock'; + +export default defineMock({ + '/todo': [ + /** */ + ], + '[POST]/todo': ({ data }) => { + // ... + // return ... + } + // ... +}); +``` + +```javascript title=kevin-v1.1.js +import { defineMock } from '@alova/mock'; + +export default defineMock({ + '[PUT]/todo/add': ({ data }) => { + // ... + // return ... + }, + '[DELETE]/todo/remove': ({ data }) => { + // ... + // return ... + } + // ... +}); +``` + +```javascript title=request.js +import Augustv1_1 from './August-v1.1'; +import kevinv1_1 from './kevin-v1.1'; + +const mockAdapter = createAlovaMockAdapter([Augustv1_1, kevinv1_1], { + httpAdapter: GlobalFetch(), + delay: 1000 +}); +export const alovaInst = createAlova({ + baseURL: 'http://xxx', + requestAdapter: mockAdapter + // ... +}); +``` + +### 在生产环境中排除 mock 代码 + +mock 数据一般只作用于开发环境,在生产环境下将会切换到实际的接口中,因此这段 mock 代码在生产环境就变得没有作用,此时我们可以通过环境变量的判断来排除这块代码,你只需要这样做: + +```javascript +const globalFetch = GlobalFetch(); +const mockAdapter = createAlovaMockAdapter([mockGroup1, /** ... */], { + httpAdapter: globalFetch, + delay: 1000, +}); + +export const alovaInst = createAlova({ + baseURL: 'http://xxx', + + // highlight-start + // 通过环境变量控制生产环境下,不会将mock相关代码打包进去 + requestAdapter: process.env.NODE_ENV === 'development' ? mockAdapter : globalFetch, + // highlight-end + + statesHook: /** ... */ +}); +``` + +### 与 mockjs 一同使用 + +如果你不希望自己编写模拟数据,而是使用模拟数据库(例如 mockjs)一同使用,你可以这样做。 + +```javascript +import { defineMock } from '@alova/mock'; +import Mock from 'mockjs'; + +export default defineMock({ + '/api1': Mock.mock({ + 'id|1-10000': 100 + }) +}); +``` + +## 转换模拟数据 + +**@alova/mock** 默认将响应数据包装为 Response 实例,将响应头默认包装为 Headers 实例,这是针对`GlobalFetch`进行适配的,但如果使用其他的请求适配器,就需要将模拟数据转换为相应的格式。 + +### 转换响应数据 + +你可以在`onMockResponse`字段中拦截模拟响应数据并返回转换后的响应数据以及响应头。 + +> 你也可以在 onMockResponse 中抛出一个错误,表示请求失败。 + +```javascript +const mockAdapter = createAlovaMockAdapter( + [ + /* 模拟数据 */ + ], + { + // ... + // highlight-start + onMockResponse(response, request, currentMethod) { + // response为相应数据集合,其中包含status、statusText、responseHeaders、body + // request为请求数据,其中包含query、params、headers、data + // currentMethod为当前请求的method实例 + // ... + // 返回转换后的响应数据和响应头 + return { + response: /** 响应数据 */, + headers: /** 响应头 */ + }; + } + // highlight-end + } +); +``` + +### 转换错误对象 + +你可以在`onMockError`字段中拦截错误实例并返回转换后的错误信息。 + +```javascript +const mockAdapter = createAlovaMockAdapter( + [ + /* 模拟数据 */ + ], + { + // ... + // highlight-start + onMockError(error, currentMethod) { + // error为错误实例 + // currentMethod为当前请求的method实例 + // ... + // 返回转换后的错误信息集合 + } + // highlight-end + } +); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/02-alova-adapter-xhr.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/02-alova-adapter-xhr.md new file mode 100644 index 000000000..780837842 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/02-alova-adapter-xhr.md @@ -0,0 +1,337 @@ +--- +title: XMLHttpRequest适配器 +sidebar_position: 20 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## 安装 + + + + +```bash +npm install @alova/adapter-xhr --save +``` + + + + +```bash +yarn add @alova/adapter-xhr +``` + + + + +## 使用方法 + +### 创建 alova + +使用 **xhrRequestAdapter** 作为 alova 的请求适配器。 + +```javascript +import { createAlova } from 'alova'; +import { xhrRequestAdapter } from '@alova/adapter-xhr'; + +const alovaInst = createAlova({ + // ... + requestAdapter: xhrResponseAdapter() + // ... +}); +``` + +### 请求 + +XMLHttpRequest 适配器提供了基本的配置参数,包含`responseType`、`withCredentials`、`mimeType`、`auth`,具体如下: + + + + +```html + +
加载中...
+
请求数据为:{{ data }}
+
+ + +``` + +
+ + +```jsx +const list = () => + alovaInst.Get('/list', { + /** + * 设置响应数据类型 + * 可以设置更改响应类型。 值为:“arraybuffer”、“blob”、“document”、“json”和“text” + * 默认为“json” + */ + responseType: 'text', + + /** + * 当凭证要包含在跨源请求中时为true。 当它们被排除在跨源请求中以及当 cookie 在其响应中被忽略时为 false。 默认为false + */ + withCredentials: true, + + /** + * 设置响应数据的mimeType + */ + mimeType: 'text/plain; charset=x-user-defined', + + /** + * auth 表示使用 HTTP Basic 身份验证,并提供凭据。 + * 这将设置一个 `Authorization` 标头,覆盖任何现有的 + * 使用 `headers` 设置的 `Authorization` 自定义标头。 + * 请注意,只有 HTTP Basic 身份验证可以通过此参数进行配置。 + * 对于 Bearer 令牌等,请改用 `Authorization` 自定义标头。 + */ + auth: { + username: 'name1', + password: '123456' + } + }); + +const App = () => { + const { loading, data } = useRequest(list); + + return ( + { loading ?
加载中...
: null } +
请求数据为:{ JSON.stringify(data) }
+ ) +}; +``` + +
+ + +```html + + +{#if $loading} +
加载中...
+{/if} +
请求数据为:{ data }
+``` + +
+
+ +### 上传 + +使用`FormData`上传文件,这个`FormData`实例会通过`xhr.send`发送到服务端。上传时将自动设置`Content-Type`,不需要自定义设置为`multipart/form-data`。 + +```javascript +const uploadFile = imageFile => { + const formData = new FormData(); + formData.append('file', imageFile); + return alovaInst.Post('/uploadImg', formData, { + // 开启上传进度 + enableUpload: true + }); +}; + +const { + loading, + data, + uploading, + send: sendUpload +} = useRequest(uploadFile, { + immediate: false +}); + +// 图片选择事件回调 +const handleImageChoose = ({ target }) => { + sendUpload(target.files[0]); +}; +``` + +### 下载 + +将请求 url 指向文件地址即可下载,你也可以通过将`enableDownload`设置为 true 来开启下载进度。 + +```javascript +const downloadFile = () => + alovaInst.Get('/bigImage.jpg', { + // 开启下载进度 + enableDownload: true, + responseType: 'blob' + }); + +const { loading, data, downloading, send, onSuccess } = useRequest(downloadFile, { + immediate: false +}); +onSuccess(({ data: imageBlob }) => { + // 下载图片 + const anchor = document.createElement('a'); + anchor.href = URL.createObjectURL(blob); + anchor.download = 'image.jpg'; + anchor.click(); + URL.revokeObjectURL(anchor.href); +}); +const handleImageDownload = () => { + send(); +}; +``` + +## 模拟请求适配器兼容 + +在开发应用时,我们仍然可能需要用到模拟请求。只是默认情况下,[模拟请求适配器(@alova/mock)](/tutorial/request-adapter/alova-mock)的响应数据是一个`Response`实例,即默认兼容`GlobalFetch`请求适配器,当使用 XMLHttpRequest 适配器时,我们需要让模拟请求适配器的响应数据适配 XMLHttpRequest 适配器,此时你需要使用**@alova/adapter-xhr**包中导出的`xhrMockResponse`作为响应适配器。 + +```javascript +import { defineMock, createAlovaMockAdapter } from '@alova/mock'; +import { xhrRequestAdapter, xhrMockResponse } from '@alova/adapter-xhr'; + +const mocks = defineMock({ + // ... +}); + +// 模拟数据请求适配器 +export default createAlovaMockAdapter([mocks], { + // 指定请求适配器后,未匹配模拟接口的请求将使用这个适配器发送请求 + httpAdapter: xhrRequestAdapter(), + + // 使用xhrMockResponse,让模拟数据适配XMLHttpRequest适配器 + onMockResponse: xhrMockResponse +}); + +export const alovaInst = createAlova({ + // ... + // 通过环境变量控制是否使用模拟请求适配器 + requestAdapter: process.env.NODE_ENV === 'development' ? mockAdapter : xhrRequestAdapter() +}); +``` + +## Typescript + +XMLHttpRequest 请求适配器 提供了完整的类型适配。 + +### method 配置 + +在创建 method 实例时,除了 method 中通用的配置项外,你还可以使用`AlovaXHRRequestConfig`中的配置项。 + +```typescript +/** + * xhr请求配置参数 + */ +interface AlovaXHRRequestConfig { + /** + * 设置响应数据类型。 + * + * 可以设置更改响应类型。 值为:“arraybuffer”、“blob”、“document”、“json”和“text”。 + * 设置1:如果当前全局对象不是 Window 对象,则忽略设置为“文档”。 + * 设置2:如果状态正在加载或完成,则抛出“InvalidStateError”DOMException。 + * 设置3:如果设置了同步标志且当前全局对象是 Window 对象,则抛出“InvalidAccessError”DOMException。 + * @default "json" + */ + responseType?: XMLHttpRequestResponseType; + + /** + * 当凭证要包含在跨源请求中时为true。 当它们被排除在跨源请求中以及当 cookie 在其响应中被忽略时为 false。 默认为false。 + * 如果状态未发送或未打开,或者设置了send() 标志,则抛出“InvalidStateError”DOMException。 + * @default false + */ + withCredentials?: boolean; + + /** + * 设置响应数据的mimeType + */ + mimeType?: string; + + /** + * `auth` 表示应该使用 HTTP Basic 身份验证,并提供凭据。 + * 这将设置一个 `Authorization` 标头,覆盖任何现有的 + * 使用 `headers` 设置的 `Authorization` 自定义标头。 + * 请注意,只有 HTTP Basic 身份验证可以通过此参数进行配置。 + * 对于 Bearer 令牌等,请改用 `Authorization` 自定义标头。 + */ + auth?: { + username: string; + password: string; + }; +} +``` + +### 响应数据 + +XMLHttpRequest 适配器响应数据如下: + +```typescript +interface AlovaXHRResponseHeaders { + [x: string]: any; +} +interface AlovaXHRResponse { + status: number; + statusText: string; + data: T; + headers: AlovaXHRResponseHeaders; +} +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/03-alova-adapter-axios.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/03-alova-adapter-axios.md new file mode 100644 index 000000000..337bbab35 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/03-alova-adapter-axios.md @@ -0,0 +1,291 @@ +--- +title: axios适配器 +sidebar_position: 30 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## 安装 + + + + +```bash +npm install @alova/adapter-axios --save +``` + + + + +```bash +yarn add @alova/adapter-axios +``` + + + + +## 使用方法 + +### 创建 alova + +使用 **axiosRequestAdapter** 作为 alova 的请求适配器。 + +```javascript +import { createAlova } from 'alova'; +import { axiosRequestAdapter } from '@alova/adapter-axios'; + +const alovaInst = createAlova({ + // ... + requestAdapter: axiosRequestAdapter() + // ... +}); +``` + +适配器内部将会使用默认的 axios 实例进行请求,如果你为 axios 设置了一些全局参数,你可能需要注意以下两点: + +1. 优先使用 axios 实例内的`baseURL`和`timeout`参数,因此如果你在 axios 实例上设置了这些参数,那么就可以不需要在`createAlova`时设置了; +2. alova 实例的`beforeRequest`钩子将会早于 axios 的`interceptor.request`触发,alova 实例的`responded`钩子将会晚于 axios 实例的`interceptor.response`触发; + +> 你也可以[使用自定义的 axios 实例](#使用自定义的-axios-实例) + +### 请求 + +请求的使用方法与 web 环境中使用完全一致。已经完全兼容**axios**,你可以在创建 method 实例的*config*中指定`axios`支持的[全部配置项](https://axios-http.com/docs/req_config) + + + + +```html + +
加载中...
+
请求数据为:{{ data }}
+
+ + +``` + +
+ + +```jsx +const list = () => + alovaInst.Get('/list', { + // 设置的参数将传递给axios + paramsSerializer: params => { + return Qs.stringify(params, {arrayFormat: 'brackets'}) + }, + }); + +const App = () => { + const { loading, data } = useRequest(list); + + return ( + { loading ?
加载中...
: null } +
请求数据为:{ JSON.stringify(data) }
+ ) +}; +``` + +
+ + +```html + + +{#if $loading} +
加载中...
+{/if} +
请求数据为:{ data }
+``` + +
+
+ +### 上传 + +使用`FormData`上传文件,这个`FormData`实例会被传递到 axios 中,与 axios 上传文件用法保持了一致。 + +```javascript +const uploadFile = imageFile => { + const formData = new FormData(); + formData.append('file', imageFile); + return alovaInst.Post('/uploadImg', formData, { + // 开启上传进度 + enableUpload: true + }); +}; + +const { + loading, + data, + uploading, + send: sendUpload +} = useRequest(uploadFile, { + immediate: false +}); + +// 图片选择事件回调 +const handleImageChoose = ({ target }) => { + sendUpload(target.files[0]); +}; +``` + +### 下载 + +将请求 url 指向文件地址即可下载,你也可以通过将`enableDownload`设置为 true 来开启下载进度。 + +```javascript +const downloadFile = () => + alovaInst.Get('/bigImage.jpg', { + // 开启下载进度 + enableDownload: true, + responseType: 'blob' + }); + +const { loading, data, downloading, send, onSuccess } = useRequest(downloadFile, { + immediate: false +}); +onSuccess(({ data: imageBlob }) => { + // 下载图片 + const anchor = document.createElement('a'); + anchor.href = URL.createObjectURL(blob); + anchor.download = 'image.jpg'; + anchor.click(); + URL.revokeObjectURL(anchor.href); +}); +const handleImageDownload = () => { + send(); +}; +``` + +## 使用自定义的 axios 实例 + +在默认情况下,此适配器会使用默认的 axios 实例进行请求,但在一些情况下你需要使用自定义创建的 axios 实例,你可以这么做: + +```javascript +const customAxios = axios.create({ + // ... +}); + +const alovaInst = createAlova({ + // ... + // highlight-start + requestAdapter: axiosRequestAdapter({ + axios: customAxios + }) + // highlight-end +}); +``` + +## 模拟请求适配器兼容 + +在开发应用时,我们仍然可能需要用到模拟请求。只是默认情况下,[模拟请求适配器(@alova/mock)](/tutorial/request-adapter/alova-mock)的响应数据是一个`Response`实例,即默认兼容`GlobalFetch`请求适配器,当使用 axios 适配器时,我们需要让模拟请求适配器的响应数据是**AxiosResponse**兼容的,错误实例是**AxiosError**,因此你需要使用**@alova/adapter-axios**包中导出的`axiosMockResponse`作为响应适配器。 + +```javascript +import { defineMock, createAlovaMockAdapter } from '@alova/mock'; +import { axiosRequestAdapter, axiosMockResponse } from '@alova/adapter-axios'; + +const mocks = defineMock({ + // ... +}); + +// 模拟数据请求适配器 +export default createAlovaMockAdapter([mocks], { + // 指定axios请求适配器后,未匹配模拟接口的请求将使用这个适配器发送请求 + httpAdapter: axiosRequestAdapter(), + + // axiosMockResponse中包含了onMockResponse和onMockError + // 用于将模拟数据转换为AxiosResponse和AxiosError兼容的格式 + ...axiosMockResponse +}); + +export const alovaInst = createAlova({ + // ... + // 通过环境变量控制是否使用模拟请求适配器 + requestAdapter: process.env.NODE_ENV === 'development' ? mockAdapter : axiosRequestAdapter() +}); +``` + +## Typescript + +axios 请求适配器 提供了完整的类型适配,method 配置、响应数据的类型将与 axios 的类型完全匹配。 + +### method 配置 + +在创建 method 实例时,除了 method 中通用的配置项外,你还可以使用`AxiosRequestConfig`中的配置项,我们已经在类型中去除了和 method 实例通用配置冲突的项。 + +```typescript +/** + * axios请求配置参数 + * 去掉了与method冲突的属性 + */ +export type AlovaAxiosRequestConfig = Omit< + AxiosRequestConfig, + | 'url' + | 'method' + | 'baseURL' + | 'headers' + | 'params' + | 'data' + | 'timeout' + | 'cancelToken' + | 'signal' + | 'onUploadProgress' + | 'onDownloadProgress' +>; +``` + +### 响应数据 + +axios 的响应数据类型是`AxiosResponse`,当你使用 axios 适配器时,也将获得相同格式的响应数据。在实际使用中,我们通常需要在全局处理响应数据,一个简单的实例如下: + +```typescript +const alovaInst = createAlova( + baseURL: 'https://api.alovajs.org', + requestAdapter: axiosRequestAdapter(), + responded(response) { + // response自动被推断为AxiosResponse类型 + return response.data; + } +}); +``` + +### 错误 + +当 axios 遇到非 20x 和 30x 的响应状态码时将会抛出错误,为了包含更多信息,axios 将错误实例自定义设计成了一个`AxiosError`实例,而不是普通的 Error 实例,因此当遇到服务端错误或网络错误时都将抛出一个错误,你可以在全局的错误回调中捕获它。 + +```typescript +const alovaInst = createAlova( + baseURL: 'https://api.alovajs.org', + requestAdapter: axiosRequestAdapter(), + responded: { + onSuccess(response) { + // response自动被推断为AxiosResponse类型 + return response.data; + }, + onError(err: AxiosError) { + // err默认为any,你可以强制转换为AxiosError处理 + // ... + } + } +}); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/04-alova-adapter-taro.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/04-alova-adapter-taro.md new file mode 100644 index 000000000..23d98ed45 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/04-alova-adapter-taro.md @@ -0,0 +1,436 @@ +--- +title: Taro适配器 +sidebar_position: 40 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info 提示 + +此插件只支持 react 16.8+、vue3 版本的 taro 应用。 + +::: + +## 安装 + + + + +```bash +npm install @alova/adapter-taro --save +``` + + + + +```bash +yarn add @alova/adapter-taro +``` + + + + +:::warning React-Native 应用 + +如果你正在使用 Taro 开发 React-Native 应用,请确保`metro >= 0.76.0`,并在`metro.config.js`中开启`resolver.unstable_enablePackageExports` + +[关于 metro 的 unstable_enablePackageExports 参数](https://facebook.github.io/metro/docs/configuration/#unstable_enablepackageexports-experimental) + +::: + +:::warning 依赖预编译问题 + +在 Taro v3.5 beta 中新增了[依赖预编译功能](https://docs.taro.zone/blog/2022/05/19/Taro-3.5-beta#2-%E4%BE%9D%E8%B5%96%E9%A2%84%E7%BC%96%E8%AF%91)并在开发模式下默认开启,当你正在 Taro 中同时使用`alova`库和`@alova/scene-react(vue)`时可能导致报 `` [alova]can not call useHooks until set the `statesHook` at alova instance.``的错误,这是由于 prebundle 功能重复打包了两份不同的`alova`包导致,此时关闭 prebundle 功能即可解决此问题。 + +```js +// config/dev.ts +export default { + // ... + compiler: { + type: 'webpack5', + prebundle: { + // 关闭prebundle + enable: false + } + } +} satisfies UserConfigExport + +``` + +感谢[LBinin 的 issue](https://github.com/alovajs/scene/issues/63)。 + +此问题已向 Taro 团队提交[issue](https://github.com/NervJS/taro/issues/15728),期待解决此问题。 + +::: + +## 使用方法 + +### 创建 alova + +调用 **AdapterTaro** 将返回*请求适配器*、_存储适配器_,以及*ReactHook*,因此你不再需要设置这三个项,使用方法完全一致。 + + + + +```javascript +import { createAlova } from 'alova'; +import AdapterTaro from '@alova/adapter-taro'; + +const alovaInst = createAlova({ + baseURL: 'https://api.alovajs.org', + ...AdapterTaro() +}); +``` + + + + + +```javascript +import { createAlova } from 'alova'; +import AdapterTaroVue from '@alova/adapter-taro/vue'; + +const alovaInst = createAlova({ + baseURL: 'https://api.alovajs.org', + ...AdapterTaroVue() +}); +``` + + + + +### 请求 + +请求的使用方法与 web 环境中使用完全一致。已经完全兼容`Taro.request`,你可以在创建 method 实例的*config*中指定`Taro.request`支持的[全部配置项](https://taro-docs.jd.com/docs/apis/network/request/) + + + + +```jsx +const list = () => + alovaInst.Get('/list', { + // 设置的参数将传递给Taro.request + enableHttp2: true + }); + +const App = () => { + const { loading, data } = useRequest(list); + + return ( + { loading ? 加载中... : null } + 请求数据为:{ JSON.stringify(data) } + ) +}; +``` + + + + +```html + + 加载中... + 请求数据为:{{ data }} + + + +``` + + + + +### 上传 + +在 method 实例的*config*中设置`requestType: 'upload'`时表示上传文件,请求适配器将会调用`Taro.uploadFile`,上传的文件数据放在 method 实例的 data 中,你需要在 data 中指定`name`和`filePath`,这 2 个参数将传到`Taro.uploadFile`中,同时,你还可以在 data 中指定这 2 个参数外的其他参数,请求适配器会将它们传入到`formData`参数中。 + +同样的,已经完全兼容`Taro.uploadFile`,你可以在创建 method 实例的*config*中指定`Taro.uploadFile`支持的[全部配置项](https://taro-docs.jd.com/docs/apis/network/upload/uploadFile),如果还有额外的参数需要设置,请在 method 实例的*config*中指定。 + + + + +```jsx +const uploadFile = (name, filePath, formData) => + alovaInst.Post( + '/uploadImg', + { + name, + filePath, + + // 额外数据将传入uni.uploadFile的formData中 + ...formData + }, + { + // 设置请求方式为上传,适配器内将调用uni.uploadFile + requestType: 'upload', + + // 开启上传进度 + enableUpload: true + } + ); + +const App = () => { + const { loading, data, uploading, send } = useRequest(uploadFile, { + immediate: false + }); + + const handleImageChoose = () => { + Taro.chooseImage({ + success: chooseImageRes => { + const tempFilePaths = chooseImageRes.tempFilePaths; + send('fileName', tempFilePaths[0], { + extra1: 'a', + extra2: 'b' + }); + } + }); + }; + + return ( + { loading ? 上传中... : null } + 上传进度:{ uploading.loaded }/{ uploading.total } + + {/* ... */} + ) +} +``` + + + + +```html + + 上传中... + 上传进度:{{ uploading.loaded }}/{{ uploading.total }} + + + + + +``` + + + + +### 下载 + +在 method 实例的*config*中设置`requestType: 'download'`时表示下载文件,请求适配器将会调用`Taro.downloadFile`。 + +同样的,已经完全兼容`Taro.downloadFile`,你可以在创建 method 实例的*config*中指定`Taro.downloadFile`支持的[全部配置项](https://taro-docs.jd.com/docs/apis/network/download/downloadFile),如果还有额外的参数需要设置,请在 method 实例的*config*中指定。 + + + + +```jsx +const downloadFile = filePath => + alovaInst.Get('/bigImage.jpg', { + // 设置请求方式为下载,适配器内将调用uni.downloadFile + requestType: 'download', + filePath, + + // 开启下载进度 + enableDownload: true + }); + +const App = () => { + const { loading, data, downloading, send } = useRequest(downloadFile, { + immediate: false + }); + const handleImageDownload = () => { + send('file_save_path'); + }; + + return ( + { loading ? 下载中... : null } + 下载进度:{ downloading.loaded }/{ downloading.total } + + {/* ... */} + ); +} +``` + + + + +```html + + 下载中... + 下载进度:{{ downloading.loaded }}/{{ downloading.total }} + + + + + +``` + + + + +## 模拟请求适配器兼容 + +在使用 Taro 开发应用时,我们仍然可能需要用到模拟请求,只是默认情况下,[模拟请求适配器(@alova/mock)](/tutorial/request-adapter/alova-mock)的响应数据是一个`Response`实例,即默认兼容`GlobalFetch`请求适配器,当在 Taro 环境下使用时,我们需要让模拟请求适配器的响应数据是兼容 Taro 适配器的,因此你需要使用**@alova/adapter-taro**包中导出的`taroMockResponse`作为响应适配器。 + +```javascript +import { defineMock, createAlovaMockAdapter } from '@alova/mock'; +import AdapterTaro, { taroRequestAdapter, taroMockResponse } from '@alova/adapter-taro'; + +const mocks = defineMock({ + // ... +}); + +// 模拟数据请求适配器 +export default createAlovaMockAdapter([mocks], { + // 指定taro请求适配器后,未匹配模拟接口的请求将使用这个适配器发送请求 + httpAdapter: taroRequestAdapter, + + // 模拟响应适配器,指定后响应数据将转换为taro兼容的数据格式 + onMockResponse: taroMockResponse +}); + +export const alovaInst = createAlova({ + baseURL: 'https://api.alovajs.org', + timeout: 5000, + ...AdapterTaro({ + // 通过环境变量控制是否使用模拟请求适配器 + mockRequest: process.env.NODE_ENV === 'development' ? mockAdapter : undefined + }) + // ... +}); +``` + +## Typescript + +taro 请求适配器 提供了完整的类型适配,method 配置、响应数据的类型将与 taro 的类型完全匹配。 + +### method 配置 + +在创建 method 实例时,除了 method 中通用的配置项外,你还可以使用`Taro.request`、`Taro.uploadFile`和`Taro.downloadFile`中的配置项,我们已经在类型中去除了和 method 实例通用配置冲突的项。 + +```typescript +/** + * Taro.request请求额外参数 + */ +export type TaroRequestConfig = Omit< + Taro.request.Option, + 'url' | 'data' | 'header' | 'method' | 'timeout' | 'success' | 'fail' | 'complete' +>; + +/** + * Taro.uploadFile额外参数 + */ +export type TaroUploadConfig = Omit< + Taro.uploadFile.Option, + 'url' | 'filePath' | 'name' | 'header' | 'formData' | 'timeout' | 'success' | 'fail' | 'complete' +>; + +/** + * Taro.downloadFile额外参数 + */ +export type TaroDownloadConfig = Omit< + Taro.downloadFile.Option, + 'url' | 'header' | 'timeout' | 'success' | 'fail' | 'complete' +>; + +/** + * 合并的请求配置参数 + */ +export type TaroConfig = { + /** + * 请求类型,upload表示上传,download表示下载,不填写表示正常请求 + */ + requestType?: 'upload' | 'download'; +} & TaroRequestConfig & + TaroUploadConfig & + TaroDownloadConfig; +``` + +### 响应数据 + +因为 taro 请求适配器同时兼容了`Taro.request`、`Taro.uploadFile`和`Taro.downloadFile`,但它们的响应值类型稍有不同,所以响应数据类型是这样的: + +```typescript +type TaroResponse = + // Taro.request的响应类型 + | Taro.request.SuccessCallbackResult + + // Taro.uploadFile的响应类型 + | Taro.uploadFile.SuccessCallbackResult + + // Taro.downloadFile的响应类型 + | Taro.downloadFile.FileSuccessCallbackResult; +``` + +在实际使用中,我们通常需要在全局处理响应数据,建议分开场景判断返回数据,一个简单的实例如下: + +```typescript +const alovaInst = createAlova( + baseURL: 'https://api.alovajs.org', + ...AdapterTaro(), + responded(response) { + const { statusCode, data } = response as Taro.request.SuccessCallbackResult; + if (statusCode >= 400) { + throw new Error('请求错误'); + } + return data || null; + } +}); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/05-alova-adapter-uniapp.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/05-alova-adapter-uniapp.md new file mode 100644 index 000000000..18ea8079f --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/05-alova-adapter-uniapp.md @@ -0,0 +1,279 @@ +--- +title: Uniapp适配器 +sidebar_position: 50 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info 提示 + +此插件只支持 vue3 版本的 uniapp 应用。 + +::: + +## 安装 + + + + +```bash +npm install @alova/adapter-uniapp --save +``` + + + + +```bash +yarn add @alova/adapter-uniapp +``` + + + + +## 使用方法 + +### 创建 alova + +调用 **AdapterUniapp** 将返回*请求适配器*、_存储适配器_,以及*VueHook*,因此你不再需要设置这三个项,使用方法完全一致。 + +```javascript +import { createAlova } from 'alova'; +import AdapterUniapp from '@alova/adapter-uniapp'; + +const alovaInst = createAlova({ + baseURL: 'https://api.alovajs.org', + ...AdapterUniapp() +}); +``` + +### 请求 + +请求的使用方法与 web 环境中使用完全一致。已经完全兼容`uni.request`,你可以在创建 method 实例的*config*中指定`uni.request`支持的[全部配置项](https://uniapp.dcloud.net.cn/api/request/request.html) + +```html + + 加载中... + 请求数据为:{{ data }} + + + +``` + +当使用`useRequest/useWatcher`立即发送请求时,它将会在`onLoad`钩子中异步执行,因此你可以在`methodHandler`中访问 options 数据,访问方式如下: + +```javascript +import { onLoad } from '@dcloudio/uni-app'; + +let options = {}; +onLoad(opt => { + options = opt; +}); +const { loading, data } = useRequest(() => getDetail(options.id)); +``` + +### 上传 + +在 method 实例的*config*中设置`requestType: 'upload'`时表示上传文件,请求适配器将会调用`uni.uploadFile`,上传的文件数据放在 method 实例的 data 中,你需要在 data 中指定`name`、`filePath或files`,以及`file`(如果需要),这 4 个参数将传到`uni.uploadFile`中,同时,你还可以在 data 中指定这 4 个参数外的其他参数,请求适配器会将它们传入到`formData`参数中。 + +同样的,已经完全兼容`uni.uploadFile`,你可以在创建 method 实例的*config*中指定`uni.uploadFile`支持的[全部配置项](https://uniapp.dcloud.net.cn/api/request/network-file.html#uploadfile),如果还有额外的参数需要设置,请在 method 实例的*config*中指定。 + +```html + + 上传中... + 上传进度:{{ uploading.loaded }}/{{ uploading.total }} + + + + + +``` + +### 下载 + +在 method 实例的*config*中设置`requestType: 'download'`时表示下载文件,请求适配器将会调用`uni.downloadFile`。 + +同样的,已经完全兼容`uni.downloadFile`,你可以在创建 method 实例的*config*中指定`uni.downloadFile`支持的[全部配置项](https://uniapp.dcloud.net.cn/api/request/network-file.html#downloadfile),如果还有额外的参数需要设置,请在 method 实例的*config*中指定。 + +```html + + 下载中... + 下载进度:{{ downloading.loaded }}/{{ downloading.total }} + + + + + +``` + +## 模拟请求适配器兼容 + +在使用 uniapp 开发应用时,我们仍然可能需要用到模拟请求,只是默认情况下,[模拟请求适配器(@alova/mock)](/tutorial/request-adapter/alova-mock)的响应数据是一个`Response`实例,即默认兼容`GlobalFetch`请求适配器,当在 uniapp 环境下使用时,我们需要让模拟请求适配器的响应数据是兼容 uniapp 适配器的,因此你需要使用**@alova/adapter-uniapp**包中导出的`uniappMockResponse`作为响应适配器。 + +```javascript +import { defineMock, createAlovaMockAdapter } from '@alova/mock'; +import AdapterUniapp, { uniappRequestAdapter, uniappMockResponse } from '@alova/adapter-uniapp'; + +const mocks = defineMock({ + // ... +}); + +// 模拟数据请求适配器 +export default createAlovaMockAdapter([mocks], { + // 指定uniapp请求适配器后,未匹配模拟接口的请求将使用这个适配器发送请求 + httpAdapter: uniappRequestAdapter, + + // 模拟响应适配器,指定后响应数据将转换为uniapp兼容的数据格式 + onMockResponse: uniappMockResponse +}); + +export const alovaInst = createAlova({ + baseURL: 'https://api.alovajs.org', + timeout: 5000, + ...AdapterUniapp({ + // 通过环境变量控制是否使用模拟请求适配器 + mockRequest: process.env.NODE_ENV === 'development' ? mockAdapter : undefined + }) + // ... +}); +``` + +## Typescript + +uniapp 请求适配器提供了完整的类型适配,method 配置、响应数据的类型将与 uniapp 的类型完全匹配。 + +### method 配置 + +在创建 method 实例时,除了 method 中通用的配置项外,你还可以使用`uniapp.request`、`uniapp.uploadFile`和`uniapp.downloadFile`中的配置项,我们已经在类型中去除了和 method 实例通用配置冲突的项。 + +```typescript +/** + * uni.request请求额外参数 + */ +export type UniappRequestConfig = Omit< + UniNamespace.RequestOptions, + 'url' | 'data' | 'header' | 'method' | 'timeout' | 'success' | 'fail' | 'complete' +>; + +/** + * uni.uploadFile额外参数 + */ +export type UniappUploadConfig = Omit< + UniNamespace.UploadFileOption, + 'url' | 'name' | 'header' | 'formData' | 'timeout' | 'success' | 'fail' | 'complete' +>; + +/** + * uni.downloadFile额外参数 + */ +export type UniappDownloadConfig = Omit< + UniNamespace.DownloadFileOption, + 'url' | 'header' | 'timeout' | 'success' | 'fail' | 'complete' +>; + +/** + * 合并的请求配置参数 + */ +export type UniappConfig = { + /** + * 请求类型,upload表示上传,download表示下载,不填写表示正常请求 + */ + requestType?: 'upload' | 'download'; +} & UniappRequestConfig & + UniappUploadConfig & + UniappDownloadConfig; +``` + +### 响应数据 + +因为 uniapp 请求适配器同时兼容了`uniapp.request`、`uniapp.uploadFile`和`uniapp.downloadFile`,但它们的响应值类型稍有不同,所以响应数据类型是这样的: + +```typescript +type UniappResponse = + // uni.request的响应类型 + | UniNamespace.RequestSuccessCallbackResult + + // uni.uploadFile的响应类型 + | UniNamespace.UploadFileSuccessCallbackResult + + // uni.downloadFile的响应类型 + | UniNamespace.DownloadSuccessData; +``` + +在实际使用中,我们通常需要在全局处理响应数据,建议分开场景判断返回数据,一个简单的实例如下: + +```typescript +const alovaInst = createAlova( + baseURL: 'https://api.alovajs.org', + ...AdapterUniapp(), + responded(response) { + const { statusCode, data } = response as UniNamespace.RequestSuccessCallbackResult; + if (statusCode >= 400) { + throw new Error('请求错误'); + } + return data || null; + } +}); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/_category_.json b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/_category_.json new file mode 100644 index 000000000..b2734b673 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "扩展", + "link": { + "type": "generated-index" + } +} diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/01-vue-options.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/01-vue-options.md new file mode 100644 index 000000000..16007b3da --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/01-vue-options.md @@ -0,0 +1,334 @@ +--- +title: vue2/3 options +sidebar_position: 10 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +通常,use hook 只能在 vue 的 setup 中使用,但通过`@alova/vue-options`提供的辅助函数,你也可以在 vue 的 options 中使用 alova 的 use hook,完美兼容 alova 的几乎所有功能。 + +> vue2 和 vue3 中均可使用。 + +[![npm](https://img.shields.io/npm/v/@alova/vue-options)](https://www.npmjs.com/package/@alova/vue-options) +[![build](https://github.com/alovajs/vue-options/actions/workflows/release.yml/badge.svg?branch=main)](https://github.com/alovajs/vue-options/actions/workflows/release.yml) +[![coverage status](https://coveralls.io/repos/github/alovajs/vue-options/badge.svg?branch=main)](https://coveralls.io/github/alovajs/vue-options?branch=main) +![typescript](https://badgen.net/badge/icon/typescript?icon=typescript&label) +![license](https://img.shields.io/badge/license-MIT-blue.svg) + +## 安装 + + + + +```bash +npm install alova @alova/vue-options --save +``` + + + + +```bash +yarn add alova @alova/vue-options +``` + + + + +:::info alova 版本要求 + +alova 版本 >= 2.13.2 + +::: + +## 用法 + +### 映射 hook 状态和函数到 vue 实例上 + +先使用`vueOptionHook`创建 alova 实例。 + +```javascript +import { createAlova, Method } from 'alova'; +import GlobalFetch from 'alova/GlobalFetch'; +import { VueOptionsHook } from '@alova/vue-options'; + +// api.js +const alovaInst = createAlova({ + baseURL: 'http://example.com', + statesHook: VueOptionsHook, + requestAdapter: GlobalFetch(), + responded: response => response.json() +}); + +/** @type {() => Method} */ +export const getData = () => alovaInst.Get('/todolist'); +``` + +再使用`mapAlovaHook`将 use hook 的返回值集合映射到组件实例上,以下是访问响应式状态和操作函数的方法: + +1. 可以通过集合的 key 来访问`loading/data/error`等响应式状态,例如`this.key.loading`、`this.key.data`。 +2. 可以通过集合的 key 加函数名称来访问操作函数,并使用`$`拼接,例如`this.key$send`、`this.key$onSuccess`。 + +以下是一个完整的例子。 + +```html + + + +``` + +### 计算属性 + +如果你需要定义依赖 hook 相关的请求状态的计算属性,只需要和平时一样编写即可。 + +```javascript +export default { + computed: { + todoRequestLoading() { + return this.todoRequest.loading; + }, + todoRequestData() { + return this.todoRequest.data; + } + } +}; +``` + +### 监听 hook 状态变化 + +由于 vue2 的限制,所有 hook 状态都是挂载在一个名叫`alovaHook$`的对象上,因此在监听时需要添加`alovaHook$`前缀。 + +```javascript +export default { + watch: { + // ❌无法监听 + 'todoRequest.loading'(newVal, oldVal) { + // ... + }, + // ✅监听正常 + 'alovaHook$.todoRequest.loading'(newVal, oldVal) { + // ... + } + } +}; +``` + +但这样稍显麻烦,因此提供了一个`mapWatcher`辅助函数,不仅可以自动添加前缀、嵌套监听,还可以批量监听。 + +#### 设置单个监听函数 + +```javascript +export default { + watch: mapWatcher({ + // 用法1 + 'todoRequest.loading'(newVal, oldVal) {}, + + // 用法2 + todoRequest: { + loading(newVal, oldVal) {}, + data(newVal, oldVal) {} + } + }) +}; +``` + +同时也支持监听器对象。 + +```javascript +export default { + watch: mapWatcher({ + todoRequest: { + data: { + handler(newVal, oldVal) {}, + deep: true + } + } + }) +}; +``` + +#### 批量设置监听函数 + +多个监听 key 使用`,`分隔。 + +```javascript +export default { + watch: mapWatcher({ + // 用法1 + 'todoRequest1.data, todoRequest2.data'(newVal, oldVal) {}, + + // 用法2 + 'todoRequest1, todoRequest2': { + loading(newVal, oldVal) {}, + data(newVal, oldVal) {} + }, + + // 用法3 + todoRequest1: { + 'loading, data'(newVal, oldVal) {} + }, + + // 用法4 + 'todoRequest1, todoRequest2': { + 'loading, data'(newVal, oldVal) {} + } + }) +}; +``` + +> 批量监听也同样支持监听器对象 + +## 函数说明 + +### mapAlovaHook + +`mapAlovaHook`是用于将 alova 的 use hook 返回的状态和函数集合,通过 mixins 映射到 vue 组件实例上。它接收一个回调函数并返回 use hook 的返回值集合。 + +值得注意的是,回调函数将会在`created`阶段执行,你可以通过以下方式访问 vue 组件实例。 + +```javascript +// 1. 通过 this 访问组件实例,注意回调函数不能为箭头函数 +mapAlovaHook(function () { + console.log(this); + return { + todoRequest: useRequest(getData) + }; +}); + +// ======================= +// 2. 通过函数参数访问组件实例,此时可以用箭头函数 +mapAlovaHook(vm => { + console.log(vm); + return { + todoRequest: useRequest(getData) + }; +}); +``` + +### mapWatcher + +`mapWatcher`是用于快速定义 hook 状态变化的辅助函数,它接收一个对象,键为 hook 状态的 key 或嵌套值的字符串表示,值为监听函数或监听器对象。 + +```javascript +// 1. 监听单个状态 +mapWatcher({ + 'todoRequest.loading'(newVal, oldVal) { + //... + }, + todoRequest: { + data(newVal, oldVal) { + //... + } + }, + todoRequest: { + 'loading, data'(newVal, oldVal) { + //... + } + } +} +``` + +`mapWatcher`除了支持对 alova useHook 的监听辅助外,你还可以使用它批量设置普通状态值的监听。 + +```javascript +export default { + data() { + state1: '', + state2: 0 + }, + + // 第二个参数传入false即可监听普通状态 + watch: mapWatcher({ + 'state1, state2'(newVal, oldVal) { + //... + } + }, false) +} +``` + +## 类型支持 + +### 自动推断 + +`@alova/vue-options`提供了完整的 ts 类型支持,无论是否使用 typescript,映射的值类型都会被自动推断,例如: + +```javascript +this.todoRequest.loading; // boolean +this.todoRequest.error; // Error | undefined +this.todoRequest.data; // any +this.todoRequest$send; // (...args: any[]) => Promise +this.todoRequest$onSuccess; // (handler: SuccessHandler) => void +this.todoRequest$onError; // (handler: ErrorHandler) => void +this.todoRequest$onComplete; // (handler: CompleteHandler) => void +// ... +``` + +### 为响应数据添加类型 + +除了`this.todoRequest.data`外,其他值都有了正确的类型,那么如何给`data`也设置类型呢?其实与 alova 在其他环境中使用是相同。 + +**javascript** + +在 javascript 中可以使用类型注释添加类型,Method 的前两个泛型参数为`unknown`,第三个泛型参数就是响应数据的类型 + +```javascript +import { Method } from 'alova'; + +/** @type {() => Method} */ +export const getData = () => alovaInst.Get('/todolist'); +``` + +**typescript** + +在 typescript 中添加响应数据类型,请阅读 [alova 文档 typescript 章节](/tutorial/combine-framework/typescript) + +## 限制 + +1. 暂不支持[管理额外的状态](/tutorial/advanced/manage-extra-states)。 +2. 目前只支持 alova 的`useRequest/useWatcher/useFetcher`三个核心 useHook,以及你在自己项目中基于核心 useHook 的封装,暂不支持[@alova/scene](https://github.com/alovajs/scene)内的扩展 useHook。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/02-solid.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/02-solid.md new file mode 100644 index 000000000..f204dd737 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/02-solid.md @@ -0,0 +1,6 @@ +--- +title: solid +sidebar_position: 20 +--- + +敬请期待... diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/03-angular.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/03-angular.md new file mode 100644 index 000000000..49a755cb1 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/03-angular.md @@ -0,0 +1,6 @@ +--- +title: angular +sidebar_position: 30 +--- + +敬请期待... diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/04-native-mp.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/04-native-mp.md new file mode 100644 index 000000000..b04786d56 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/04-native-mp.md @@ -0,0 +1,6 @@ +--- +title: 原生小程序 +sidebar_position: 40 +--- + +敬请期待... diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/05-preact.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/05-preact.md new file mode 100644 index 000000000..9fd875bd7 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/05-preact.md @@ -0,0 +1,6 @@ +--- +title: preact +sidebar_position: 50 +--- + +敬请期待... diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/06-qwik.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/06-qwik.md new file mode 100644 index 000000000..893e62274 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/06-qwik.md @@ -0,0 +1,6 @@ +--- +title: qwik +sidebar_position: 60 +--- + +敬请期待... diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/07-lit.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/07-lit.md new file mode 100644 index 000000000..106cd17b5 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/07-lit.md @@ -0,0 +1,6 @@ +--- +title: lit +sidebar_position: 70 +--- + +敬请期待... diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/08-stencil.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/08-stencil.md new file mode 100644 index 000000000..07a070572 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/08-stencil.md @@ -0,0 +1,6 @@ +--- +title: stencil +sidebar_position: 80 +--- + +敬请期待... diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/10-custom/01-overview.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/10-custom/01-overview.md new file mode 100644 index 000000000..4b9513613 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/10-custom/01-overview.md @@ -0,0 +1,26 @@ +--- +title: 概览 +sidebar_position: 10 +--- + +alova 具有很高的扩展性,它除了提供核心的缓存机制、请求共享机制以及状态管理等通用特性外,还提供了各类定制功能以及中间件机制,可以适配不同 js 环境,以及自定义请求策略。 + +## 适配器 + +为了满足 js 在不同环境下的运行需求,你可以自定义请求适配器、存储适配器,甚至是 UI 框架的状态适配器,在接下来的章节中也会详细介绍。以下列出了一些适配器示例。 + +- [fetch 适配器](https://github.com/alovajs/alova/blob/main/src/predefine/GlobalFetch.ts) +- [localStorage 存储适配器](https://github.com/alovajs/alova/blob/main/src/predefine/globalLocalStorage.ts) +- [vue states hook](https://github.com/alovajs/alova/blob/main/src/predefine/VueHook.ts) + +你也可以将多个类型的适配器组成一个集合,例如[Uniapp 适配器](/tutorial/request-adapter/alova-adapter-uniapp)。 + +## 编写请求策略 + +alova 的请求策略和 alova 核心库是分开的,目的是为了开发者们也可以利用 alova 的高扩展性来编写自己的请求策略。通常情况下,一个自定义的请求策略是基于`useRequest`、`useWatcher` 和 `useFetcher` 三者的组合,以及为它们编写[中间件](/tutorial/advanced/middleware)、缓存的操纵函数来控制它们的请求方式,从而实现各种效果的请求策略。 + +**@alova/scene** 中的请求策略具有很好的代表性,强烈建议你参考源码寻找灵感。 + +- [分页请求策略源码](https://github.com/alovajs/scene/blob/main/src/hooks/pagination/usePagination.js) +- [验证码策略源码](https://github.com/alovajs/scene/blob/main/src/hooks/useCaptcha.ts) +- [表单提交策略源码](https://github.com/alovajs/scene/blob/main/src/hooks/useForm.ts) diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/10-custom/02-custom-http-adapter.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/10-custom/02-custom-http-adapter.md new file mode 100644 index 000000000..647b1a2e7 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/10-custom/02-custom-http-adapter.md @@ -0,0 +1,193 @@ +--- +title: 自定义请求适配器 +sidebar_position: 20 +--- + +还记得如何创建一个 Alova 实例吗? + +```javascript +const alovaInstance = createAlova({ + // ... + requestAdapter: GlobalFetch() +}); +``` + +`requestAdapter`就是请求适配器,内部的请求发送和接收都将依赖请求适配器,`GlobalFetch`就是通过 fetch api 的方式来管理请求的,在大多数情况下我们可以使用它,但是,当`alova`运行在 fetch api 不可用的环境时(如 app、小程序),就需要更换一个支持当前环境的请求适配器。 + +那应该如何自定义一个请求适配器呢?很简单,它其实是一个函数,在每次发起请求时都会调用此函数,并返回一个对象,这个对象内包含如`url`、`method`、`data`、`headers`、`timeout`等请求相关的数据集合,虽然字段较多,但我们只需访问我们需要的数据即可。 + +## 请求适配器结构 + +请求适配器将接收到请求相关的参数,以及当前正在请求的 method 实例,并返回一个响应相关的函数集合。 + +```javascript +function CustomRequestAdapter(requestElements, methodInstance) { + // 发送请求... + return { + async response() { + // ...返回响应数据 + }, + async headers() { + // 返回响应头的异步函数 + }, + abort() { + // 中断请求,当外部调用abort时将触发此函数 + }, + onDownload(updateDownloadProgress) { + // 下载进度信息,内部持续调用updateDownloadProgress来更新下载进度 + }, + onUpload(updateUploadProgress) { + // 上传进度信息,内部持续调用updateUploadProgress来更新上传进度 + } + }; +} +``` + +### 请求参数详细 + +**requestElements** + +发送请求的相关元素,包含以下数据。 + +```typescript +interface RequestElements { + // 请求url,get参数已包含其中 + readonly url: string; + + // 请求类型,如GET、POST、PUT等 + readonly type: MethodType; + + // 请求头信息,对象 + readonly headers: Arg; + + // 请求体信息 + readonly data?: RequestBody; +} +``` + +**methodInstance** + +当前请求的 method 实例 + +### 返回参数详细 + +**response(必填)** + +一个异步函数,函数返回响应值,它将会传递给全局的响应拦截器(responded); + +**headers(必填)** + +一个异步函数,函数返回的响应头对象将传递给 Method 实例的 transformData 转换钩子函数; + +**abort(必填)** + +一个普通函数,它用于中断请求,所有的中断请求最终都将会调用此函数来执行; + +**onDownload(可选)** + +一个普通函数,它接收一个更新下载进度的回调函数,在此函数内自定义进度更新的频率,在此示例中模拟每隔 100 毫秒更新一次。`updateDownloadProgress`回调函数接收两个参数,第一个参数是总大小,第二个参数是已下载大小; + +**onUpload(可选)** + +一个普通函数,它接收一个更新上传进度的回调函数,在此函数内自定义进度更新的频率,在此示例中模拟每隔 100 毫秒更新一次。`updateUploadProgress`回调函数接收两个参数,第一个参数是总大小,第二个参数是已上传大小; + +## XMLHttpRequest 请求适配器示例 + +以下是一个通过 XMLHttpRequest 发送请求的适配器的示例,主要用于演示适配器的写法,代码不完整,不可运行。 + +```javascript +function XMLHttpRequestAdapter(requestElements, methodInstance) { + // 解构出需要用到的数据 + const { url, type, data, headers } = config; + + // 发送请求 + const xhr = new XMLHttpRequest(); + xhr.open(type, url); + for (const key in headers) { + xhr.setRequestHeader(key, headers[key]); + } + const responsePromise = new Promise((resolve, reject) => { + xhr.addEventListener('load', event => { + // 处理响应数据 + resolve(/* ... */); + }); + xhr.addEventListener('error', event => { + // 处理请求错误 + reject(/* ... */); + }); + }); + + xhr.send(JSON.stringify(data)); + + return { + // 返回响应数据的异步函数 + response: () => responsePromise, + + // 返回响应头的异步函数 + headers: () => responsePromise.then(() => xhr.getAllResponseHeaders()), + abort: () => { + xhr.abort(); + }, + + // 下载进度信息,内部持续调用updateDownloadProgress来更新下载进度 + onDownload: updateDownloadProgress => { + xhr.addEventListener('progress', event => { + // 数据接收进度 + updateDownloadProgress(event.total, event.loaded); + }); + }, + + // 上传进度信息,内部持续调用updateUploadProgress来更新上传进度 + onUpload: updateUploadProgress => { + xhr.upload.onprogress = event => { + updateUploadProgress(event.total, event.loaded); + }; + } + }; +} +``` + +:::note + +更完整的请求适配器细节可以查阅 [GlobalFetch 源码](https://github.com/alovajs/alova/blob/main/src/predefine/GlobalFetch.ts) 来了解。 + +::: + +## 请求适配器类型 + +全局的`beforeRequest`、`responded`拦截器,以及 method 实例创建时的配置对象的类型,都会根据请求适配器提供的类型自动推断,以下是 GlobalFetch 的类型。 + +```javascript +import type { RequestElements, Method, ProgressUpdater } from 'alova'; + +export type GlobalFetch = () => ( + elements: RequestElements, + method: Method +) => { + response: () => Promise, + headers: () => Promise, + onDownload: (handler: ProgressUpdater) => void, + abort: () => void +}; +``` + +在这个类型中分别指定了`RC`、`RE`和`RH`三个类型的值,因此在全局的拦截器中、method 实例配置中等地方将自动推断为请求适配器给定的类型。 + +它们分别表示为: + +- **RC**:*RequestConfig*的缩写,请求配置对象类型 +- **RH**:*ResponseHeader*的缩写,响应头对象类型 +- **RE**:*Response*的缩写,响应类型 + +如果你正在使用 **GlobalFetch**,他们的类型分别会被推断为: + +- **RC**:fetch api 的请求配置对象`RequestInit`; +- **RH**:响应头对象`Headers`; +- **RE**:响应对象`Response`; + +为了方便定义请求适配器的类型,alova 中也提供了一个适配器类型,你只需要根据需要传入`RC/RE/RH`泛型参数即可。 + +```typescript +import type { AlovaRequestAdapter } from 'alova'; +type CustomRequestAdpater = AlovaRequestAdapter; +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/10-custom/03-custom-storage-adapter.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/10-custom/03-custom-storage-adapter.md new file mode 100644 index 000000000..2ccc85e9e --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/10-custom/03-custom-storage-adapter.md @@ -0,0 +1,48 @@ +--- +title: 自定义存储适配器 +sidebar_position: 30 +--- + +alova 中涉及多个需要数据持久化的功能,如持久化缓存、静默提交和离线提交。**在默认情况下,alova 会使用`localStorage`来存储持久化数据**,但考虑到非浏览器环境下,因此也支持了自定义。 + +自定义存储适配器同样非常简单,你只需要指定保存数据、获取数据,以及移除数据的函数即可,大致是这样的。 + +```javascript +const customStorageAdapter = { + set(key, value) { + // 保存数据,value为结构化数据,可调用JSON.stringify转换为字符串 + }, + get(key) { + // 获取数据,需要返回结构化数据,可调用JSON.parse转换为对象 + }, + remove(key) { + // 移除数据 + } +}; +``` + +然后在创建`alova`实例时传入这个适配器即可。 + +```javascript +const alovaInstance = createAlova({ + // ... + storageAdapter: customStorageAdapter +}); +``` + +## SessionStorage 存储适配器示例 + +```javascript +const sessionStorageAdapter = { + set(key, value) { + sessionStorage.setItem(key, JSON.stringify(value)); + }, + get(key) { + const data = sessionStorage.getItem(key); + return data ? JSON.parse(data) : data; + }, + remove(key) { + sessionStorage.removeItem(key); + } +}; +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/10-custom/04-custom-stateshook.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/10-custom/04-custom-stateshook.md new file mode 100644 index 000000000..7266e4e2f --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/10-custom/04-custom-stateshook.md @@ -0,0 +1,101 @@ +--- +title: 自定义States Hook +sidebar_position: 50 +--- + +还记得如何创建一个 Alova 实例吗? + +```javascript +const alovaInstance = createAlova({ + // ... + statesHook: ReactHook +}); +``` + +`statesHook`将决定在请求时返回哪个 UI 库的状态,alova 目前提供了**VueHook、ReactHook、svelteHook**。 + +在大部分情况下你应该用不到这个功能,但如果你需要适配更多 alova 不支持的 MVVM 库,就需要自定义编写`statesHook`了。 + +`statesHook`是一个包含特定函数的普通对象,不过这些还是基本不涉及算法,我们来看看 **VueHook** 是怎么编写的吧。 + +## statesHook 结构 + +statesHook 使用一个对象进行表示,以下为**VueHook**的示例。 + +```javascript +import { ref, watch, onUnmounted } from 'vue'; + +const VueHook = { + // 状态创建函数 + create: rawData => ref(data), + + // 状态导出函数 + export: state => state, + + // 脱水函数 + dehydrate: state => state.value, + + // 响应式状态更新函数 + update: (newVal, states) => { + Object.keys(newVal).forEach(key => { + states[key].value = newVal[key]; + }); + }, + + // 请求发送控制函数 + effectRequest({ handler, removeStates, saveStates, immediate, frontStates, watchingStates }) { + // 组件卸载时移除对应状态 + onUnmounted(removeStates); + + // 调用useRequest和useFetcher时,watchingStates为undefined + if (!watchingStates) { + handler(); + return; + } + + // 调用useWatcher时,watchingStates为需要监听的状态数组 + // immediate为true时,表示需要立即发送请求 + watch(watchingStates, handler, { immediate }); + } +}; +``` + +## 自定义 statesHook 函数说明 + +> 以下 5 个函数均必须指定。 + +**create** + +响应式状态创建函数,`loading`、`error`、`data`、`downloading`、`uploading`等都是调用此函数创建的,如 vue3 项目下将创建为 ref 值; + +**export** + +状态导出函数,此函数接收 create 函数创建的响应式状态,并导出最终给开发者使用的状态,这里`VueHook`导出的状态是原状态; + +**dehydrate** + +脱水函数,意思是将响应式状态转换为普通数据,与 create 是相反的操作,在`updateState`中; + +**update** + +响应式状态更新函数,`alova`内部维护的状态更新都是通过此函数完成。此函数接收两个参数,第一个参数是新的数据对象,第二个参数是原响应式状态的 map 集合,这里你可以固定写一个循环更新`states`; + +**effectRequest** + +请求发送控制函数,它会在`useRequest`、`useWatcher`、`useFetcher`被调用时立即执行此函数,我们要在这个函数内要完成三件事: + +1. 当前组件卸载时,调用 removeStates 函数移除当前组件涉及到的响应式状态,避免内存溢出; +2. 当调用 useWatcher 时,绑定状态监听,状态改变时调用 sendRequest 函数,你可以用`states`是否为数组判断是否为`useWatcher`被调用,同时,`immediate`参数用于判断`useWatcher`调用时是否需要立即发送请求; +3. 当调用`useRequest`和`useFetcher`时,调用 sendRequest 发出一次请求,此时`states`为`undefined`; + +:::warning 注意 + +如果 statesHook 涉及的库是像`react`,每次重新渲染都会调用`alova`的 use hook 的,那么在`effectRequest`中还需要在每次重新渲染时触发`saveStates`函数,这是因为`react`每次重新渲染都会刷新它的状态引用,因此我们需要再次重新保存它们。 + +::: + +[ReactHook 源码点此查看](https://github.com/alovajs/alova/blob/main/src/predefine/ReactHook.ts) + +## statesHook 类型 + +如果你在自定义 statesHook 时,也希望它可以支持 typescript,可以 [点此查看](/tutorial/combine-framework/typescript) diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/10-custom/_category_.json b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/10-custom/_category_.json new file mode 100644 index 000000000..505fadaab --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/10-custom/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Custom", + "link": { + "type": "generated-index" + } +} diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/01-RSM.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/01-RSM.md new file mode 100644 index 000000000..d2db81034 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/01-RSM.md @@ -0,0 +1,61 @@ +--- +title: 请求场景模型(RSM) +sidebar_position: 10 +--- + +## 什么是请求场景模型 + +请求场景模型是以客户端视角的,描述客户端从触发请求意图到接收请求结果的抽象模型,分别由请求时机、请求行为、请求事件以及响应管理四个阶段组成。例如在进行一次请求时经常需要思考以下问题, + +1. 什么时候发出请求; +2. 是否要展示请求状态; +3. 是否需要对请求进行失败重试; +4. 要如何加工响应数据; +5. 是否需要对请求参数加密; +6. 是否要对高频使用的响应数据做缓存; +7. 如何进行跨页面操作数据; +8. 弱网或断网环境下需要如何处理请求; +9. ... + +`fetch`或`axios`往往更专注于如何与服务端交互,但对于上面的问题我们总是需要自己处理,这些有利于应用性能和稳定性的功能,总会让程序员们编写出低维护性的代码。请求场景模型就是从准备请求到响应数据加工完毕的所有环节进行抽象,从而覆盖以前端为视角的,整个 CS 交互生命周期的模型。`alova`就是一个以请求场景模型的库,它是对`axios`等请求库的一种补充,而非替代品。 + +> CS 交互:泛指所有客户端类型和服务端的数据交互 + +## 请求场景模型 + +![RSM](/img/rsm-cn.png) + +## 请求时机 + +描述在什么时候需要发出请求,在`alova`中以`useHook`实现。 + +- 初始化展示数据,如刚进入某个界面或子界面; +- 人机交互触发 CS 交互,需要变更数据重新发出请求,如翻页、筛选、排序、模糊搜索等; +- 以防抖方式发送请求,避免视图数据闪动,以及降低服务端压力 +- 预加载数据,如分页内预先加载下一页内容、预测用户点击某个按钮后预先拉取数据; +- 操作服务端数据,需发出增删改查请求,如提交数据、删除数据等; +- 同步服务端状态,如数据变化较快的场景下轮询请求、操作了某个数据后重新拉取数据; + +## 请求行为 + +描述以怎样的方式处理请求,在`alova`中以 Method 抽象实现。 + +- 占位请求,请求时展示 loading、骨架图、或者是上次使用的真实数据; +- 缓存高频响应,多次执行请求会使用保鲜数据; +- 多请求串行与并行; +- 重要接口的重试机制,降低网络不稳定造成的请求失败概率; +- 静默提交,当只关心提交数据时,提交请求后直接响应成功事件,后台保证请求成功; +- 离线提交,离线时将提交数据暂存到本地,网络连接后再提交; + +## 请求事件 + +表示携带请求参数发送请求,获得响应,`alova`可以与`axios`、`fetch`、`XMLHttpRequest`等任意请求库或原生方案共同协作。 + +## 响应管理 + +`alova`将响应数据状态化并统一管理,以请求层面的方式刷新视图数据、操作缓存,避免了在组件层面的操作,更加优雅和统一。 + +- 移除缓存响应数据,再次发起请求时将从服务端拉取; +- 更新缓存响应数据,可更新任意位置响应数据,非常有利于跨页面更新数据; +- 刷新响应数据,可重新刷新任意位置的响应数据,也非常有利于跨页面更新数据; +- 自定义设置缓存,在请求批量数据时,可手动对批量数据一一设置缓存,从而满足后续单条数据的缓存命中; diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/02-comparison.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/02-comparison.md new file mode 100644 index 000000000..6839ce538 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/02-comparison.md @@ -0,0 +1,93 @@ +--- +title: 与其他库比较 +sidebar_position: 20 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## 与 axios 对比 + +axios 提供了基于 promise 的非常简单易用的 HTTP 请求功能,只需要简单的一行代码即可发送和接收请求,并且可以在浏览器和 nodejs 环境下运行,是一个非常优秀的请求 js 库。 + +但是 axios 聚焦于请求发送和接收响应,这意味着如果你需要自行编写更多代码来主动优化请求功能,而 alova 像是 axios 的武器装备,将 axios 与 alova 组合使用可以获得更强大的请求能力,以下是 alova 为 axios 附加的请求管理能力。 + +### alova 为 axios 提供自动化请求状态管理 + +仅使用 axios 时,通常需要你自行维护请求相关状态,使用 alova 的 use hook 后可以获得自动化的请求状态管理能力。 + + + + +```javascript +// vue3代码示例 +const loading = ref(false); +const data = ref({}); +const error = ref(null); +const request = async () => { + try { + loading.value = true; + data.value = await axios.get('/xxx'); + } catch (e) { + error.value = e; + } + loading.value = false; +}; +mounted(request); +``` + + + + +```javascript +// 将axios作为alova的请求适配器 +const { loading, data, error } = useRequest(alova.Get('/xxx')); +``` + + + + +### alova 提供开箱即用的高性能请求策略 + +alova 为你提供了[多个高性能的请求策略模块](/tutorial/strategy),你可以根据不同请求场景使用不同的模块,这是 axios 不具备的。 + +### alova 为 axios 提供响应数据缓存 + +alova 分别提供了 3 种缓存模式来满足不同的缓存场景,分别为内存模式、缓存占位模式、恢复模式。它们是组件无关的,只要请求地址和参数相同都可以命中缓存,除非你关闭了它。响应数据缓存可以极大地提高请求流畅性,降低服务端压力。 + +### alova 为 axios 提供请求共享功能 + +请求共享在同时发送多个相同请求时,将会复用同一个请求,它也可以提升应用流畅性和降低服务端压力。 + +### alova 为 axios 提供数据预拉取 + +提前请求将要使用的数据,也可以极大提升应用流畅性。 + +### alova 可管理请求状态 + +你可以使用 alova 跨任意的组件层级来访问其他组件内的状态化数据,这可以让你减少跨组件通信的一些麻烦。 + +## 与 react-query、swr 对比 + +react-query 是一个强大的异步状态管理,swr 是一个用于数据请求的 React Hooks 库,它们的共同特性也是使用 use hook 来发送和管理请求,和数据缓存功能,对于它们,alova 有以下不同之处。 + +### alova 的目标不同 + +实际上,alova 的 use hook 也是参考了 react-query 和 swr 的设计,但是 alova 选择了请求策略库的方向,你可以在不同的请求场景下使用不同的请求策略模块,让你在编写更少量代码同时,也能实现更高效地 Client-Server 数据交互。 + +### Method 代理设计 + +react-query 和 swr 都是在 use hook 中直接使用`axios`或`fetch api`发送请求,而 alova 使用了 `Method` 代理的设计模式,这样设计具有以下 3 个好处: + +1. 统一的使用体验,不会因平台或 UI 框架不同而存在不同的使用方式。 +2. `axios`和`fetch api`等请求库以请求适配器的方式,与每个 api 解耦,这让 alova 提供了统一的开发体验和完美的代码迁移。 +3. 每个 method 实例都代表不同的 api,你可以将同一个 api 的请求参数与请求行为参数聚合到同一个 method 实例中,而不会分散开,更适合管理大量的 api。 +4. alova 通过对 method 实例上的请求参数序列化,实现了自动化管理响应数据缓存,你不需要指定缓存 key,而 react-query 和 swr 都需要自定义设置`queryKey`来管理缓存。 + +### 高灵活性 + +alova 通过各种适配器、中间件实现了很高的灵活性,不仅可以运行在任何 js 环境,还可以支持用户自定义不同场景下的请求模块。 + +### 轻量化 + +alova 很轻量,体积只有 react-query 和 axios 的 30%+。与 swr 体积相似,但提供更丰富的功能。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/03-Q&A.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/03-Q&A.md new file mode 100644 index 000000000..b1b4684db --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/03-Q&A.md @@ -0,0 +1,24 @@ +--- +title: 提问&回答 +sidebar_position: 30 +--- + +## 为什么创造 alova? + +数据请求一直是应用程序必不可少的重要部分,自从 XMLHttpRequest 诞生以来请求方案层出不穷,客户端的数据交互探索一直聚焦于请求的简单性,如`$.ajax`、`axios`、`fetch api`以及`react-query`等请求工具,编码形式从回调函数、Promise,再到 usehook 不断发展,这些 js 库在请求简单性上已经做得很好了,不过它们只提供了通用的功能,这意味着对于不同的请求场景例如共享请求、分页请求、表单提交、上传和下载文件等,开发者依然需要自己编写复杂的代码,降低开发效率,性能也无法得到保证,在越来越看重的用户体验的时代,应用的流畅性变得越来越重要。 + +同时,客户端和服务端的协作也是割裂的,前端工程师需要查阅 API 文档并手动编写 API 函数,并且服务端 API 的任何更改都需要主动通知前端工程师,这将会使产品变得更加不可控。 + +**而我们认为还有更简单的方案,即根据请求场景,例如分页、表单提交、断点续传等,选择对应的 useHook,它将帮你管理数据,控制何时应该发送请求**。从而让开发者在编写少量代码也能实现更高效地 Client-Server 数据交互。 + +同时,alova 具有很灵活的扩展能力来实现不同场景下的请求策略,你也可以自定义自己的请求场景,这部分内容在[自定义章节](/category/custom)。 + +为了覆盖更多请求场景,我们还将请求场景抽象成了 [请求场景模型(RSM)](/tutorial/others/RSM),它很好地解释了 alova 的请求策略方案。在未来,alova 将承载着我们对请求策略的探索之路继续前行。 + +## 替代请求库? + +alova 是一个请求策略库,它的创建初衷是对不同请求场景提供特定的请求策略解决方案,从而更简洁优雅地实现流畅的请求体验,而例如`$.ajax`、`axios`和`fetch-api`等对请求发送和响应接收提供了很好的支持,它们是 [RSM](/tutorial/others/RSM) 流程中必不可少的一个环节(请求事件),alova 仍然需要依靠它们进行请求,因此我们可以将 alova 看作是请求库的一种武装,让请求库变得更加强大。 + +## 为什么要深度绑定 UI 框架? + +对一个 js 库来说解耦意味着更多场景下的使用,例如 axios 可以在 nodejs 中使用,但同时意味着开发者需要写更多的模板代码,比如使用 useHooks 封装 axios 等。而 alova 摒弃了解耦带来的更多使用场景,将使用范围定位在与 UI 框架配合使用,以最精简的方式使用 alova,这是为了开发者的收益方面而考量的,在一个 UI 框架盛行的时候,深度绑定可以为开发者提供直接使用的功能,提升开发者的使用体验,而不需要太多的模板代码。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/04-future.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/04-future.md new file mode 100644 index 000000000..dbf3efd7d --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/04-future.md @@ -0,0 +1,36 @@ +--- +title: alova的未来 +sidebar_position: 40 +--- + +alovajs 的定位是轻量级的请求策略库,目前在请求功能和请求策略方面提供了较好的支持,但 alovajs 的未来不止于此。 + +## 更多的请求策略 + +这是一直不变的方向,我们会持续探索基于常见业务下的高效易用的请求策略。 + +## 更多的 UI 框架支持 + +alovajs 虽然是一个基于 UI 框架的请求工具,但它的灵活设计支持我们在各种 UI 框架中使用,最终将会兼容以下的 UI 框架和 js 环境: + +- 函数风格框架,如`react/react-native/vue-composntion/svelte/solid/preact/qwik`。 +- SSR 框架,如`next/nuxt/sveltekit`。 +- class 风格框架,如`angular/lit/stencil`。 +- options 风格框架,如`vue-options/原生小程序(中国🇨🇳)`。 +- 多端适配框架,如`Uniapp/Taro`(中国 🇨🇳)。 + +详情请[前往 UI 框架](/category/framework)中查看。 + +## API 的自动管理和维护 + +在未来,alovajs 还致力于解决前端 API 的问题,并更进一步地简化前端开发的工作流,这就是 alovajs 的下一个发展方向:**API 的自动管理和维护**,具体包含以下三点。 + +1. 自动生成 ts 类型完整的、描述完整的请求函数,无论是 js 项目还是 ts 项目,都调用无需引入,让开发者就像直接调用`location.reload`这样方便,并且请求函数可直接看到完整的描述和请求参数类型提示,这些都可以由 openAPI 自动生成。 + +2. 由于自动生成的请求函数拥有完整的描述和类型提示,开发一个 vscode 插件通过关键字来快速检索需要使用的 API,你也不再需要去查阅 API 文档了。 + +3. 解决前后端协作断层的问题,接口的任何变动前端可知,可以在启动项目时被通知,如果在构建项目时发现存在变动,则会抛出错误停止构建,如果是 ts 项目也会在编译时抛出错误,也可以通过 vscode 插件查看变动记录。 + +## 开发清单 + +[点此查看所有规划的开发清单](/contributing/become-core-member#%E5%8F%AF%E5%8F%82%E4%B8%8E%E7%9A%84%E4%BB%93%E5%BA%93%E5%88%97%E8%A1%A8) diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/05-react-native.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/05-react-native.md new file mode 100644 index 000000000..cd224705a --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/05-react-native.md @@ -0,0 +1,15 @@ +--- +title: 开发React-Native应用 +sidebar_position: 50 +--- + +你同样可以使用 alova 开发 React-Native 应用,甚至可以直接使用 GlobalFetch 请求适配器来作为请求事件处理。 + +但是有以下的注意事项: + +## metro 版本 + +在 alova 中的`package.json`中使用了`exports`来定义多个导出项,因此需要确保这两点: + +1. metro 版本高于 0.76.0 +2. 在`metro.config.js`中开启`resolver.unstable_enablePackageExports`。[详情点此查看](https://facebook.github.io/metro/docs/configuration/#unstable_enablepackageexports-experimental) diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/06-use-in-static.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/06-use-in-static.md new file mode 100644 index 000000000..e3eb87d6f --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/06-use-in-static.md @@ -0,0 +1,139 @@ +--- +title: 在静态 html 中使用 +sidebar_position: 60 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +你可以使用 cdn 的方式使用 alova。 + + + + +```html + + + + + + + + + +
+
Loading...
+
{{ error.message }}
+ responseData: {{ data }} +
+ + + +``` + +
+ + +```html + + + + + + + + + + + +
+ + + +``` + +
+ + +:::tip + +svelte 依赖于编译工具,不能通过 CDN 直接使用,详情见 [svelte.dev](https://svelte.dev/) + +::: + + + + +```html + + + + + + + + + +
+
Loading...
+
{{ todo.error.message }}
+ responseData: {{ todo.data }} +
+ + + +``` + +
+
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/07-hide-recommend-tips.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/07-hide-recommend-tips.md new file mode 100644 index 000000000..f538b1e3b --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/07-hide-recommend-tips.md @@ -0,0 +1,46 @@ +--- +title: 隐藏推荐提示 +sidebar_position: 70 +--- + +:::info 版本要求 + +v2.7.0- + +::: + +alova 可以配合扩展库获得更好的开发体验,为了让更多开发者获得更好的开发体验,在使用时将会在控制台中推荐 alova 的扩展。 + +![tips](/img/alova-tips.png) + +这些提示代码将会在构建生产环境包时自动去除,如果你希望在开发环境隐藏它们,可以按以下方式: + +## Vite + +在`.env.development`文件中设置环境变量 **VITE_ALOVA_TIPS=0** + +```bash title=.env.development +VITE_ALOVA_TIPS=0 +``` + +:::warning 警告 +如果信息仍然存在,请尝试将 vite 的依赖缓存删除,它在`node_modules/.vite/deps`。 +::: + +## Webpack + +### Vue + +在`.env.development`文件中设置环境变量 **VUE_APP_ALOVA_TIPS=0** + +```bash title=.env.development +VUE_APP_ALOVA_TIPS=0 +``` + +### React + +在`.env.development`文件中设置环境变量 **REACT_APP_ALOVA_TIPS=0** + +```bash title=.env.development +REACT_APP_ALOVA_TIPS=0 +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/_category_.json b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/_category_.json new file mode 100644 index 000000000..352959533 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "其他", + "link": { + "type": "generated-index" + } +} diff --git a/package-lock.json b/package-lock.json index 61011949d..a644bff91 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,10 +10,10 @@ "dependencies": { "@codesandbox/sandpack-react": "^2.9.0", "@codesandbox/sandpack-themes": "^2.0.21", - "@docusaurus/core": "^3.1.1", - "@docusaurus/preset-classic": "^3.1.1", - "@docusaurus/theme-mermaid": "^3.1.1", - "@docusaurus/tsconfig": "^3.1.1", + "@docusaurus/core": "^3.4.0", + "@docusaurus/preset-classic": "^3.4.0", + "@docusaurus/theme-mermaid": "^3.4.0", + "@docusaurus/tsconfig": "^3.4.0", "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", "prism-react-renderer": "^2.1.0", @@ -72,74 +72,74 @@ } }, "node_modules/@algolia/cache-browser-local-storage": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.22.1.tgz", - "integrity": "sha512-Sw6IAmOCvvP6QNgY9j+Hv09mvkvEIDKjYW8ow0UDDAxSXy664RBNQk3i/0nt7gvceOJ6jGmOTimaZoY1THmU7g==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.23.3.tgz", + "integrity": "sha512-vRHXYCpPlTDE7i6UOy2xE03zHF2C8MEFjPN2v7fRbqVpcOvAUQK81x3Kc21xyb5aSIpYCjWCZbYZuz8Glyzyyg==", "dependencies": { - "@algolia/cache-common": "4.22.1" + "@algolia/cache-common": "4.23.3" } }, "node_modules/@algolia/cache-common": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.22.1.tgz", - "integrity": "sha512-TJMBKqZNKYB9TptRRjSUtevJeQVXRmg6rk9qgFKWvOy8jhCPdyNZV1nB3SKGufzvTVbomAukFR8guu/8NRKBTA==" + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.23.3.tgz", + "integrity": "sha512-h9XcNI6lxYStaw32pHpB1TMm0RuxphF+Ik4o7tcQiodEdpKK+wKufY6QXtba7t3k8eseirEMVB83uFFF3Nu54A==" }, "node_modules/@algolia/cache-in-memory": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.22.1.tgz", - "integrity": "sha512-ve+6Ac2LhwpufuWavM/aHjLoNz/Z/sYSgNIXsinGofWOysPilQZPUetqLj8vbvi+DHZZaYSEP9H5SRVXnpsNNw==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.23.3.tgz", + "integrity": "sha512-yvpbuUXg/+0rbcagxNT7un0eo3czx2Uf0y4eiR4z4SD7SiptwYTpbuS0IHxcLHG3lq22ukx1T6Kjtk/rT+mqNg==", "dependencies": { - "@algolia/cache-common": "4.22.1" + "@algolia/cache-common": "4.23.3" } }, "node_modules/@algolia/client-account": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.22.1.tgz", - "integrity": "sha512-k8m+oegM2zlns/TwZyi4YgCtyToackkOpE+xCaKCYfBfDtdGOaVZCM5YvGPtK+HGaJMIN/DoTL8asbM3NzHonw==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.23.3.tgz", + "integrity": "sha512-hpa6S5d7iQmretHHF40QGq6hz0anWEHGlULcTIT9tbUssWUriN9AUXIFQ8Ei4w9azD0hc1rUok9/DeQQobhQMA==", "dependencies": { - "@algolia/client-common": "4.22.1", - "@algolia/client-search": "4.22.1", - "@algolia/transporter": "4.22.1" + "@algolia/client-common": "4.23.3", + "@algolia/client-search": "4.23.3", + "@algolia/transporter": "4.23.3" } }, "node_modules/@algolia/client-analytics": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.22.1.tgz", - "integrity": "sha512-1ssi9pyxyQNN4a7Ji9R50nSdISIumMFDwKNuwZipB6TkauJ8J7ha/uO60sPJFqQyqvvI+px7RSNRQT3Zrvzieg==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.23.3.tgz", + "integrity": "sha512-LBsEARGS9cj8VkTAVEZphjxTjMVCci+zIIiRhpFun9jGDUlS1XmhCW7CTrnaWeIuCQS/2iPyRqSy1nXPjcBLRA==", "dependencies": { - "@algolia/client-common": "4.22.1", - "@algolia/client-search": "4.22.1", - "@algolia/requester-common": "4.22.1", - "@algolia/transporter": "4.22.1" + "@algolia/client-common": "4.23.3", + "@algolia/client-search": "4.23.3", + "@algolia/requester-common": "4.23.3", + "@algolia/transporter": "4.23.3" } }, "node_modules/@algolia/client-common": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.22.1.tgz", - "integrity": "sha512-IvaL5v9mZtm4k4QHbBGDmU3wa/mKokmqNBqPj0K7lcR8ZDKzUorhcGp/u8PkPC/e0zoHSTvRh7TRkGX3Lm7iOQ==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.23.3.tgz", + "integrity": "sha512-l6EiPxdAlg8CYhroqS5ybfIczsGUIAC47slLPOMDeKSVXYG1n0qGiz4RjAHLw2aD0xzh2EXZ7aRguPfz7UKDKw==", "dependencies": { - "@algolia/requester-common": "4.22.1", - "@algolia/transporter": "4.22.1" + "@algolia/requester-common": "4.23.3", + "@algolia/transporter": "4.23.3" } }, "node_modules/@algolia/client-personalization": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.22.1.tgz", - "integrity": "sha512-sl+/klQJ93+4yaqZ7ezOttMQ/nczly/3GmgZXJ1xmoewP5jmdP/X/nV5U7EHHH3hCUEHeN7X1nsIhGPVt9E1cQ==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.23.3.tgz", + "integrity": "sha512-3E3yF3Ocr1tB/xOZiuC3doHQBQ2zu2MPTYZ0d4lpfWads2WTKG7ZzmGnsHmm63RflvDeLK/UVx7j2b3QuwKQ2g==", "dependencies": { - "@algolia/client-common": "4.22.1", - "@algolia/requester-common": "4.22.1", - "@algolia/transporter": "4.22.1" + "@algolia/client-common": "4.23.3", + "@algolia/requester-common": "4.23.3", + "@algolia/transporter": "4.23.3" } }, "node_modules/@algolia/client-search": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.22.1.tgz", - "integrity": "sha512-yb05NA4tNaOgx3+rOxAmFztgMTtGBi97X7PC3jyNeGiwkAjOZc2QrdZBYyIdcDLoI09N0gjtpClcackoTN0gPA==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.23.3.tgz", + "integrity": "sha512-P4VAKFHqU0wx9O+q29Q8YVuaowaZ5EM77rxfmGnkHUJggh28useXQdopokgwMeYw2XUht49WX5RcTQ40rZIabw==", "dependencies": { - "@algolia/client-common": "4.22.1", - "@algolia/requester-common": "4.22.1", - "@algolia/transporter": "4.22.1" + "@algolia/client-common": "4.23.3", + "@algolia/requester-common": "4.23.3", + "@algolia/transporter": "4.23.3" } }, "node_modules/@algolia/events": { @@ -148,47 +148,65 @@ "integrity": "sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ==" }, "node_modules/@algolia/logger-common": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.22.1.tgz", - "integrity": "sha512-OnTFymd2odHSO39r4DSWRFETkBufnY2iGUZNrMXpIhF5cmFE8pGoINNPzwg02QLBlGSaLqdKy0bM8S0GyqPLBg==" + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.23.3.tgz", + "integrity": "sha512-y9kBtmJwiZ9ZZ+1Ek66P0M68mHQzKRxkW5kAAXYN/rdzgDN0d2COsViEFufxJ0pb45K4FRcfC7+33YB4BLrZ+g==" }, "node_modules/@algolia/logger-console": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.22.1.tgz", - "integrity": "sha512-O99rcqpVPKN1RlpgD6H3khUWylU24OXlzkavUAMy6QZd1776QAcauE3oP8CmD43nbaTjBexZj2nGsBH9Tc0FVA==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.23.3.tgz", + "integrity": "sha512-8xoiseoWDKuCVnWP8jHthgaeobDLolh00KJAdMe9XPrWPuf1by732jSpgy2BlsLTaT9m32pHI8CRfrOqQzHv3A==", "dependencies": { - "@algolia/logger-common": "4.22.1" + "@algolia/logger-common": "4.23.3" + } + }, + "node_modules/@algolia/recommend": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-4.23.3.tgz", + "integrity": "sha512-9fK4nXZF0bFkdcLBRDexsnGzVmu4TSYZqxdpgBW2tEyfuSSY54D4qSRkLmNkrrz4YFvdh2GM1gA8vSsnZPR73w==", + "dependencies": { + "@algolia/cache-browser-local-storage": "4.23.3", + "@algolia/cache-common": "4.23.3", + "@algolia/cache-in-memory": "4.23.3", + "@algolia/client-common": "4.23.3", + "@algolia/client-search": "4.23.3", + "@algolia/logger-common": "4.23.3", + "@algolia/logger-console": "4.23.3", + "@algolia/requester-browser-xhr": "4.23.3", + "@algolia/requester-common": "4.23.3", + "@algolia/requester-node-http": "4.23.3", + "@algolia/transporter": "4.23.3" } }, "node_modules/@algolia/requester-browser-xhr": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.22.1.tgz", - "integrity": "sha512-dtQGYIg6MteqT1Uay3J/0NDqD+UciHy3QgRbk7bNddOJu+p3hzjTRYESqEnoX/DpEkaNYdRHUKNylsqMpgwaEw==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.23.3.tgz", + "integrity": "sha512-jDWGIQ96BhXbmONAQsasIpTYWslyjkiGu0Quydjlowe+ciqySpiDUrJHERIRfELE5+wFc7hc1Q5hqjGoV7yghw==", "dependencies": { - "@algolia/requester-common": "4.22.1" + "@algolia/requester-common": "4.23.3" } }, "node_modules/@algolia/requester-common": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.22.1.tgz", - "integrity": "sha512-dgvhSAtg2MJnR+BxrIFqlLtkLlVVhas9HgYKMk2Uxiy5m6/8HZBL40JVAMb2LovoPFs9I/EWIoFVjOrFwzn5Qg==" + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.23.3.tgz", + "integrity": "sha512-xloIdr/bedtYEGcXCiF2muajyvRhwop4cMZo+K2qzNht0CMzlRkm8YsDdj5IaBhshqfgmBb3rTg4sL4/PpvLYw==" }, "node_modules/@algolia/requester-node-http": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.22.1.tgz", - "integrity": "sha512-JfmZ3MVFQkAU+zug8H3s8rZ6h0ahHZL/SpMaSasTCGYR5EEJsCc8SI5UZ6raPN2tjxa5bxS13BRpGSBUens7EA==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.23.3.tgz", + "integrity": "sha512-zgu++8Uj03IWDEJM3fuNl34s746JnZOWn1Uz5taV1dFyJhVM/kTNw9Ik7YJWiUNHJQXcaD8IXD1eCb0nq/aByA==", "dependencies": { - "@algolia/requester-common": "4.22.1" + "@algolia/requester-common": "4.23.3" } }, "node_modules/@algolia/transporter": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.22.1.tgz", - "integrity": "sha512-kzWgc2c9IdxMa3YqA6TN0NW5VrKYYW/BELIn7vnLyn+U/RFdZ4lxxt9/8yq3DKV5snvoDzzO4ClyejZRdV3lMQ==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.23.3.tgz", + "integrity": "sha512-Wjl5gttqnf/gQKJA+dafnD0Y6Yw97yvfY8R9h0dQltX1GXTgNs1zWgvtWW0tHl1EgMdhAyw189uWiZMnL3QebQ==", "dependencies": { - "@algolia/cache-common": "4.22.1", - "@algolia/logger-common": "4.22.1", - "@algolia/requester-common": "4.22.1" + "@algolia/cache-common": "4.23.3", + "@algolia/logger-common": "4.23.3", + "@algolia/requester-common": "4.23.3" } }, "node_modules/@ampproject/remapping": { @@ -535,9 +553,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.7.tgz", + "integrity": "sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==", "engines": { "node": ">=6.9.0" } @@ -1597,11 +1615,11 @@ } }, "node_modules/@babel/plugin-transform-react-constant-elements": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.22.5.tgz", - "integrity": "sha512-BF5SXoO+nX3h5OhlN78XbbDrBOffv+AxPP2ENaJOVqjWCgBDeOY3WcaUcddutGSfoap+5NEQ/q/4I3WZIvgkXA==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.24.7.tgz", + "integrity": "sha512-7LidzZfUXyfZ8/buRW6qIIHBY8wAZ1OrY9c/wTr8YhZ6vMPo+Uc/CVFLYY1spZrEQlD4w5u8wjqk5NQ3OVqQKA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -2323,9 +2341,9 @@ } }, "node_modules/@docusaurus/core": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.1.1.tgz", - "integrity": "sha512-2nQfKFcf+MLEM7JXsXwQxPOmQAR6ytKMZVSx7tVi9HEm9WtfwBH1fp6bn8Gj4zLUhjWKCLoysQ9/Wm+EZCQ4yQ==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.4.0.tgz", + "integrity": "sha512-g+0wwmN2UJsBqy2fQRQ6fhXruoEa62JDeEa5d8IdTJlMoaDaEDfHh7WjwGRn4opuTQWpjAwP/fbcgyHKlE+64w==", "dependencies": { "@babel/core": "^7.23.3", "@babel/generator": "^7.23.3", @@ -2337,15 +2355,12 @@ "@babel/runtime": "^7.22.6", "@babel/runtime-corejs3": "^7.22.6", "@babel/traverse": "^7.22.8", - "@docusaurus/cssnano-preset": "3.1.1", - "@docusaurus/logger": "3.1.1", - "@docusaurus/mdx-loader": "3.1.1", - "@docusaurus/react-loadable": "5.5.2", - "@docusaurus/utils": "3.1.1", - "@docusaurus/utils-common": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", - "@slorber/static-site-generator-webpack-plugin": "^4.0.7", - "@svgr/webpack": "^6.5.1", + "@docusaurus/cssnano-preset": "3.4.0", + "@docusaurus/logger": "3.4.0", + "@docusaurus/mdx-loader": "3.4.0", + "@docusaurus/utils": "3.4.0", + "@docusaurus/utils-common": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", "autoprefixer": "^10.4.14", "babel-loader": "^9.1.3", "babel-plugin-dynamic-import-node": "^2.3.3", @@ -2359,12 +2374,13 @@ "copy-webpack-plugin": "^11.0.0", "core-js": "^3.31.1", "css-loader": "^6.8.1", - "css-minimizer-webpack-plugin": "^4.2.2", - "cssnano": "^5.1.15", + "css-minimizer-webpack-plugin": "^5.0.1", + "cssnano": "^6.1.2", "del": "^6.1.1", "detect-port": "^1.5.1", "escape-html": "^1.0.3", "eta": "^2.2.0", + "eval": "^0.1.8", "file-loader": "^6.2.0", "fs-extra": "^11.1.1", "html-minifier-terser": "^7.2.0", @@ -2373,12 +2389,13 @@ "leven": "^3.1.0", "lodash": "^4.17.21", "mini-css-extract-plugin": "^2.7.6", + "p-map": "^4.0.0", "postcss": "^8.4.26", "postcss-loader": "^7.3.3", "prompts": "^2.4.2", "react-dev-utils": "^12.0.1", "react-helmet-async": "^1.3.0", - "react-loadable": "npm:@docusaurus/react-loadable@5.5.2", + "react-loadable": "npm:@docusaurus/react-loadable@6.0.0", "react-loadable-ssr-addon-v5-slorber": "^1.0.1", "react-router": "^5.3.4", "react-router-config": "^5.1.1", @@ -2409,13 +2426,13 @@ } }, "node_modules/@docusaurus/cssnano-preset": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-3.1.1.tgz", - "integrity": "sha512-LnoIDjJWbirdbVZDMq+4hwmrTl2yHDnBf9MLG9qyExeAE3ac35s4yUhJI8yyTCdixzNfKit4cbXblzzqMu4+8g==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-3.4.0.tgz", + "integrity": "sha512-qwLFSz6v/pZHy/UP32IrprmH5ORce86BGtN0eBtG75PpzQJAzp9gefspox+s8IEOr0oZKuQ/nhzZ3xwyc3jYJQ==", "dependencies": { - "cssnano-preset-advanced": "^5.3.10", - "postcss": "^8.4.26", - "postcss-sort-media-queries": "^4.4.1", + "cssnano-preset-advanced": "^6.1.2", + "postcss": "^8.4.38", + "postcss-sort-media-queries": "^5.2.0", "tslib": "^2.6.0" }, "engines": { @@ -2423,9 +2440,9 @@ } }, "node_modules/@docusaurus/logger": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-3.1.1.tgz", - "integrity": "sha512-BjkNDpQzewcTnST8trx4idSoAla6zZ3w22NqM/UMcFtvYJgmoE4layuTzlfql3VFPNuivvj7BOExa/+21y4X2Q==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-3.4.0.tgz", + "integrity": "sha512-bZwkX+9SJ8lB9kVRkXw+xvHYSMGG4bpYHKGXeXFvyVc79NMeeBSGgzd4TQLHH+DYeOJoCdl8flrFJVxlZ0wo/Q==", "dependencies": { "chalk": "^4.1.2", "tslib": "^2.6.0" @@ -2435,15 +2452,13 @@ } }, "node_modules/@docusaurus/mdx-loader": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.1.1.tgz", - "integrity": "sha512-xN2IccH9+sv7TmxwsDJNS97BHdmlqWwho+kIVY4tcCXkp+k4QuzvWBeunIMzeayY4Fu13A6sAjHGv5qm72KyGA==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.4.0.tgz", + "integrity": "sha512-kSSbrrk4nTjf4d+wtBA9H+FGauf2gCax89kV8SUSJu3qaTdSIKdWERlngsiHaCFgZ7laTJ8a67UFf+xlFPtuTw==", "dependencies": { - "@babel/parser": "^7.22.7", - "@babel/traverse": "^7.22.8", - "@docusaurus/logger": "3.1.1", - "@docusaurus/utils": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", + "@docusaurus/logger": "3.4.0", + "@docusaurus/utils": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", "@mdx-js/mdx": "^3.0.0", "@slorber/remark-comment": "^1.0.0", "escape-html": "^1.0.3", @@ -2475,18 +2490,17 @@ } }, "node_modules/@docusaurus/module-type-aliases": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.1.1.tgz", - "integrity": "sha512-xBJyx0TMfAfVZ9ZeIOb1awdXgR4YJMocIEzTps91rq+hJDFJgJaylDtmoRhUxkwuYmNK1GJpW95b7DLztSBJ3A==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.4.0.tgz", + "integrity": "sha512-A1AyS8WF5Bkjnb8s+guTDuYmUiwJzNrtchebBHpc0gz0PyHJNMaybUlSrmJjHVcGrya0LKI4YcR3lBDQfXRYLw==", "dependencies": { - "@docusaurus/react-loadable": "5.5.2", - "@docusaurus/types": "3.1.1", + "@docusaurus/types": "3.4.0", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", "@types/react-router-dom": "*", "react-helmet-async": "*", - "react-loadable": "npm:@docusaurus/react-loadable@5.5.2" + "react-loadable": "npm:@docusaurus/react-loadable@6.0.0" }, "peerDependencies": { "react": "*", @@ -2494,17 +2508,17 @@ } }, "node_modules/@docusaurus/plugin-content-blog": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.1.1.tgz", - "integrity": "sha512-ew/3VtVoG3emoAKmoZl7oKe1zdFOsI0NbcHS26kIxt2Z8vcXKCUgK9jJJrz0TbOipyETPhqwq4nbitrY3baibg==", - "dependencies": { - "@docusaurus/core": "3.1.1", - "@docusaurus/logger": "3.1.1", - "@docusaurus/mdx-loader": "3.1.1", - "@docusaurus/types": "3.1.1", - "@docusaurus/utils": "3.1.1", - "@docusaurus/utils-common": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.4.0.tgz", + "integrity": "sha512-vv6ZAj78ibR5Jh7XBUT4ndIjmlAxkijM3Sx5MAAzC1gyv0vupDQNhzuFg1USQmQVj3P5I6bquk12etPV3LJ+Xw==", + "dependencies": { + "@docusaurus/core": "3.4.0", + "@docusaurus/logger": "3.4.0", + "@docusaurus/mdx-loader": "3.4.0", + "@docusaurus/types": "3.4.0", + "@docusaurus/utils": "3.4.0", + "@docusaurus/utils-common": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", "cheerio": "^1.0.0-rc.12", "feed": "^4.2.2", "fs-extra": "^11.1.1", @@ -2525,17 +2539,18 @@ } }, "node_modules/@docusaurus/plugin-content-docs": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.1.1.tgz", - "integrity": "sha512-lhFq4E874zw0UOH7ujzxnCayOyAt0f9YPVYSb9ohxrdCM8B4szxitUw9rIX4V9JLLHVoqIJb6k+lJJ1jrcGJ0A==", - "dependencies": { - "@docusaurus/core": "3.1.1", - "@docusaurus/logger": "3.1.1", - "@docusaurus/mdx-loader": "3.1.1", - "@docusaurus/module-type-aliases": "3.1.1", - "@docusaurus/types": "3.1.1", - "@docusaurus/utils": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.4.0.tgz", + "integrity": "sha512-HkUCZffhBo7ocYheD9oZvMcDloRnGhBMOZRyVcAQRFmZPmNqSyISlXA1tQCIxW+r478fty97XXAGjNYzBjpCsg==", + "dependencies": { + "@docusaurus/core": "3.4.0", + "@docusaurus/logger": "3.4.0", + "@docusaurus/mdx-loader": "3.4.0", + "@docusaurus/module-type-aliases": "3.4.0", + "@docusaurus/types": "3.4.0", + "@docusaurus/utils": "3.4.0", + "@docusaurus/utils-common": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", "@types/react-router-config": "^5.0.7", "combine-promises": "^1.1.0", "fs-extra": "^11.1.1", @@ -2554,15 +2569,15 @@ } }, "node_modules/@docusaurus/plugin-content-pages": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.1.1.tgz", - "integrity": "sha512-NQHncNRAJbyLtgTim9GlEnNYsFhuCxaCNkMwikuxLTiGIPH7r/jpb7O3f3jUMYMebZZZrDq5S7om9a6rvB/YCA==", - "dependencies": { - "@docusaurus/core": "3.1.1", - "@docusaurus/mdx-loader": "3.1.1", - "@docusaurus/types": "3.1.1", - "@docusaurus/utils": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.4.0.tgz", + "integrity": "sha512-h2+VN/0JjpR8fIkDEAoadNjfR3oLzB+v1qSXbIAKjQ46JAHx3X22n9nqS+BWSQnTnp1AjkjSvZyJMekmcwxzxg==", + "dependencies": { + "@docusaurus/core": "3.4.0", + "@docusaurus/mdx-loader": "3.4.0", + "@docusaurus/types": "3.4.0", + "@docusaurus/utils": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", "fs-extra": "^11.1.1", "tslib": "^2.6.0", "webpack": "^5.88.1" @@ -2576,13 +2591,13 @@ } }, "node_modules/@docusaurus/plugin-debug": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-3.1.1.tgz", - "integrity": "sha512-xWeMkueM9wE/8LVvl4+Qf1WqwXmreMjI5Kgr7GYCDoJ8zu4kD+KaMhrh7py7MNM38IFvU1RfrGKacCEe2DRRfQ==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-3.4.0.tgz", + "integrity": "sha512-uV7FDUNXGyDSD3PwUaf5YijX91T5/H9SX4ErEcshzwgzWwBtK37nUWPU3ZLJfeTavX3fycTOqk9TglpOLaWkCg==", "dependencies": { - "@docusaurus/core": "3.1.1", - "@docusaurus/types": "3.1.1", - "@docusaurus/utils": "3.1.1", + "@docusaurus/core": "3.4.0", + "@docusaurus/types": "3.4.0", + "@docusaurus/utils": "3.4.0", "fs-extra": "^11.1.1", "react-json-view-lite": "^1.2.0", "tslib": "^2.6.0" @@ -2596,13 +2611,13 @@ } }, "node_modules/@docusaurus/plugin-google-analytics": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.1.1.tgz", - "integrity": "sha512-+q2UpWTqVi8GdlLoSlD5bS/YpxW+QMoBwrPrUH/NpvpuOi0Of7MTotsQf9JWd3hymZxl2uu1o3PIrbpxfeDFDQ==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.4.0.tgz", + "integrity": "sha512-mCArluxEGi3cmYHqsgpGGt3IyLCrFBxPsxNZ56Mpur0xSlInnIHoeLDH7FvVVcPJRPSQ9/MfRqLsainRw+BojA==", "dependencies": { - "@docusaurus/core": "3.1.1", - "@docusaurus/types": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", + "@docusaurus/core": "3.4.0", + "@docusaurus/types": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", "tslib": "^2.6.0" }, "engines": { @@ -2614,13 +2629,13 @@ } }, "node_modules/@docusaurus/plugin-google-gtag": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.1.1.tgz", - "integrity": "sha512-0mMPiBBlQ5LFHTtjxuvt/6yzh8v7OxLi3CbeEsxXZpUzcKO/GC7UA1VOWUoBeQzQL508J12HTAlR3IBU9OofSw==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.4.0.tgz", + "integrity": "sha512-Dsgg6PLAqzZw5wZ4QjUYc8Z2KqJqXxHxq3vIoyoBWiLEEfigIs7wHR+oiWUQy3Zk9MIk6JTYj7tMoQU0Jm3nqA==", "dependencies": { - "@docusaurus/core": "3.1.1", - "@docusaurus/types": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", + "@docusaurus/core": "3.4.0", + "@docusaurus/types": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", "@types/gtag.js": "^0.0.12", "tslib": "^2.6.0" }, @@ -2633,13 +2648,13 @@ } }, "node_modules/@docusaurus/plugin-google-tag-manager": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.1.1.tgz", - "integrity": "sha512-d07bsrMLdDIryDtY17DgqYUbjkswZQr8cLWl4tzXrt5OR/T/zxC1SYKajzB3fd87zTu5W5klV5GmUwcNSMXQXA==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.4.0.tgz", + "integrity": "sha512-O9tX1BTwxIhgXpOLpFDueYA9DWk69WCbDRrjYoMQtFHSkTyE7RhNgyjSPREUWJb9i+YUg3OrsvrBYRl64FCPCQ==", "dependencies": { - "@docusaurus/core": "3.1.1", - "@docusaurus/types": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", + "@docusaurus/core": "3.4.0", + "@docusaurus/types": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", "tslib": "^2.6.0" }, "engines": { @@ -2651,16 +2666,16 @@ } }, "node_modules/@docusaurus/plugin-sitemap": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.1.1.tgz", - "integrity": "sha512-iJ4hCaMmDaUqRv131XJdt/C/jJQx8UreDWTRqZKtNydvZVh/o4yXGRRFOplea1D9b/zpwL1Y+ZDwX7xMhIOTmg==", - "dependencies": { - "@docusaurus/core": "3.1.1", - "@docusaurus/logger": "3.1.1", - "@docusaurus/types": "3.1.1", - "@docusaurus/utils": "3.1.1", - "@docusaurus/utils-common": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.4.0.tgz", + "integrity": "sha512-+0VDvx9SmNrFNgwPoeoCha+tRoAjopwT0+pYO1xAbyLcewXSemq+eLxEa46Q1/aoOaJQ0qqHELuQM7iS2gp33Q==", + "dependencies": { + "@docusaurus/core": "3.4.0", + "@docusaurus/logger": "3.4.0", + "@docusaurus/types": "3.4.0", + "@docusaurus/utils": "3.4.0", + "@docusaurus/utils-common": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", "fs-extra": "^11.1.1", "sitemap": "^7.1.1", "tslib": "^2.6.0" @@ -2674,23 +2689,23 @@ } }, "node_modules/@docusaurus/preset-classic": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-3.1.1.tgz", - "integrity": "sha512-jG4ys/hWYf69iaN/xOmF+3kjs4Nnz1Ay3CjFLDtYa8KdxbmUhArA9HmP26ru5N0wbVWhY+6kmpYhTJpez5wTyg==", - "dependencies": { - "@docusaurus/core": "3.1.1", - "@docusaurus/plugin-content-blog": "3.1.1", - "@docusaurus/plugin-content-docs": "3.1.1", - "@docusaurus/plugin-content-pages": "3.1.1", - "@docusaurus/plugin-debug": "3.1.1", - "@docusaurus/plugin-google-analytics": "3.1.1", - "@docusaurus/plugin-google-gtag": "3.1.1", - "@docusaurus/plugin-google-tag-manager": "3.1.1", - "@docusaurus/plugin-sitemap": "3.1.1", - "@docusaurus/theme-classic": "3.1.1", - "@docusaurus/theme-common": "3.1.1", - "@docusaurus/theme-search-algolia": "3.1.1", - "@docusaurus/types": "3.1.1" + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-3.4.0.tgz", + "integrity": "sha512-Ohj6KB7siKqZaQhNJVMBBUzT3Nnp6eTKqO+FXO3qu/n1hJl3YLwVKTWBg28LF7MWrKu46UuYavwMRxud0VyqHg==", + "dependencies": { + "@docusaurus/core": "3.4.0", + "@docusaurus/plugin-content-blog": "3.4.0", + "@docusaurus/plugin-content-docs": "3.4.0", + "@docusaurus/plugin-content-pages": "3.4.0", + "@docusaurus/plugin-debug": "3.4.0", + "@docusaurus/plugin-google-analytics": "3.4.0", + "@docusaurus/plugin-google-gtag": "3.4.0", + "@docusaurus/plugin-google-tag-manager": "3.4.0", + "@docusaurus/plugin-sitemap": "3.4.0", + "@docusaurus/theme-classic": "3.4.0", + "@docusaurus/theme-common": "3.4.0", + "@docusaurus/theme-search-algolia": "3.4.0", + "@docusaurus/types": "3.4.0" }, "engines": { "node": ">=18.0" @@ -2700,35 +2715,23 @@ "react-dom": "^18.0.0" } }, - "node_modules/@docusaurus/react-loadable": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz", - "integrity": "sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ==", - "dependencies": { - "@types/react": "*", - "prop-types": "^15.6.2" - }, - "peerDependencies": { - "react": "*" - } - }, "node_modules/@docusaurus/theme-classic": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-3.1.1.tgz", - "integrity": "sha512-GiPE/jbWM8Qv1A14lk6s9fhc0LhPEQ00eIczRO4QL2nAQJZXkjPG6zaVx+1cZxPFWbAsqSjKe2lqkwF3fGkQ7Q==", - "dependencies": { - "@docusaurus/core": "3.1.1", - "@docusaurus/mdx-loader": "3.1.1", - "@docusaurus/module-type-aliases": "3.1.1", - "@docusaurus/plugin-content-blog": "3.1.1", - "@docusaurus/plugin-content-docs": "3.1.1", - "@docusaurus/plugin-content-pages": "3.1.1", - "@docusaurus/theme-common": "3.1.1", - "@docusaurus/theme-translations": "3.1.1", - "@docusaurus/types": "3.1.1", - "@docusaurus/utils": "3.1.1", - "@docusaurus/utils-common": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-3.4.0.tgz", + "integrity": "sha512-0IPtmxsBYv2adr1GnZRdMkEQt1YW6tpzrUPj02YxNpvJ5+ju4E13J5tB4nfdaen/tfR1hmpSPlTFPvTf4kwy8Q==", + "dependencies": { + "@docusaurus/core": "3.4.0", + "@docusaurus/mdx-loader": "3.4.0", + "@docusaurus/module-type-aliases": "3.4.0", + "@docusaurus/plugin-content-blog": "3.4.0", + "@docusaurus/plugin-content-docs": "3.4.0", + "@docusaurus/plugin-content-pages": "3.4.0", + "@docusaurus/theme-common": "3.4.0", + "@docusaurus/theme-translations": "3.4.0", + "@docusaurus/types": "3.4.0", + "@docusaurus/utils": "3.4.0", + "@docusaurus/utils-common": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", "copy-text-to-clipboard": "^3.2.0", @@ -2752,17 +2755,17 @@ } }, "node_modules/@docusaurus/theme-common": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.1.1.tgz", - "integrity": "sha512-38urZfeMhN70YaXkwIGXmcUcv2CEYK/2l4b05GkJPrbEbgpsIZM3Xc+Js2ehBGGZmfZq8GjjQ5RNQYG+MYzCYg==", - "dependencies": { - "@docusaurus/mdx-loader": "3.1.1", - "@docusaurus/module-type-aliases": "3.1.1", - "@docusaurus/plugin-content-blog": "3.1.1", - "@docusaurus/plugin-content-docs": "3.1.1", - "@docusaurus/plugin-content-pages": "3.1.1", - "@docusaurus/utils": "3.1.1", - "@docusaurus/utils-common": "3.1.1", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.4.0.tgz", + "integrity": "sha512-0A27alXuv7ZdCg28oPE8nH/Iz73/IUejVaCazqu9elS4ypjiLhK3KfzdSQBnL/g7YfHSlymZKdiOHEo8fJ0qMA==", + "dependencies": { + "@docusaurus/mdx-loader": "3.4.0", + "@docusaurus/module-type-aliases": "3.4.0", + "@docusaurus/plugin-content-blog": "3.4.0", + "@docusaurus/plugin-content-docs": "3.4.0", + "@docusaurus/plugin-content-pages": "3.4.0", + "@docusaurus/utils": "3.4.0", + "@docusaurus/utils-common": "3.4.0", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", @@ -2781,15 +2784,15 @@ } }, "node_modules/@docusaurus/theme-mermaid": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-mermaid/-/theme-mermaid-3.1.1.tgz", - "integrity": "sha512-O6u9/7QX/ZapV4HJJSzNs0Jir1KA/LRLORWYeDvbGswqZNusj6q4iLELrKIClysJ3PB3zWUzyKtI/wjIKiV1vA==", - "dependencies": { - "@docusaurus/core": "3.1.1", - "@docusaurus/module-type-aliases": "3.1.1", - "@docusaurus/theme-common": "3.1.1", - "@docusaurus/types": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-mermaid/-/theme-mermaid-3.4.0.tgz", + "integrity": "sha512-3w5QW0HEZ2O6x2w6lU3ZvOe1gNXP2HIoKDMJBil1VmLBc9PmpAG17VmfhI/p3L2etNmOiVs5GgniUqvn8AFEGQ==", + "dependencies": { + "@docusaurus/core": "3.4.0", + "@docusaurus/module-type-aliases": "3.4.0", + "@docusaurus/theme-common": "3.4.0", + "@docusaurus/types": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", "mermaid": "^10.4.0", "tslib": "^2.6.0" }, @@ -2802,18 +2805,18 @@ } }, "node_modules/@docusaurus/theme-search-algolia": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.1.1.tgz", - "integrity": "sha512-tBH9VY5EpRctVdaAhT+b1BY8y5dyHVZGFXyCHgTrvcXQy5CV4q7serEX7U3SveNT9zksmchPyct6i1sFDC4Z5g==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.4.0.tgz", + "integrity": "sha512-aiHFx7OCw4Wck1z6IoShVdUWIjntC8FHCw9c5dR8r3q4Ynh+zkS8y2eFFunN/DL6RXPzpnvKCg3vhLQYJDmT9Q==", "dependencies": { "@docsearch/react": "^3.5.2", - "@docusaurus/core": "3.1.1", - "@docusaurus/logger": "3.1.1", - "@docusaurus/plugin-content-docs": "3.1.1", - "@docusaurus/theme-common": "3.1.1", - "@docusaurus/theme-translations": "3.1.1", - "@docusaurus/utils": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", + "@docusaurus/core": "3.4.0", + "@docusaurus/logger": "3.4.0", + "@docusaurus/plugin-content-docs": "3.4.0", + "@docusaurus/theme-common": "3.4.0", + "@docusaurus/theme-translations": "3.4.0", + "@docusaurus/utils": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", "algoliasearch": "^4.18.0", "algoliasearch-helper": "^3.13.3", "clsx": "^2.0.0", @@ -2832,9 +2835,9 @@ } }, "node_modules/@docusaurus/theme-translations": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.1.1.tgz", - "integrity": "sha512-xvWQFwjxHphpJq5fgk37FXCDdAa2o+r7FX8IpMg+bGZBNXyWBu3MjZ+G4+eUVNpDhVinTc+j6ucL0Ain5KCGrg==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.4.0.tgz", + "integrity": "sha512-zSxCSpmQCCdQU5Q4CnX/ID8CSUUI3fvmq4hU/GNP/XoAWtXo9SAVnM3TzpU8Gb//H3WCsT8mJcTfyOk3d9ftNg==", "dependencies": { "fs-extra": "^11.1.1", "tslib": "^2.6.0" @@ -2844,14 +2847,14 @@ } }, "node_modules/@docusaurus/tsconfig": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/tsconfig/-/tsconfig-3.1.1.tgz", - "integrity": "sha512-FTBuY3KvaHfMVBgvlPmDQ+KS9Q/bYtVftq2ugou3PgBDJoQmw2aUZ4Sg15HKqLGbfIkxoy9t6cqE4Yw1Ta8Q1A==" + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/tsconfig/-/tsconfig-3.4.0.tgz", + "integrity": "sha512-0qENiJ+TRaeTzcg4olrnh0BQ7eCxTgbYWBnWUeQDc84UYkt/T3pDNnm3SiQkqPb+YQ1qtYFlC0RriAElclo8Dg==" }, "node_modules/@docusaurus/types": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.1.1.tgz", - "integrity": "sha512-grBqOLnubUecgKFXN9q3uit2HFbCxTWX4Fam3ZFbMN0sWX9wOcDoA7lwdX/8AmeL20Oc4kQvWVgNrsT8bKRvzg==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.4.0.tgz", + "integrity": "sha512-4jcDO8kXi5Cf9TcyikB/yKmz14f2RZ2qTRerbHAsS+5InE9ZgSLBNLsewtFTcTOXSVcbU3FoGOzcNWAmU1TR0A==", "dependencies": { "@mdx-js/mdx": "^3.0.0", "@types/history": "^4.7.11", @@ -2869,12 +2872,13 @@ } }, "node_modules/@docusaurus/utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.1.1.tgz", - "integrity": "sha512-ZJfJa5cJQtRYtqijsPEnAZoduW6sjAQ7ZCWSZavLcV10Fw0Z3gSaPKA/B4micvj2afRZ4gZxT7KfYqe5H8Cetg==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.4.0.tgz", + "integrity": "sha512-fRwnu3L3nnWaXOgs88BVBmG1yGjcQqZNHG+vInhEa2Sz2oQB+ZjbEMO5Rh9ePFpZ0YDiDUhpaVjwmS+AU2F14g==", "dependencies": { - "@docusaurus/logger": "3.1.1", - "@svgr/webpack": "^6.5.1", + "@docusaurus/logger": "3.4.0", + "@docusaurus/utils-common": "3.4.0", + "@svgr/webpack": "^8.1.0", "escape-string-regexp": "^4.0.0", "file-loader": "^6.2.0", "fs-extra": "^11.1.1", @@ -2885,10 +2889,12 @@ "js-yaml": "^4.1.0", "lodash": "^4.17.21", "micromatch": "^4.0.5", + "prompts": "^2.4.2", "resolve-pathname": "^3.0.0", "shelljs": "^0.8.5", "tslib": "^2.6.0", "url-loader": "^4.1.1", + "utility-types": "^3.10.0", "webpack": "^5.88.1" }, "engines": { @@ -2904,9 +2910,9 @@ } }, "node_modules/@docusaurus/utils-common": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.1.1.tgz", - "integrity": "sha512-eGne3olsIoNfPug5ixjepZAIxeYFzHHnor55Wb2P57jNbtVaFvij/T+MS8U0dtZRFi50QU+UPmRrXdVUM8uyMg==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.4.0.tgz", + "integrity": "sha512-NVx54Wr4rCEKsjOH5QEVvxIqVvm+9kh7q8aYTU5WzUU9/Hctd6aTrcZ3G0Id4zYJ+AeaG5K5qHA4CY5Kcm2iyQ==", "dependencies": { "tslib": "^2.6.0" }, @@ -2923,14 +2929,17 @@ } }, "node_modules/@docusaurus/utils-validation": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.1.1.tgz", - "integrity": "sha512-KlY4P9YVDnwL+nExvlIpu79abfEv6ZCHuOX4ZQ+gtip+Wxj0daccdReIWWtqxM/Fb5Cz1nQvUCc7VEtT8IBUAA==", - "dependencies": { - "@docusaurus/logger": "3.1.1", - "@docusaurus/utils": "3.1.1", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.4.0.tgz", + "integrity": "sha512-hYQ9fM+AXYVTWxJOT1EuNaRnrR2WGpRdLDQG07O8UOpsvCPWUVOeo26Rbm0JWY2sGLfzAb+tvJ62yF+8F+TV0g==", + "dependencies": { + "@docusaurus/logger": "3.4.0", + "@docusaurus/utils": "3.4.0", + "@docusaurus/utils-common": "3.4.0", + "fs-extra": "^11.2.0", "joi": "^17.9.2", "js-yaml": "^4.1.0", + "lodash": "^4.17.21", "tslib": "^2.6.0" }, "engines": { @@ -3276,30 +3285,17 @@ "micromark-util-symbol": "^1.0.1" } }, - "node_modules/@slorber/static-site-generator-webpack-plugin": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/@slorber/static-site-generator-webpack-plugin/-/static-site-generator-webpack-plugin-4.0.7.tgz", - "integrity": "sha512-Ug7x6z5lwrz0WqdnNFOMYrDQNTPAprvHLSh6+/fmml3qUiz6l5eq+2MzLKWtn/q5K5NpSiFsZTP/fck/3vjSxA==", - "dependencies": { - "eval": "^0.1.8", - "p-map": "^4.0.0", - "webpack-sources": "^3.2.2" - }, - "engines": { - "node": ">=14" - } - }, "node_modules/@stitches/core": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/@stitches/core/-/core-1.2.8.tgz", "integrity": "sha512-Gfkvwk9o9kE9r9XNBmJRfV8zONvXThnm1tcuojL04Uy5uRyqg93DC83lDebl0rocZCfKSjUv+fWYtMQmEDJldg==" }, "node_modules/@svgr/babel-plugin-add-jsx-attribute": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-6.5.1.tgz", - "integrity": "sha512-9PYGcXrAxitycIjRmZB+Q0JaN07GZIWaTBIGQzfaZv+qr1n8X1XUEJ5rZ/vx6OVD9RRYlrNnXWExQXcmZeD/BQ==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==", "engines": { - "node": ">=10" + "node": ">=14" }, "funding": { "type": "github", @@ -3340,11 +3336,11 @@ } }, "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-6.5.1.tgz", - "integrity": "sha512-8DPaVVE3fd5JKuIC29dqyMB54sA6mfgki2H2+swh+zNJoynC8pMPzOkidqHOSc6Wj032fhl8Z0TVn1GiPpAiJg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz", + "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==", "engines": { - "node": ">=10" + "node": ">=14" }, "funding": { "type": "github", @@ -3355,11 +3351,11 @@ } }, "node_modules/@svgr/babel-plugin-svg-dynamic-title": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-6.5.1.tgz", - "integrity": "sha512-FwOEi0Il72iAzlkaHrlemVurgSQRDFbk0OC8dSvD5fSBPHltNh7JtLsxmZUhjYBZo2PpcU/RJvvi6Q0l7O7ogw==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz", + "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==", "engines": { - "node": ">=10" + "node": ">=14" }, "funding": { "type": "github", @@ -3370,11 +3366,11 @@ } }, "node_modules/@svgr/babel-plugin-svg-em-dimensions": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-6.5.1.tgz", - "integrity": "sha512-gWGsiwjb4tw+ITOJ86ndY/DZZ6cuXMNE/SjcDRg+HLuCmwpcjOktwRF9WgAiycTqJD/QXqL2f8IzE2Rzh7aVXA==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz", + "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==", "engines": { - "node": ">=10" + "node": ">=14" }, "funding": { "type": "github", @@ -3385,11 +3381,11 @@ } }, "node_modules/@svgr/babel-plugin-transform-react-native-svg": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-6.5.1.tgz", - "integrity": "sha512-2jT3nTayyYP7kI6aGutkyfJ7UMGtuguD72OjeGLwVNyfPRBD8zQthlvL+fAbAKk5n9ZNcvFkp/b1lZ7VsYqVJg==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz", + "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==", "engines": { - "node": ">=10" + "node": ">=14" }, "funding": { "type": "github", @@ -3400,9 +3396,9 @@ } }, "node_modules/@svgr/babel-plugin-transform-svg-component": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-6.5.1.tgz", - "integrity": "sha512-a1p6LF5Jt33O3rZoVRBqdxL350oge54iZWHNI6LJB5tQ7EelvD/Mb1mfBiZNAan0dt4i3VArkFRjA4iObuNykQ==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz", + "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==", "engines": { "node": ">=12" }, @@ -3415,21 +3411,21 @@ } }, "node_modules/@svgr/babel-preset": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-6.5.1.tgz", - "integrity": "sha512-6127fvO/FF2oi5EzSQOAjo1LE3OtNVh11R+/8FXa+mHx1ptAaS4cknIjnUA7e6j6fwGGJ17NzaTJFUwOV2zwCw==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz", + "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==", "dependencies": { - "@svgr/babel-plugin-add-jsx-attribute": "^6.5.1", - "@svgr/babel-plugin-remove-jsx-attribute": "*", - "@svgr/babel-plugin-remove-jsx-empty-expression": "*", - "@svgr/babel-plugin-replace-jsx-attribute-value": "^6.5.1", - "@svgr/babel-plugin-svg-dynamic-title": "^6.5.1", - "@svgr/babel-plugin-svg-em-dimensions": "^6.5.1", - "@svgr/babel-plugin-transform-react-native-svg": "^6.5.1", - "@svgr/babel-plugin-transform-svg-component": "^6.5.1" + "@svgr/babel-plugin-add-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0", + "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0", + "@svgr/babel-plugin-svg-dynamic-title": "8.0.0", + "@svgr/babel-plugin-svg-em-dimensions": "8.0.0", + "@svgr/babel-plugin-transform-react-native-svg": "8.1.0", + "@svgr/babel-plugin-transform-svg-component": "8.0.0" }, "engines": { - "node": ">=10" + "node": ">=14" }, "funding": { "type": "github", @@ -3440,18 +3436,18 @@ } }, "node_modules/@svgr/core": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/core/-/core-6.5.1.tgz", - "integrity": "sha512-/xdLSWxK5QkqG524ONSjvg3V/FkNyCv538OIBdQqPNaAta3AsXj/Bd2FbvR87yMbXO2hFSWiAe/Q6IkVPDw+mw==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", + "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", "dependencies": { - "@babel/core": "^7.19.6", - "@svgr/babel-preset": "^6.5.1", - "@svgr/plugin-jsx": "^6.5.1", + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", "camelcase": "^6.2.0", - "cosmiconfig": "^7.0.1" + "cosmiconfig": "^8.1.3", + "snake-case": "^3.0.4" }, "engines": { - "node": ">=10" + "node": ">=14" }, "funding": { "type": "github", @@ -3459,15 +3455,15 @@ } }, "node_modules/@svgr/hast-util-to-babel-ast": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-6.5.1.tgz", - "integrity": "sha512-1hnUxxjd83EAxbL4a0JDJoD3Dao3hmjvyvyEV8PzWmLK3B9m9NPlW7GKjFyoWE8nM7HnXzPcmmSyOW8yOddSXw==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz", + "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==", "dependencies": { - "@babel/types": "^7.20.0", + "@babel/types": "^7.21.3", "entities": "^4.4.0" }, "engines": { - "node": ">=10" + "node": ">=14" }, "funding": { "type": "github", @@ -3475,37 +3471,37 @@ } }, "node_modules/@svgr/plugin-jsx": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-6.5.1.tgz", - "integrity": "sha512-+UdQxI3jgtSjCykNSlEMuy1jSRQlGC7pqBCPvkG/2dATdWo082zHTTK3uhnAju2/6XpE6B5mZ3z4Z8Ns01S8Gw==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz", + "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==", "dependencies": { - "@babel/core": "^7.19.6", - "@svgr/babel-preset": "^6.5.1", - "@svgr/hast-util-to-babel-ast": "^6.5.1", + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "@svgr/hast-util-to-babel-ast": "8.0.0", "svg-parser": "^2.0.4" }, "engines": { - "node": ">=10" + "node": ">=14" }, "funding": { "type": "github", "url": "https://github.com/sponsors/gregberge" }, "peerDependencies": { - "@svgr/core": "^6.0.0" + "@svgr/core": "*" } }, "node_modules/@svgr/plugin-svgo": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-6.5.1.tgz", - "integrity": "sha512-omvZKf8ixP9z6GWgwbtmP9qQMPX4ODXi+wzbVZgomNFsUIlHA1sf4fThdwTWSsZGgvGAG6yE+b/F5gWUkcZ/iQ==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-8.1.0.tgz", + "integrity": "sha512-Ywtl837OGO9pTLIN/onoWLmDQ4zFUycI1g76vuKGEz6evR/ZTJlJuz3G/fIkb6OVBJ2g0o6CGJzaEjfmEo3AHA==", "dependencies": { - "cosmiconfig": "^7.0.1", - "deepmerge": "^4.2.2", - "svgo": "^2.8.0" + "cosmiconfig": "^8.1.3", + "deepmerge": "^4.3.1", + "svgo": "^3.0.2" }, "engines": { - "node": ">=10" + "node": ">=14" }, "funding": { "type": "github", @@ -3516,21 +3512,21 @@ } }, "node_modules/@svgr/webpack": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-6.5.1.tgz", - "integrity": "sha512-cQ/AsnBkXPkEK8cLbv4Dm7JGXq2XrumKnL1dRpJD9rIO2fTIlJI9a1uCciYG1F2aUsox/hJQyNGbt3soDxSRkA==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-8.1.0.tgz", + "integrity": "sha512-LnhVjMWyMQV9ZmeEy26maJk+8HTIbd59cH4F2MJ439k9DqejRisfFNGAPvRYlKETuh9LrImlS8aKsBgKjMA8WA==", "dependencies": { - "@babel/core": "^7.19.6", - "@babel/plugin-transform-react-constant-elements": "^7.18.12", - "@babel/preset-env": "^7.19.4", + "@babel/core": "^7.21.3", + "@babel/plugin-transform-react-constant-elements": "^7.21.3", + "@babel/preset-env": "^7.20.2", "@babel/preset-react": "^7.18.6", - "@babel/preset-typescript": "^7.18.6", - "@svgr/core": "^6.5.1", - "@svgr/plugin-jsx": "^6.5.1", - "@svgr/plugin-svgo": "^6.5.1" + "@babel/preset-typescript": "^7.21.0", + "@svgr/core": "8.1.0", + "@svgr/plugin-jsx": "8.1.0", + "@svgr/plugin-svgo": "8.1.0" }, "engines": { - "node": ">=10" + "node": ">=14" }, "funding": { "type": "github", @@ -3914,9 +3910,9 @@ } }, "node_modules/@types/yargs": { - "version": "17.0.31", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.31.tgz", - "integrity": "sha512-bocYSx4DI8TmdlvxqGpVNXOgCNR1Jj0gNPhhAY+iz1rgKDAaYrAYdFYnhDV1IFuiuVc9HkOwyDcFxaTElF3/wg==", + "version": "17.0.32", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", + "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", "dependencies": { "@types/yargs-parser": "*" } @@ -4201,30 +4197,31 @@ } }, "node_modules/algoliasearch": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.22.1.tgz", - "integrity": "sha512-jwydKFQJKIx9kIZ8Jm44SdpigFwRGPESaxZBaHSV0XWN2yBJAOT4mT7ppvlrpA4UGzz92pqFnVKr/kaZXrcreg==", - "dependencies": { - "@algolia/cache-browser-local-storage": "4.22.1", - "@algolia/cache-common": "4.22.1", - "@algolia/cache-in-memory": "4.22.1", - "@algolia/client-account": "4.22.1", - "@algolia/client-analytics": "4.22.1", - "@algolia/client-common": "4.22.1", - "@algolia/client-personalization": "4.22.1", - "@algolia/client-search": "4.22.1", - "@algolia/logger-common": "4.22.1", - "@algolia/logger-console": "4.22.1", - "@algolia/requester-browser-xhr": "4.22.1", - "@algolia/requester-common": "4.22.1", - "@algolia/requester-node-http": "4.22.1", - "@algolia/transporter": "4.22.1" + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.23.3.tgz", + "integrity": "sha512-Le/3YgNvjW9zxIQMRhUHuhiUjAlKY/zsdZpfq4dlLqg6mEm0nL6yk+7f2hDOtLpxsgE4jSzDmvHL7nXdBp5feg==", + "dependencies": { + "@algolia/cache-browser-local-storage": "4.23.3", + "@algolia/cache-common": "4.23.3", + "@algolia/cache-in-memory": "4.23.3", + "@algolia/client-account": "4.23.3", + "@algolia/client-analytics": "4.23.3", + "@algolia/client-common": "4.23.3", + "@algolia/client-personalization": "4.23.3", + "@algolia/client-search": "4.23.3", + "@algolia/logger-common": "4.23.3", + "@algolia/logger-console": "4.23.3", + "@algolia/recommend": "4.23.3", + "@algolia/requester-browser-xhr": "4.23.3", + "@algolia/requester-common": "4.23.3", + "@algolia/requester-node-http": "4.23.3", + "@algolia/transporter": "4.23.3" } }, "node_modules/algoliasearch-helper": { - "version": "3.16.3", - "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.16.3.tgz", - "integrity": "sha512-1OuJT6sONAa9PxcOmWo5WCAT3jQSpCR9/m5Azujja7nhUQwAUDvaaAYrcmUySsrvHh74usZHbE3jFfGnWtZj8w==", + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.21.0.tgz", + "integrity": "sha512-hjVOrL15I3Y3K8xG0icwG1/tWE+MocqBrhW6uVBWpU+/kVEMK0BnM2xdssj6mZM61eJ4iRxHR0djEI3ENOpR8w==", "dependencies": { "@algolia/events": "^4.0.1" }, @@ -4348,9 +4345,9 @@ } }, "node_modules/autoprefixer": { - "version": "10.4.18", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.18.tgz", - "integrity": "sha512-1DKbDfsr6KUElM6wg+0zRNkB/Q7WcKYAaK+pzXn+Xqmszm/5Xa9coeNdtP88Vi+dPzZnMjhge8GIV49ZQkDa+g==", + "version": "10.4.19", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz", + "integrity": "sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==", "funding": [ { "type": "opencollective", @@ -4367,7 +4364,7 @@ ], "dependencies": { "browserslist": "^4.23.0", - "caniuse-lite": "^1.0.30001591", + "caniuse-lite": "^1.0.30001599", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", "picocolors": "^1.0.0", @@ -4762,9 +4759,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001596", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001596.tgz", - "integrity": "sha512-zpkZ+kEr6We7w63ORkoJ2pOfBwBkY/bJrG/UZ90qNb45Isblu8wzDgevEOrRL1r9dWayHjYiiyCMEXPn4DweGQ==", + "version": "1.0.30001632", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001632.tgz", + "integrity": "sha512-udx3o7yHJfUxMLkGohMlVHCvFvWmirKh9JAH/d7WOLPetlH+LTL5cocMZ0t7oZx/mdlOWXti97xLZWc8uURRHg==", "funding": [ { "type": "opencollective", @@ -5362,18 +5359,28 @@ } }, "node_modules/cosmiconfig": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", - "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" }, "engines": { - "node": ">=10" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, "node_modules/crelt": { @@ -5420,11 +5427,11 @@ } }, "node_modules/css-declaration-sorter": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz", - "integrity": "sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.2.0.tgz", + "integrity": "sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow==", "engines": { - "node": "^10 || ^12 || >=14" + "node": "^14 || ^16 || >=18" }, "peerDependencies": { "postcss": "^8.0.9" @@ -5456,16 +5463,16 @@ } }, "node_modules/css-minimizer-webpack-plugin": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-4.2.2.tgz", - "integrity": "sha512-s3Of/4jKfw1Hj9CxEO1E5oXhQAxlayuHO2y/ML+C6I9sQ7FdzfEV6QgMLN3vI+qFsjJGIAFLKtQK7t8BOXAIyA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-5.0.1.tgz", + "integrity": "sha512-3caImjKFQkS+ws1TGcFn0V1HyDJFq1Euy589JlD6/3rV2kj+w7r5G9WDMgSHvpvXHNZ2calVypZWuEDQd9wfLg==", "dependencies": { - "cssnano": "^5.1.8", - "jest-worker": "^29.1.2", - "postcss": "^8.4.17", - "schema-utils": "^4.0.0", - "serialize-javascript": "^6.0.0", - "source-map": "^0.6.1" + "@jridgewell/trace-mapping": "^0.3.18", + "cssnano": "^6.0.1", + "jest-worker": "^29.4.3", + "postcss": "^8.4.24", + "schema-utils": "^4.0.1", + "serialize-javascript": "^6.0.1" }, "engines": { "node": ">= 14.15.0" @@ -5498,14 +5505,6 @@ } } }, - "node_modules/css-minimizer-webpack-plugin/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/css-select": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", @@ -5522,23 +5521,15 @@ } }, "node_modules/css-tree": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", - "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", "dependencies": { - "mdn-data": "2.0.14", - "source-map": "^0.6.1" + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" }, "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/css-tree/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" } }, "node_modules/css-what": { @@ -5564,108 +5555,128 @@ } }, "node_modules/cssnano": { - "version": "5.1.15", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.15.tgz", - "integrity": "sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-6.1.2.tgz", + "integrity": "sha512-rYk5UeX7VAM/u0lNqewCdasdtPK81CgX8wJFLEIXHbV2oldWRgJAsZrdhRXkV1NJzA2g850KiFm9mMU2HxNxMA==", "dependencies": { - "cssnano-preset-default": "^5.2.14", - "lilconfig": "^2.0.3", - "yaml": "^1.10.2" + "cssnano-preset-default": "^6.1.2", + "lilconfig": "^3.1.1" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/cssnano" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/cssnano-preset-advanced": { - "version": "5.3.10", - "resolved": "https://registry.npmjs.org/cssnano-preset-advanced/-/cssnano-preset-advanced-5.3.10.tgz", - "integrity": "sha512-fnYJyCS9jgMU+cmHO1rPSPf9axbQyD7iUhLO5Df6O4G+fKIOMps+ZbU0PdGFejFBBZ3Pftf18fn1eG7MAPUSWQ==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/cssnano-preset-advanced/-/cssnano-preset-advanced-6.1.2.tgz", + "integrity": "sha512-Nhao7eD8ph2DoHolEzQs5CfRpiEP0xa1HBdnFZ82kvqdmbwVBUr2r1QuQ4t1pi+D1ZpqpcO4T+wy/7RxzJ/WPQ==", "dependencies": { - "autoprefixer": "^10.4.12", - "cssnano-preset-default": "^5.2.14", - "postcss-discard-unused": "^5.1.0", - "postcss-merge-idents": "^5.1.1", - "postcss-reduce-idents": "^5.2.0", - "postcss-zindex": "^5.1.0" + "autoprefixer": "^10.4.19", + "browserslist": "^4.23.0", + "cssnano-preset-default": "^6.1.2", + "postcss-discard-unused": "^6.0.5", + "postcss-merge-idents": "^6.0.3", + "postcss-reduce-idents": "^6.0.3", + "postcss-zindex": "^6.0.2" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/cssnano-preset-default": { - "version": "5.2.14", - "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.14.tgz", - "integrity": "sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A==", - "dependencies": { - "css-declaration-sorter": "^6.3.1", - "cssnano-utils": "^3.1.0", - "postcss-calc": "^8.2.3", - "postcss-colormin": "^5.3.1", - "postcss-convert-values": "^5.1.3", - "postcss-discard-comments": "^5.1.2", - "postcss-discard-duplicates": "^5.1.0", - "postcss-discard-empty": "^5.1.1", - "postcss-discard-overridden": "^5.1.0", - "postcss-merge-longhand": "^5.1.7", - "postcss-merge-rules": "^5.1.4", - "postcss-minify-font-values": "^5.1.0", - "postcss-minify-gradients": "^5.1.1", - "postcss-minify-params": "^5.1.4", - "postcss-minify-selectors": "^5.2.1", - "postcss-normalize-charset": "^5.1.0", - "postcss-normalize-display-values": "^5.1.0", - "postcss-normalize-positions": "^5.1.1", - "postcss-normalize-repeat-style": "^5.1.1", - "postcss-normalize-string": "^5.1.0", - "postcss-normalize-timing-functions": "^5.1.0", - "postcss-normalize-unicode": "^5.1.1", - "postcss-normalize-url": "^5.1.0", - "postcss-normalize-whitespace": "^5.1.1", - "postcss-ordered-values": "^5.1.3", - "postcss-reduce-initial": "^5.1.2", - "postcss-reduce-transforms": "^5.1.0", - "postcss-svgo": "^5.1.0", - "postcss-unique-selectors": "^5.1.1" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-6.1.2.tgz", + "integrity": "sha512-1C0C+eNaeN8OcHQa193aRgYexyJtU8XwbdieEjClw+J9d94E41LwT6ivKH0WT+fYwYWB0Zp3I3IZ7tI/BbUbrg==", + "dependencies": { + "browserslist": "^4.23.0", + "css-declaration-sorter": "^7.2.0", + "cssnano-utils": "^4.0.2", + "postcss-calc": "^9.0.1", + "postcss-colormin": "^6.1.0", + "postcss-convert-values": "^6.1.0", + "postcss-discard-comments": "^6.0.2", + "postcss-discard-duplicates": "^6.0.3", + "postcss-discard-empty": "^6.0.3", + "postcss-discard-overridden": "^6.0.2", + "postcss-merge-longhand": "^6.0.5", + "postcss-merge-rules": "^6.1.1", + "postcss-minify-font-values": "^6.1.0", + "postcss-minify-gradients": "^6.0.3", + "postcss-minify-params": "^6.1.0", + "postcss-minify-selectors": "^6.0.4", + "postcss-normalize-charset": "^6.0.2", + "postcss-normalize-display-values": "^6.0.2", + "postcss-normalize-positions": "^6.0.2", + "postcss-normalize-repeat-style": "^6.0.2", + "postcss-normalize-string": "^6.0.2", + "postcss-normalize-timing-functions": "^6.0.2", + "postcss-normalize-unicode": "^6.1.0", + "postcss-normalize-url": "^6.0.2", + "postcss-normalize-whitespace": "^6.0.2", + "postcss-ordered-values": "^6.0.2", + "postcss-reduce-initial": "^6.1.0", + "postcss-reduce-transforms": "^6.0.2", + "postcss-svgo": "^6.0.3", + "postcss-unique-selectors": "^6.0.4" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/cssnano-utils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz", - "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-4.0.2.tgz", + "integrity": "sha512-ZR1jHg+wZ8o4c3zqf1SIUSTIvm/9mU343FMR6Obe/unskbvpGhZOo1J6d/r8D1pzkRQYuwbcH3hToOuoA2G7oQ==", "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/csso": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", - "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", + "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", "dependencies": { - "css-tree": "^1.1.2" + "css-tree": "~2.2.0" }, "engines": { - "node": ">=8.0.0" + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" } }, + "node_modules/csso/node_modules/css-tree": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", + "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", + "dependencies": { + "mdn-data": "2.0.28", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.28", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", + "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==" + }, "node_modules/csstype": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", @@ -6815,16 +6826,13 @@ } }, "node_modules/estree-util-value-to-estree": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/estree-util-value-to-estree/-/estree-util-value-to-estree-3.0.1.tgz", - "integrity": "sha512-b2tdzTurEIbwRh+mKrEcaWfu1wgb8J1hVsgREg7FFiecWwK/PhO8X0kyc+0bIcKNtD4sqxIdNoRy6/p/TvECEA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/estree-util-value-to-estree/-/estree-util-value-to-estree-3.1.1.tgz", + "integrity": "sha512-5mvUrF2suuv5f5cGDnDphIy4/gW86z82kl5qG6mM9z04SEQI4FB5Apmaw/TGEf3l55nLtMs5s51dmhUzvAHQCA==", "dependencies": { "@types/estree": "^1.0.0", "is-plain-obj": "^4.0.0" }, - "engines": { - "node": ">=16.0.0" - }, "funding": { "url": "https://github.com/sponsors/remcohaszing" } @@ -7446,9 +7454,9 @@ } }, "node_modules/fs-extra": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", - "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -7850,9 +7858,9 @@ } }, "node_modules/hast-util-raw": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.0.2.tgz", - "integrity": "sha512-PldBy71wO9Uq1kyaMch9AHIghtQvIwxBUkv823pKmkTM3oV1JxtsTNYdevMxvUHqcnOAuO65JKU2+0NOxc2ksA==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.0.3.tgz", + "integrity": "sha512-ICWvVOF2fq4+7CMmtCPD5CM4QKjPbHpPotE6+8tDooV0ZuyJVUzHsrNX+O5NaRbieTf0F7FfeBOMAwi6Td0+yQ==", "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", @@ -8950,11 +8958,14 @@ } }, "node_modules/lilconfig": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", - "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", + "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", "engines": { - "node": ">=10" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" } }, "node_modules/lines-and-columns": { @@ -9501,9 +9512,9 @@ } }, "node_modules/mdn-data": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", - "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==" }, "node_modules/media-typer": { "version": "0.3.0", @@ -11916,17 +11927,6 @@ "node": ">=0.10.0" } }, - "node_modules/normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/npm-run-path": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", @@ -12421,9 +12421,9 @@ } }, "node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "version": "8.4.38", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", + "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", "funding": [ { "type": "opencollective", @@ -12439,114 +12439,117 @@ } ], "dependencies": { - "nanoid": "^3.3.6", + "nanoid": "^3.3.7", "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "source-map-js": "^1.2.0" }, "engines": { "node": "^10 || ^12 || >=14" } }, "node_modules/postcss-calc": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz", - "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-9.0.1.tgz", + "integrity": "sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==", "dependencies": { - "postcss-selector-parser": "^6.0.9", + "postcss-selector-parser": "^6.0.11", "postcss-value-parser": "^4.2.0" }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, "peerDependencies": { "postcss": "^8.2.2" } }, "node_modules/postcss-colormin": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.1.tgz", - "integrity": "sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-6.1.0.tgz", + "integrity": "sha512-x9yX7DOxeMAR+BgGVnNSAxmAj98NX/YxEMNFP+SDCEeNLb2r3i6Hh1ksMsnW8Ub5SLCpbescQqn9YEbE9554Sw==", "dependencies": { - "browserslist": "^4.21.4", + "browserslist": "^4.23.0", "caniuse-api": "^3.0.0", - "colord": "^2.9.1", + "colord": "^2.9.3", "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-convert-values": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.3.tgz", - "integrity": "sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-6.1.0.tgz", + "integrity": "sha512-zx8IwP/ts9WvUM6NkVSkiU902QZL1bwPhaVaLynPtCsOTqp+ZKbNi+s6XJg3rfqpKGA/oc7Oxk5t8pOQJcwl/w==", "dependencies": { - "browserslist": "^4.21.4", + "browserslist": "^4.23.0", "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-discard-comments": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz", - "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-6.0.2.tgz", + "integrity": "sha512-65w/uIqhSBBfQmYnG92FO1mWZjJ4GL5b8atm5Yw2UgrwD7HiNiSSNwJor1eCFGzUgYnN/iIknhNRVqjrrpuglw==", "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-discard-duplicates": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", - "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-6.0.3.tgz", + "integrity": "sha512-+JA0DCvc5XvFAxwx6f/e68gQu/7Z9ud584VLmcgto28eB8FqSFZwtrLwB5Kcp70eIoWP/HXqz4wpo8rD8gpsTw==", "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-discard-empty": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", - "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-6.0.3.tgz", + "integrity": "sha512-znyno9cHKQsK6PtxL5D19Fj9uwSzC2mB74cpT66fhgOadEUPyXFkbgwm5tvc3bt3NAy8ltE5MrghxovZRVnOjQ==", "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-discard-overridden": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", - "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-6.0.2.tgz", + "integrity": "sha512-j87xzI4LUggC5zND7KdjsI25APtyMuynXZSujByMaav2roV6OZX+8AaCUcZSWqckZpjAjRyFDdpqybgjFO0HJQ==", "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-discard-unused": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-5.1.0.tgz", - "integrity": "sha512-KwLWymI9hbwXmJa0dkrzpRbSJEh0vVUd7r8t0yOGPcfKzyJJxFM8kLyC5Ev9avji6nY95pOp1W6HqIrfT+0VGw==", + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-6.0.5.tgz", + "integrity": "sha512-wHalBlRHkaNnNwfC8z+ppX57VhvS+HWgjW508esjdaEYr3Mx7Gnn2xA4R/CKf5+Z9S5qsqC+Uzh4ueENWwCVUA==", "dependencies": { - "postcss-selector-parser": "^6.0.5" + "postcss-selector-parser": "^6.0.16" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-loader": { @@ -12570,136 +12573,111 @@ "webpack": "^5.0.0" } }, - "node_modules/postcss-loader/node_modules/cosmiconfig": { - "version": "8.3.6", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", - "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", - "dependencies": { - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0", - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - }, - "peerDependencies": { - "typescript": ">=4.9.5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, "node_modules/postcss-merge-idents": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-5.1.1.tgz", - "integrity": "sha512-pCijL1TREiCoog5nQp7wUe+TUonA2tC2sQ54UGeMmryK3UFGIYKqDyjnqd6RcuI4znFn9hWSLNN8xKE/vWcUQw==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-6.0.3.tgz", + "integrity": "sha512-1oIoAsODUs6IHQZkLQGO15uGEbK3EAl5wi9SS8hs45VgsxQfMnxvt+L+zIr7ifZFIH14cfAeVe2uCTa+SPRa3g==", "dependencies": { - "cssnano-utils": "^3.1.0", + "cssnano-utils": "^4.0.2", "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-merge-longhand": { - "version": "5.1.7", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.7.tgz", - "integrity": "sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==", + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-6.0.5.tgz", + "integrity": "sha512-5LOiordeTfi64QhICp07nzzuTDjNSO8g5Ksdibt44d+uvIIAE1oZdRn8y/W5ZtYgRH/lnLDlvi9F8btZcVzu3w==", "dependencies": { "postcss-value-parser": "^4.2.0", - "stylehacks": "^5.1.1" + "stylehacks": "^6.1.1" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-merge-rules": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.4.tgz", - "integrity": "sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-6.1.1.tgz", + "integrity": "sha512-KOdWF0gju31AQPZiD+2Ar9Qjowz1LTChSjFFbS+e2sFgc4uHOp3ZvVX4sNeTlk0w2O31ecFGgrFzhO0RSWbWwQ==", "dependencies": { - "browserslist": "^4.21.4", + "browserslist": "^4.23.0", "caniuse-api": "^3.0.0", - "cssnano-utils": "^3.1.0", - "postcss-selector-parser": "^6.0.5" + "cssnano-utils": "^4.0.2", + "postcss-selector-parser": "^6.0.16" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-minify-font-values": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz", - "integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-6.1.0.tgz", + "integrity": "sha512-gklfI/n+9rTh8nYaSJXlCo3nOKqMNkxuGpTn/Qm0gstL3ywTr9/WRKznE+oy6fvfolH6dF+QM4nCo8yPLdvGJg==", "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-minify-gradients": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz", - "integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-6.0.3.tgz", + "integrity": "sha512-4KXAHrYlzF0Rr7uc4VrfwDJ2ajrtNEpNEuLxFgwkhFZ56/7gaE4Nr49nLsQDZyUe+ds+kEhf+YAUolJiYXF8+Q==", "dependencies": { - "colord": "^2.9.1", - "cssnano-utils": "^3.1.0", + "colord": "^2.9.3", + "cssnano-utils": "^4.0.2", "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-minify-params": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.4.tgz", - "integrity": "sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-6.1.0.tgz", + "integrity": "sha512-bmSKnDtyyE8ujHQK0RQJDIKhQ20Jq1LYiez54WiaOoBtcSuflfK3Nm596LvbtlFcpipMjgClQGyGr7GAs+H1uA==", "dependencies": { - "browserslist": "^4.21.4", - "cssnano-utils": "^3.1.0", + "browserslist": "^4.23.0", + "cssnano-utils": "^4.0.2", "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-minify-selectors": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz", - "integrity": "sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-6.0.4.tgz", + "integrity": "sha512-L8dZSwNLgK7pjTto9PzWRoMbnLq5vsZSTu8+j1P/2GB8qdtGQfn+K1uSvFgYvgh83cbyxT5m43ZZhUMTJDSClQ==", "dependencies": { - "postcss-selector-parser": "^6.0.5" + "postcss-selector-parser": "^6.0.16" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-modules-extract-imports": { @@ -12758,192 +12736,191 @@ } }, "node_modules/postcss-normalize-charset": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", - "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-6.0.2.tgz", + "integrity": "sha512-a8N9czmdnrjPHa3DeFlwqst5eaL5W8jYu3EBbTTkI5FHkfMhFZh1EGbku6jhHhIzTA6tquI2P42NtZ59M/H/kQ==", "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-normalize-display-values": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz", - "integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-6.0.2.tgz", + "integrity": "sha512-8H04Mxsb82ON/aAkPeq8kcBbAtI5Q2a64X/mnRRfPXBq7XeogoQvReqxEfc0B4WPq1KimjezNC8flUtC3Qz6jg==", "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-normalize-positions": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz", - "integrity": "sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-6.0.2.tgz", + "integrity": "sha512-/JFzI441OAB9O7VnLA+RtSNZvQ0NCFZDOtp6QPFo1iIyawyXg0YI3CYM9HBy1WvwCRHnPep/BvI1+dGPKoXx/Q==", "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-normalize-repeat-style": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz", - "integrity": "sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-6.0.2.tgz", + "integrity": "sha512-YdCgsfHkJ2jEXwR4RR3Tm/iOxSfdRt7jplS6XRh9Js9PyCR/aka/FCb6TuHT2U8gQubbm/mPmF6L7FY9d79VwQ==", "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-normalize-string": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz", - "integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-6.0.2.tgz", + "integrity": "sha512-vQZIivlxlfqqMp4L9PZsFE4YUkWniziKjQWUtsxUiVsSSPelQydwS8Wwcuw0+83ZjPWNTl02oxlIvXsmmG+CiQ==", "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-normalize-timing-functions": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz", - "integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-6.0.2.tgz", + "integrity": "sha512-a+YrtMox4TBtId/AEwbA03VcJgtyW4dGBizPl7e88cTFULYsprgHWTbfyjSLyHeBcK/Q9JhXkt2ZXiwaVHoMzA==", "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-normalize-unicode": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.1.tgz", - "integrity": "sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-6.1.0.tgz", + "integrity": "sha512-QVC5TQHsVj33otj8/JD869Ndr5Xcc/+fwRh4HAsFsAeygQQXm+0PySrKbr/8tkDKzW+EVT3QkqZMfFrGiossDg==", "dependencies": { - "browserslist": "^4.21.4", + "browserslist": "^4.23.0", "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-normalize-url": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz", - "integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-6.0.2.tgz", + "integrity": "sha512-kVNcWhCeKAzZ8B4pv/DnrU1wNh458zBNp8dh4y5hhxih5RZQ12QWMuQrDgPRw3LRl8mN9vOVfHl7uhvHYMoXsQ==", "dependencies": { - "normalize-url": "^6.0.1", "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-normalize-whitespace": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz", - "integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-6.0.2.tgz", + "integrity": "sha512-sXZ2Nj1icbJOKmdjXVT9pnyHQKiSAyuNQHSgRCUgThn2388Y9cGVDR+E9J9iAYbSbLHI+UUwLVl1Wzco/zgv0Q==", "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-ordered-values": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz", - "integrity": "sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-6.0.2.tgz", + "integrity": "sha512-VRZSOB+JU32RsEAQrO94QPkClGPKJEL/Z9PCBImXMhIeK5KAYo6slP/hBYlLgrCjFxyqvn5VC81tycFEDBLG1Q==", "dependencies": { - "cssnano-utils": "^3.1.0", + "cssnano-utils": "^4.0.2", "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-reduce-idents": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-5.2.0.tgz", - "integrity": "sha512-BTrLjICoSB6gxbc58D5mdBK8OhXRDqud/zodYfdSi52qvDHdMwk+9kB9xsM8yJThH/sZU5A6QVSmMmaN001gIg==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-6.0.3.tgz", + "integrity": "sha512-G3yCqZDpsNPoQgbDUy3T0E6hqOQ5xigUtBQyrmq3tn2GxlyiL0yyl7H+T8ulQR6kOcHJ9t7/9H4/R2tv8tJbMA==", "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-reduce-initial": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.2.tgz", - "integrity": "sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-6.1.0.tgz", + "integrity": "sha512-RarLgBK/CrL1qZags04oKbVbrrVK2wcxhvta3GCxrZO4zveibqbRPmm2VI8sSgCXwoUHEliRSbOfpR0b/VIoiw==", "dependencies": { - "browserslist": "^4.21.4", + "browserslist": "^4.23.0", "caniuse-api": "^3.0.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-reduce-transforms": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz", - "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-6.0.2.tgz", + "integrity": "sha512-sB+Ya++3Xj1WaT9+5LOOdirAxP7dJZms3GRcYheSPi1PiTMigsxHAdkrbItHxwYHr4kt1zL7mmcHstgMYT+aiA==", "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-selector-parser": { - "version": "6.0.13", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz", - "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.0.tgz", + "integrity": "sha512-UMz42UD0UY0EApS0ZL9o1XnLhSTtvvvLe5Dc2H2O56fvRZi+KulDyf5ctDhhtYJBGKStV2FL1fy6253cmLgqVQ==", "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -12953,46 +12930,46 @@ } }, "node_modules/postcss-sort-media-queries": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/postcss-sort-media-queries/-/postcss-sort-media-queries-4.4.1.tgz", - "integrity": "sha512-QDESFzDDGKgpiIh4GYXsSy6sek2yAwQx1JASl5AxBtU1Lq2JfKBljIPNdil989NcSKRQX1ToiaKphImtBuhXWw==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/postcss-sort-media-queries/-/postcss-sort-media-queries-5.2.0.tgz", + "integrity": "sha512-AZ5fDMLD8SldlAYlvi8NIqo0+Z8xnXU2ia0jxmuhxAU+Lqt9K+AlmLNJ/zWEnE9x+Zx3qL3+1K20ATgNOr3fAA==", "dependencies": { - "sort-css-media-queries": "2.1.0" + "sort-css-media-queries": "2.2.0" }, "engines": { - "node": ">=10.0.0" + "node": ">=14.0.0" }, "peerDependencies": { - "postcss": "^8.4.16" + "postcss": "^8.4.23" } }, "node_modules/postcss-svgo": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz", - "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-6.0.3.tgz", + "integrity": "sha512-dlrahRmxP22bX6iKEjOM+c8/1p+81asjKT+V5lrgOH944ryx/OHpclnIbGsKVd3uWOXFLYJwCVf0eEkJGvO96g==", "dependencies": { "postcss-value-parser": "^4.2.0", - "svgo": "^2.7.0" + "svgo": "^3.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >= 18" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-unique-selectors": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz", - "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-6.0.4.tgz", + "integrity": "sha512-K38OCaIrO8+PzpArzkLKB42dSARtC2tmG6PvD4b1o1Q2E9Os8jzfWFfSy/rixsHwohtsDdFtAWGjFVFUdwYaMg==", "dependencies": { - "postcss-selector-parser": "^6.0.5" + "postcss-selector-parser": "^6.0.16" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-value-parser": { @@ -13001,14 +12978,14 @@ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, "node_modules/postcss-zindex": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-5.1.0.tgz", - "integrity": "sha512-fgFMf0OtVSBR1va1JNHYgMxYk73yhn/qb4uQDq1DLGYolz8gHCyr/sesEuGUaYs58E3ZJRcpoGuPVoB7Meiq9A==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-6.0.2.tgz", + "integrity": "sha512-5BxW9l1evPB/4ZIc+2GobEBoKC+h8gPGCMi+jxsYvd2x0mjq7wazk6DrP71pStqxE9Foxh5TVnonbWpFZzXaYg==", "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/prettier": { @@ -13505,9 +13482,9 @@ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, "node_modules/react-json-view-lite": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/react-json-view-lite/-/react-json-view-lite-1.2.1.tgz", - "integrity": "sha512-Itc0g86fytOmKZoIoJyGgvNqohWSbh3NXIKNgH6W6FT9PC1ck4xas1tT3Rr/b3UlFXyA9Jjaw9QSXdZy2JwGMQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/react-json-view-lite/-/react-json-view-lite-1.4.0.tgz", + "integrity": "sha512-wh6F6uJyYAmQ4fK0e8dSQMEWuvTs2Wr3el3sLD9bambX1+pSWUVXIz1RFaoy3TI1mZ0FqdpKq9YgbgTTgyrmXA==", "engines": { "node": ">=14" }, @@ -13517,12 +13494,11 @@ }, "node_modules/react-loadable": { "name": "@docusaurus/react-loadable", - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz", - "integrity": "sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-6.0.0.tgz", + "integrity": "sha512-YMMxTUQV/QFSnbgrP3tjDzLHRg7vsbMn8e9HAa8o/1iXoiomo48b7sk/kkmWEuWNDPJVlKSJRB6Y2fHqdJk+SQ==", "dependencies": { - "@types/react": "*", - "prop-types": "^15.6.2" + "@types/react": "*" }, "peerDependencies": { "react": "*" @@ -14161,9 +14137,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/sax": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", - "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==" + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==" }, "node_modules/scheduler": { "version": "0.23.0", @@ -14192,9 +14168,9 @@ } }, "node_modules/search-insights": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.13.0.tgz", - "integrity": "sha512-Orrsjf9trHHxFRuo9/rzm0KIWmgzE8RMlZMzuhZOJ01Rnz3D0YBAe+V6473t6/H6c7irs6Lt48brULAiRWb3Vw==", + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.14.0.tgz", + "integrity": "sha512-OLN6MsPMCghDOqlCtsIsYgtsC0pnwVTyT9Mu6A3ewOj1DxvzZF6COrn2g86E/c05xbktB0XN04m/t1Z+n+fTGw==", "peer": true }, "node_modules/section-matter": { @@ -14546,9 +14522,9 @@ "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" }, "node_modules/sitemap": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-7.1.1.tgz", - "integrity": "sha512-mK3aFtjz4VdJN0igpIJrinf3EO8U8mxOPsTBzSsy06UtjZQJ3YY3o3Xa7zSc5nMqcMrRwlChHZ18Kxg0caiPBg==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-7.1.2.tgz", + "integrity": "sha512-ARCqzHJ0p4gWt+j7NlU5eDlIO9+Rkr/JhPFZKKQ1l5GCus7rJH4UdrlVAh0xC/gDS/Qir2UMxqYNHtsKr2rpCw==", "dependencies": { "@types/node": "^17.0.5", "@types/sax": "^1.2.1", @@ -14587,6 +14563,15 @@ "node": ">=8" } }, + "node_modules/snake-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/sockjs": { "version": "0.3.24", "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", @@ -14598,9 +14583,9 @@ } }, "node_modules/sort-css-media-queries": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/sort-css-media-queries/-/sort-css-media-queries-2.1.0.tgz", - "integrity": "sha512-IeWvo8NkNiY2vVYdPa27MCQiR0MN0M80johAYFVxWWXQ44KU84WNxjslwBHmc/7ZL2ccwkM7/e6S5aiKZXm7jA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/sort-css-media-queries/-/sort-css-media-queries-2.2.0.tgz", + "integrity": "sha512-0xtkGhWCC9MGt/EzgnvbbbKhqWjl1+/rncmhTh5qCpbYguXh6S/qwePfv/JQ8jePXXmqingylxoC49pCkSPIbA==", "engines": { "node": ">= 6.3.0" } @@ -14614,9 +14599,9 @@ } }, "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", "engines": { "node": ">=0.10.0" } @@ -14691,12 +14676,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/stable": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", - "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", - "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility" - }, "node_modules/static-browser-server": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/static-browser-server/-/static-browser-server-1.0.3.tgz", @@ -14861,18 +14840,18 @@ } }, "node_modules/stylehacks": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", - "integrity": "sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-6.1.1.tgz", + "integrity": "sha512-gSTTEQ670cJNoaeIp9KX6lZmm8LJ3jPB5yJmX8Zq/wQxOsAFXV3qjWzHas3YYk1qesuVIyYWWUpZ0vSE/dTSGg==", "dependencies": { - "browserslist": "^4.21.4", - "postcss-selector-parser": "^6.0.4" + "browserslist": "^4.23.0", + "postcss-selector-parser": "^6.0.16" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/stylis": { @@ -14908,23 +14887,27 @@ "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==" }, "node_modules/svgo": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", - "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz", + "integrity": "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==", "dependencies": { "@trysound/sax": "0.2.0", "commander": "^7.2.0", - "css-select": "^4.1.3", - "css-tree": "^1.1.3", - "csso": "^4.2.0", - "picocolors": "^1.0.0", - "stable": "^0.1.8" + "css-select": "^5.1.0", + "css-tree": "^2.3.1", + "css-what": "^6.1.0", + "csso": "^5.0.5", + "picocolors": "^1.0.0" }, "bin": { "svgo": "bin/svgo" }, "engines": { - "node": ">=10.13.0" + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/svgo" } }, "node_modules/svgo/node_modules/commander": { @@ -14935,69 +14918,6 @@ "node": ">= 10" } }, - "node_modules/svgo/node_modules/css-select": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", - "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.0.1", - "domhandler": "^4.3.1", - "domutils": "^2.8.0", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/svgo/node_modules/dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/svgo/node_modules/domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/svgo/node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/svgo/node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, "node_modules/tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", @@ -16371,74 +16291,74 @@ "requires": {} }, "@algolia/cache-browser-local-storage": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.22.1.tgz", - "integrity": "sha512-Sw6IAmOCvvP6QNgY9j+Hv09mvkvEIDKjYW8ow0UDDAxSXy664RBNQk3i/0nt7gvceOJ6jGmOTimaZoY1THmU7g==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.23.3.tgz", + "integrity": "sha512-vRHXYCpPlTDE7i6UOy2xE03zHF2C8MEFjPN2v7fRbqVpcOvAUQK81x3Kc21xyb5aSIpYCjWCZbYZuz8Glyzyyg==", "requires": { - "@algolia/cache-common": "4.22.1" + "@algolia/cache-common": "4.23.3" } }, "@algolia/cache-common": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.22.1.tgz", - "integrity": "sha512-TJMBKqZNKYB9TptRRjSUtevJeQVXRmg6rk9qgFKWvOy8jhCPdyNZV1nB3SKGufzvTVbomAukFR8guu/8NRKBTA==" + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.23.3.tgz", + "integrity": "sha512-h9XcNI6lxYStaw32pHpB1TMm0RuxphF+Ik4o7tcQiodEdpKK+wKufY6QXtba7t3k8eseirEMVB83uFFF3Nu54A==" }, "@algolia/cache-in-memory": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.22.1.tgz", - "integrity": "sha512-ve+6Ac2LhwpufuWavM/aHjLoNz/Z/sYSgNIXsinGofWOysPilQZPUetqLj8vbvi+DHZZaYSEP9H5SRVXnpsNNw==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.23.3.tgz", + "integrity": "sha512-yvpbuUXg/+0rbcagxNT7un0eo3czx2Uf0y4eiR4z4SD7SiptwYTpbuS0IHxcLHG3lq22ukx1T6Kjtk/rT+mqNg==", "requires": { - "@algolia/cache-common": "4.22.1" + "@algolia/cache-common": "4.23.3" } }, "@algolia/client-account": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.22.1.tgz", - "integrity": "sha512-k8m+oegM2zlns/TwZyi4YgCtyToackkOpE+xCaKCYfBfDtdGOaVZCM5YvGPtK+HGaJMIN/DoTL8asbM3NzHonw==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.23.3.tgz", + "integrity": "sha512-hpa6S5d7iQmretHHF40QGq6hz0anWEHGlULcTIT9tbUssWUriN9AUXIFQ8Ei4w9azD0hc1rUok9/DeQQobhQMA==", "requires": { - "@algolia/client-common": "4.22.1", - "@algolia/client-search": "4.22.1", - "@algolia/transporter": "4.22.1" + "@algolia/client-common": "4.23.3", + "@algolia/client-search": "4.23.3", + "@algolia/transporter": "4.23.3" } }, "@algolia/client-analytics": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.22.1.tgz", - "integrity": "sha512-1ssi9pyxyQNN4a7Ji9R50nSdISIumMFDwKNuwZipB6TkauJ8J7ha/uO60sPJFqQyqvvI+px7RSNRQT3Zrvzieg==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.23.3.tgz", + "integrity": "sha512-LBsEARGS9cj8VkTAVEZphjxTjMVCci+zIIiRhpFun9jGDUlS1XmhCW7CTrnaWeIuCQS/2iPyRqSy1nXPjcBLRA==", "requires": { - "@algolia/client-common": "4.22.1", - "@algolia/client-search": "4.22.1", - "@algolia/requester-common": "4.22.1", - "@algolia/transporter": "4.22.1" + "@algolia/client-common": "4.23.3", + "@algolia/client-search": "4.23.3", + "@algolia/requester-common": "4.23.3", + "@algolia/transporter": "4.23.3" } }, "@algolia/client-common": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.22.1.tgz", - "integrity": "sha512-IvaL5v9mZtm4k4QHbBGDmU3wa/mKokmqNBqPj0K7lcR8ZDKzUorhcGp/u8PkPC/e0zoHSTvRh7TRkGX3Lm7iOQ==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.23.3.tgz", + "integrity": "sha512-l6EiPxdAlg8CYhroqS5ybfIczsGUIAC47slLPOMDeKSVXYG1n0qGiz4RjAHLw2aD0xzh2EXZ7aRguPfz7UKDKw==", "requires": { - "@algolia/requester-common": "4.22.1", - "@algolia/transporter": "4.22.1" + "@algolia/requester-common": "4.23.3", + "@algolia/transporter": "4.23.3" } }, "@algolia/client-personalization": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.22.1.tgz", - "integrity": "sha512-sl+/klQJ93+4yaqZ7ezOttMQ/nczly/3GmgZXJ1xmoewP5jmdP/X/nV5U7EHHH3hCUEHeN7X1nsIhGPVt9E1cQ==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.23.3.tgz", + "integrity": "sha512-3E3yF3Ocr1tB/xOZiuC3doHQBQ2zu2MPTYZ0d4lpfWads2WTKG7ZzmGnsHmm63RflvDeLK/UVx7j2b3QuwKQ2g==", "requires": { - "@algolia/client-common": "4.22.1", - "@algolia/requester-common": "4.22.1", - "@algolia/transporter": "4.22.1" + "@algolia/client-common": "4.23.3", + "@algolia/requester-common": "4.23.3", + "@algolia/transporter": "4.23.3" } }, "@algolia/client-search": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.22.1.tgz", - "integrity": "sha512-yb05NA4tNaOgx3+rOxAmFztgMTtGBi97X7PC3jyNeGiwkAjOZc2QrdZBYyIdcDLoI09N0gjtpClcackoTN0gPA==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.23.3.tgz", + "integrity": "sha512-P4VAKFHqU0wx9O+q29Q8YVuaowaZ5EM77rxfmGnkHUJggh28useXQdopokgwMeYw2XUht49WX5RcTQ40rZIabw==", "requires": { - "@algolia/client-common": "4.22.1", - "@algolia/requester-common": "4.22.1", - "@algolia/transporter": "4.22.1" + "@algolia/client-common": "4.23.3", + "@algolia/requester-common": "4.23.3", + "@algolia/transporter": "4.23.3" } }, "@algolia/events": { @@ -16447,47 +16367,65 @@ "integrity": "sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ==" }, "@algolia/logger-common": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.22.1.tgz", - "integrity": "sha512-OnTFymd2odHSO39r4DSWRFETkBufnY2iGUZNrMXpIhF5cmFE8pGoINNPzwg02QLBlGSaLqdKy0bM8S0GyqPLBg==" + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.23.3.tgz", + "integrity": "sha512-y9kBtmJwiZ9ZZ+1Ek66P0M68mHQzKRxkW5kAAXYN/rdzgDN0d2COsViEFufxJ0pb45K4FRcfC7+33YB4BLrZ+g==" }, "@algolia/logger-console": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.22.1.tgz", - "integrity": "sha512-O99rcqpVPKN1RlpgD6H3khUWylU24OXlzkavUAMy6QZd1776QAcauE3oP8CmD43nbaTjBexZj2nGsBH9Tc0FVA==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.23.3.tgz", + "integrity": "sha512-8xoiseoWDKuCVnWP8jHthgaeobDLolh00KJAdMe9XPrWPuf1by732jSpgy2BlsLTaT9m32pHI8CRfrOqQzHv3A==", + "requires": { + "@algolia/logger-common": "4.23.3" + } + }, + "@algolia/recommend": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-4.23.3.tgz", + "integrity": "sha512-9fK4nXZF0bFkdcLBRDexsnGzVmu4TSYZqxdpgBW2tEyfuSSY54D4qSRkLmNkrrz4YFvdh2GM1gA8vSsnZPR73w==", "requires": { - "@algolia/logger-common": "4.22.1" + "@algolia/cache-browser-local-storage": "4.23.3", + "@algolia/cache-common": "4.23.3", + "@algolia/cache-in-memory": "4.23.3", + "@algolia/client-common": "4.23.3", + "@algolia/client-search": "4.23.3", + "@algolia/logger-common": "4.23.3", + "@algolia/logger-console": "4.23.3", + "@algolia/requester-browser-xhr": "4.23.3", + "@algolia/requester-common": "4.23.3", + "@algolia/requester-node-http": "4.23.3", + "@algolia/transporter": "4.23.3" } }, "@algolia/requester-browser-xhr": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.22.1.tgz", - "integrity": "sha512-dtQGYIg6MteqT1Uay3J/0NDqD+UciHy3QgRbk7bNddOJu+p3hzjTRYESqEnoX/DpEkaNYdRHUKNylsqMpgwaEw==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.23.3.tgz", + "integrity": "sha512-jDWGIQ96BhXbmONAQsasIpTYWslyjkiGu0Quydjlowe+ciqySpiDUrJHERIRfELE5+wFc7hc1Q5hqjGoV7yghw==", "requires": { - "@algolia/requester-common": "4.22.1" + "@algolia/requester-common": "4.23.3" } }, "@algolia/requester-common": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.22.1.tgz", - "integrity": "sha512-dgvhSAtg2MJnR+BxrIFqlLtkLlVVhas9HgYKMk2Uxiy5m6/8HZBL40JVAMb2LovoPFs9I/EWIoFVjOrFwzn5Qg==" + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.23.3.tgz", + "integrity": "sha512-xloIdr/bedtYEGcXCiF2muajyvRhwop4cMZo+K2qzNht0CMzlRkm8YsDdj5IaBhshqfgmBb3rTg4sL4/PpvLYw==" }, "@algolia/requester-node-http": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.22.1.tgz", - "integrity": "sha512-JfmZ3MVFQkAU+zug8H3s8rZ6h0ahHZL/SpMaSasTCGYR5EEJsCc8SI5UZ6raPN2tjxa5bxS13BRpGSBUens7EA==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.23.3.tgz", + "integrity": "sha512-zgu++8Uj03IWDEJM3fuNl34s746JnZOWn1Uz5taV1dFyJhVM/kTNw9Ik7YJWiUNHJQXcaD8IXD1eCb0nq/aByA==", "requires": { - "@algolia/requester-common": "4.22.1" + "@algolia/requester-common": "4.23.3" } }, "@algolia/transporter": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.22.1.tgz", - "integrity": "sha512-kzWgc2c9IdxMa3YqA6TN0NW5VrKYYW/BELIn7vnLyn+U/RFdZ4lxxt9/8yq3DKV5snvoDzzO4ClyejZRdV3lMQ==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.23.3.tgz", + "integrity": "sha512-Wjl5gttqnf/gQKJA+dafnD0Y6Yw97yvfY8R9h0dQltX1GXTgNs1zWgvtWW0tHl1EgMdhAyw189uWiZMnL3QebQ==", "requires": { - "@algolia/cache-common": "4.22.1", - "@algolia/logger-common": "4.22.1", - "@algolia/requester-common": "4.22.1" + "@algolia/cache-common": "4.23.3", + "@algolia/logger-common": "4.23.3", + "@algolia/requester-common": "4.23.3" } }, "@ampproject/remapping": { @@ -16750,9 +16688,9 @@ } }, "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==" + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.7.tgz", + "integrity": "sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==" }, "@babel/helper-remap-async-to-generator": { "version": "7.22.20", @@ -17428,11 +17366,11 @@ } }, "@babel/plugin-transform-react-constant-elements": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.22.5.tgz", - "integrity": "sha512-BF5SXoO+nX3h5OhlN78XbbDrBOffv+AxPP2ENaJOVqjWCgBDeOY3WcaUcddutGSfoap+5NEQ/q/4I3WZIvgkXA==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.24.7.tgz", + "integrity": "sha512-7LidzZfUXyfZ8/buRW6qIIHBY8wAZ1OrY9c/wTr8YhZ6vMPo+Uc/CVFLYY1spZrEQlD4w5u8wjqk5NQ3OVqQKA==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.7" } }, "@babel/plugin-transform-react-display-name": { @@ -17972,9 +17910,9 @@ } }, "@docusaurus/core": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.1.1.tgz", - "integrity": "sha512-2nQfKFcf+MLEM7JXsXwQxPOmQAR6ytKMZVSx7tVi9HEm9WtfwBH1fp6bn8Gj4zLUhjWKCLoysQ9/Wm+EZCQ4yQ==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.4.0.tgz", + "integrity": "sha512-g+0wwmN2UJsBqy2fQRQ6fhXruoEa62JDeEa5d8IdTJlMoaDaEDfHh7WjwGRn4opuTQWpjAwP/fbcgyHKlE+64w==", "requires": { "@babel/core": "^7.23.3", "@babel/generator": "^7.23.3", @@ -17986,15 +17924,12 @@ "@babel/runtime": "^7.22.6", "@babel/runtime-corejs3": "^7.22.6", "@babel/traverse": "^7.22.8", - "@docusaurus/cssnano-preset": "3.1.1", - "@docusaurus/logger": "3.1.1", - "@docusaurus/mdx-loader": "3.1.1", - "@docusaurus/react-loadable": "5.5.2", - "@docusaurus/utils": "3.1.1", - "@docusaurus/utils-common": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", - "@slorber/static-site-generator-webpack-plugin": "^4.0.7", - "@svgr/webpack": "^6.5.1", + "@docusaurus/cssnano-preset": "3.4.0", + "@docusaurus/logger": "3.4.0", + "@docusaurus/mdx-loader": "3.4.0", + "@docusaurus/utils": "3.4.0", + "@docusaurus/utils-common": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", "autoprefixer": "^10.4.14", "babel-loader": "^9.1.3", "babel-plugin-dynamic-import-node": "^2.3.3", @@ -18008,12 +17943,13 @@ "copy-webpack-plugin": "^11.0.0", "core-js": "^3.31.1", "css-loader": "^6.8.1", - "css-minimizer-webpack-plugin": "^4.2.2", - "cssnano": "^5.1.15", + "css-minimizer-webpack-plugin": "^5.0.1", + "cssnano": "^6.1.2", "del": "^6.1.1", "detect-port": "^1.5.1", "escape-html": "^1.0.3", "eta": "^2.2.0", + "eval": "^0.1.8", "file-loader": "^6.2.0", "fs-extra": "^11.1.1", "html-minifier-terser": "^7.2.0", @@ -18022,12 +17958,13 @@ "leven": "^3.1.0", "lodash": "^4.17.21", "mini-css-extract-plugin": "^2.7.6", + "p-map": "^4.0.0", "postcss": "^8.4.26", "postcss-loader": "^7.3.3", "prompts": "^2.4.2", "react-dev-utils": "^12.0.1", "react-helmet-async": "^1.3.0", - "react-loadable": "npm:@docusaurus/react-loadable@5.5.2", + "react-loadable": "npm:@docusaurus/react-loadable@6.0.0", "react-loadable-ssr-addon-v5-slorber": "^1.0.1", "react-router": "^5.3.4", "react-router-config": "^5.1.1", @@ -18048,35 +17985,33 @@ } }, "@docusaurus/cssnano-preset": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-3.1.1.tgz", - "integrity": "sha512-LnoIDjJWbirdbVZDMq+4hwmrTl2yHDnBf9MLG9qyExeAE3ac35s4yUhJI8yyTCdixzNfKit4cbXblzzqMu4+8g==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-3.4.0.tgz", + "integrity": "sha512-qwLFSz6v/pZHy/UP32IrprmH5ORce86BGtN0eBtG75PpzQJAzp9gefspox+s8IEOr0oZKuQ/nhzZ3xwyc3jYJQ==", "requires": { - "cssnano-preset-advanced": "^5.3.10", - "postcss": "^8.4.26", - "postcss-sort-media-queries": "^4.4.1", + "cssnano-preset-advanced": "^6.1.2", + "postcss": "^8.4.38", + "postcss-sort-media-queries": "^5.2.0", "tslib": "^2.6.0" } }, "@docusaurus/logger": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-3.1.1.tgz", - "integrity": "sha512-BjkNDpQzewcTnST8trx4idSoAla6zZ3w22NqM/UMcFtvYJgmoE4layuTzlfql3VFPNuivvj7BOExa/+21y4X2Q==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-3.4.0.tgz", + "integrity": "sha512-bZwkX+9SJ8lB9kVRkXw+xvHYSMGG4bpYHKGXeXFvyVc79NMeeBSGgzd4TQLHH+DYeOJoCdl8flrFJVxlZ0wo/Q==", "requires": { "chalk": "^4.1.2", "tslib": "^2.6.0" } }, "@docusaurus/mdx-loader": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.1.1.tgz", - "integrity": "sha512-xN2IccH9+sv7TmxwsDJNS97BHdmlqWwho+kIVY4tcCXkp+k4QuzvWBeunIMzeayY4Fu13A6sAjHGv5qm72KyGA==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.4.0.tgz", + "integrity": "sha512-kSSbrrk4nTjf4d+wtBA9H+FGauf2gCax89kV8SUSJu3qaTdSIKdWERlngsiHaCFgZ7laTJ8a67UFf+xlFPtuTw==", "requires": { - "@babel/parser": "^7.22.7", - "@babel/traverse": "^7.22.8", - "@docusaurus/logger": "3.1.1", - "@docusaurus/utils": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", + "@docusaurus/logger": "3.4.0", + "@docusaurus/utils": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", "@mdx-js/mdx": "^3.0.0", "@slorber/remark-comment": "^1.0.0", "escape-html": "^1.0.3", @@ -18101,32 +18036,31 @@ } }, "@docusaurus/module-type-aliases": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.1.1.tgz", - "integrity": "sha512-xBJyx0TMfAfVZ9ZeIOb1awdXgR4YJMocIEzTps91rq+hJDFJgJaylDtmoRhUxkwuYmNK1GJpW95b7DLztSBJ3A==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.4.0.tgz", + "integrity": "sha512-A1AyS8WF5Bkjnb8s+guTDuYmUiwJzNrtchebBHpc0gz0PyHJNMaybUlSrmJjHVcGrya0LKI4YcR3lBDQfXRYLw==", "requires": { - "@docusaurus/react-loadable": "5.5.2", - "@docusaurus/types": "3.1.1", + "@docusaurus/types": "3.4.0", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", "@types/react-router-dom": "*", "react-helmet-async": "*", - "react-loadable": "npm:@docusaurus/react-loadable@5.5.2" + "react-loadable": "npm:@docusaurus/react-loadable@6.0.0" } }, "@docusaurus/plugin-content-blog": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.1.1.tgz", - "integrity": "sha512-ew/3VtVoG3emoAKmoZl7oKe1zdFOsI0NbcHS26kIxt2Z8vcXKCUgK9jJJrz0TbOipyETPhqwq4nbitrY3baibg==", - "requires": { - "@docusaurus/core": "3.1.1", - "@docusaurus/logger": "3.1.1", - "@docusaurus/mdx-loader": "3.1.1", - "@docusaurus/types": "3.1.1", - "@docusaurus/utils": "3.1.1", - "@docusaurus/utils-common": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.4.0.tgz", + "integrity": "sha512-vv6ZAj78ibR5Jh7XBUT4ndIjmlAxkijM3Sx5MAAzC1gyv0vupDQNhzuFg1USQmQVj3P5I6bquk12etPV3LJ+Xw==", + "requires": { + "@docusaurus/core": "3.4.0", + "@docusaurus/logger": "3.4.0", + "@docusaurus/mdx-loader": "3.4.0", + "@docusaurus/types": "3.4.0", + "@docusaurus/utils": "3.4.0", + "@docusaurus/utils-common": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", "cheerio": "^1.0.0-rc.12", "feed": "^4.2.2", "fs-extra": "^11.1.1", @@ -18140,17 +18074,18 @@ } }, "@docusaurus/plugin-content-docs": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.1.1.tgz", - "integrity": "sha512-lhFq4E874zw0UOH7ujzxnCayOyAt0f9YPVYSb9ohxrdCM8B4szxitUw9rIX4V9JLLHVoqIJb6k+lJJ1jrcGJ0A==", - "requires": { - "@docusaurus/core": "3.1.1", - "@docusaurus/logger": "3.1.1", - "@docusaurus/mdx-loader": "3.1.1", - "@docusaurus/module-type-aliases": "3.1.1", - "@docusaurus/types": "3.1.1", - "@docusaurus/utils": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.4.0.tgz", + "integrity": "sha512-HkUCZffhBo7ocYheD9oZvMcDloRnGhBMOZRyVcAQRFmZPmNqSyISlXA1tQCIxW+r478fty97XXAGjNYzBjpCsg==", + "requires": { + "@docusaurus/core": "3.4.0", + "@docusaurus/logger": "3.4.0", + "@docusaurus/mdx-loader": "3.4.0", + "@docusaurus/module-type-aliases": "3.4.0", + "@docusaurus/types": "3.4.0", + "@docusaurus/utils": "3.4.0", + "@docusaurus/utils-common": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", "@types/react-router-config": "^5.0.7", "combine-promises": "^1.1.0", "fs-extra": "^11.1.1", @@ -18162,129 +18097,120 @@ } }, "@docusaurus/plugin-content-pages": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.1.1.tgz", - "integrity": "sha512-NQHncNRAJbyLtgTim9GlEnNYsFhuCxaCNkMwikuxLTiGIPH7r/jpb7O3f3jUMYMebZZZrDq5S7om9a6rvB/YCA==", - "requires": { - "@docusaurus/core": "3.1.1", - "@docusaurus/mdx-loader": "3.1.1", - "@docusaurus/types": "3.1.1", - "@docusaurus/utils": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.4.0.tgz", + "integrity": "sha512-h2+VN/0JjpR8fIkDEAoadNjfR3oLzB+v1qSXbIAKjQ46JAHx3X22n9nqS+BWSQnTnp1AjkjSvZyJMekmcwxzxg==", + "requires": { + "@docusaurus/core": "3.4.0", + "@docusaurus/mdx-loader": "3.4.0", + "@docusaurus/types": "3.4.0", + "@docusaurus/utils": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", "fs-extra": "^11.1.1", "tslib": "^2.6.0", "webpack": "^5.88.1" } }, "@docusaurus/plugin-debug": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-3.1.1.tgz", - "integrity": "sha512-xWeMkueM9wE/8LVvl4+Qf1WqwXmreMjI5Kgr7GYCDoJ8zu4kD+KaMhrh7py7MNM38IFvU1RfrGKacCEe2DRRfQ==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-3.4.0.tgz", + "integrity": "sha512-uV7FDUNXGyDSD3PwUaf5YijX91T5/H9SX4ErEcshzwgzWwBtK37nUWPU3ZLJfeTavX3fycTOqk9TglpOLaWkCg==", "requires": { - "@docusaurus/core": "3.1.1", - "@docusaurus/types": "3.1.1", - "@docusaurus/utils": "3.1.1", + "@docusaurus/core": "3.4.0", + "@docusaurus/types": "3.4.0", + "@docusaurus/utils": "3.4.0", "fs-extra": "^11.1.1", "react-json-view-lite": "^1.2.0", "tslib": "^2.6.0" } }, "@docusaurus/plugin-google-analytics": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.1.1.tgz", - "integrity": "sha512-+q2UpWTqVi8GdlLoSlD5bS/YpxW+QMoBwrPrUH/NpvpuOi0Of7MTotsQf9JWd3hymZxl2uu1o3PIrbpxfeDFDQ==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.4.0.tgz", + "integrity": "sha512-mCArluxEGi3cmYHqsgpGGt3IyLCrFBxPsxNZ56Mpur0xSlInnIHoeLDH7FvVVcPJRPSQ9/MfRqLsainRw+BojA==", "requires": { - "@docusaurus/core": "3.1.1", - "@docusaurus/types": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", + "@docusaurus/core": "3.4.0", + "@docusaurus/types": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", "tslib": "^2.6.0" } }, "@docusaurus/plugin-google-gtag": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.1.1.tgz", - "integrity": "sha512-0mMPiBBlQ5LFHTtjxuvt/6yzh8v7OxLi3CbeEsxXZpUzcKO/GC7UA1VOWUoBeQzQL508J12HTAlR3IBU9OofSw==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.4.0.tgz", + "integrity": "sha512-Dsgg6PLAqzZw5wZ4QjUYc8Z2KqJqXxHxq3vIoyoBWiLEEfigIs7wHR+oiWUQy3Zk9MIk6JTYj7tMoQU0Jm3nqA==", "requires": { - "@docusaurus/core": "3.1.1", - "@docusaurus/types": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", + "@docusaurus/core": "3.4.0", + "@docusaurus/types": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", "@types/gtag.js": "^0.0.12", "tslib": "^2.6.0" } }, "@docusaurus/plugin-google-tag-manager": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.1.1.tgz", - "integrity": "sha512-d07bsrMLdDIryDtY17DgqYUbjkswZQr8cLWl4tzXrt5OR/T/zxC1SYKajzB3fd87zTu5W5klV5GmUwcNSMXQXA==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.4.0.tgz", + "integrity": "sha512-O9tX1BTwxIhgXpOLpFDueYA9DWk69WCbDRrjYoMQtFHSkTyE7RhNgyjSPREUWJb9i+YUg3OrsvrBYRl64FCPCQ==", "requires": { - "@docusaurus/core": "3.1.1", - "@docusaurus/types": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", + "@docusaurus/core": "3.4.0", + "@docusaurus/types": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", "tslib": "^2.6.0" } }, "@docusaurus/plugin-sitemap": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.1.1.tgz", - "integrity": "sha512-iJ4hCaMmDaUqRv131XJdt/C/jJQx8UreDWTRqZKtNydvZVh/o4yXGRRFOplea1D9b/zpwL1Y+ZDwX7xMhIOTmg==", - "requires": { - "@docusaurus/core": "3.1.1", - "@docusaurus/logger": "3.1.1", - "@docusaurus/types": "3.1.1", - "@docusaurus/utils": "3.1.1", - "@docusaurus/utils-common": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.4.0.tgz", + "integrity": "sha512-+0VDvx9SmNrFNgwPoeoCha+tRoAjopwT0+pYO1xAbyLcewXSemq+eLxEa46Q1/aoOaJQ0qqHELuQM7iS2gp33Q==", + "requires": { + "@docusaurus/core": "3.4.0", + "@docusaurus/logger": "3.4.0", + "@docusaurus/types": "3.4.0", + "@docusaurus/utils": "3.4.0", + "@docusaurus/utils-common": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", "fs-extra": "^11.1.1", "sitemap": "^7.1.1", "tslib": "^2.6.0" } }, "@docusaurus/preset-classic": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-3.1.1.tgz", - "integrity": "sha512-jG4ys/hWYf69iaN/xOmF+3kjs4Nnz1Ay3CjFLDtYa8KdxbmUhArA9HmP26ru5N0wbVWhY+6kmpYhTJpez5wTyg==", - "requires": { - "@docusaurus/core": "3.1.1", - "@docusaurus/plugin-content-blog": "3.1.1", - "@docusaurus/plugin-content-docs": "3.1.1", - "@docusaurus/plugin-content-pages": "3.1.1", - "@docusaurus/plugin-debug": "3.1.1", - "@docusaurus/plugin-google-analytics": "3.1.1", - "@docusaurus/plugin-google-gtag": "3.1.1", - "@docusaurus/plugin-google-tag-manager": "3.1.1", - "@docusaurus/plugin-sitemap": "3.1.1", - "@docusaurus/theme-classic": "3.1.1", - "@docusaurus/theme-common": "3.1.1", - "@docusaurus/theme-search-algolia": "3.1.1", - "@docusaurus/types": "3.1.1" - } - }, - "@docusaurus/react-loadable": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz", - "integrity": "sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ==", - "requires": { - "@types/react": "*", - "prop-types": "^15.6.2" + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-3.4.0.tgz", + "integrity": "sha512-Ohj6KB7siKqZaQhNJVMBBUzT3Nnp6eTKqO+FXO3qu/n1hJl3YLwVKTWBg28LF7MWrKu46UuYavwMRxud0VyqHg==", + "requires": { + "@docusaurus/core": "3.4.0", + "@docusaurus/plugin-content-blog": "3.4.0", + "@docusaurus/plugin-content-docs": "3.4.0", + "@docusaurus/plugin-content-pages": "3.4.0", + "@docusaurus/plugin-debug": "3.4.0", + "@docusaurus/plugin-google-analytics": "3.4.0", + "@docusaurus/plugin-google-gtag": "3.4.0", + "@docusaurus/plugin-google-tag-manager": "3.4.0", + "@docusaurus/plugin-sitemap": "3.4.0", + "@docusaurus/theme-classic": "3.4.0", + "@docusaurus/theme-common": "3.4.0", + "@docusaurus/theme-search-algolia": "3.4.0", + "@docusaurus/types": "3.4.0" } }, "@docusaurus/theme-classic": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-3.1.1.tgz", - "integrity": "sha512-GiPE/jbWM8Qv1A14lk6s9fhc0LhPEQ00eIczRO4QL2nAQJZXkjPG6zaVx+1cZxPFWbAsqSjKe2lqkwF3fGkQ7Q==", - "requires": { - "@docusaurus/core": "3.1.1", - "@docusaurus/mdx-loader": "3.1.1", - "@docusaurus/module-type-aliases": "3.1.1", - "@docusaurus/plugin-content-blog": "3.1.1", - "@docusaurus/plugin-content-docs": "3.1.1", - "@docusaurus/plugin-content-pages": "3.1.1", - "@docusaurus/theme-common": "3.1.1", - "@docusaurus/theme-translations": "3.1.1", - "@docusaurus/types": "3.1.1", - "@docusaurus/utils": "3.1.1", - "@docusaurus/utils-common": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-3.4.0.tgz", + "integrity": "sha512-0IPtmxsBYv2adr1GnZRdMkEQt1YW6tpzrUPj02YxNpvJ5+ju4E13J5tB4nfdaen/tfR1hmpSPlTFPvTf4kwy8Q==", + "requires": { + "@docusaurus/core": "3.4.0", + "@docusaurus/mdx-loader": "3.4.0", + "@docusaurus/module-type-aliases": "3.4.0", + "@docusaurus/plugin-content-blog": "3.4.0", + "@docusaurus/plugin-content-docs": "3.4.0", + "@docusaurus/plugin-content-pages": "3.4.0", + "@docusaurus/theme-common": "3.4.0", + "@docusaurus/theme-translations": "3.4.0", + "@docusaurus/types": "3.4.0", + "@docusaurus/utils": "3.4.0", + "@docusaurus/utils-common": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", "copy-text-to-clipboard": "^3.2.0", @@ -18301,17 +18227,17 @@ } }, "@docusaurus/theme-common": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.1.1.tgz", - "integrity": "sha512-38urZfeMhN70YaXkwIGXmcUcv2CEYK/2l4b05GkJPrbEbgpsIZM3Xc+Js2ehBGGZmfZq8GjjQ5RNQYG+MYzCYg==", - "requires": { - "@docusaurus/mdx-loader": "3.1.1", - "@docusaurus/module-type-aliases": "3.1.1", - "@docusaurus/plugin-content-blog": "3.1.1", - "@docusaurus/plugin-content-docs": "3.1.1", - "@docusaurus/plugin-content-pages": "3.1.1", - "@docusaurus/utils": "3.1.1", - "@docusaurus/utils-common": "3.1.1", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.4.0.tgz", + "integrity": "sha512-0A27alXuv7ZdCg28oPE8nH/Iz73/IUejVaCazqu9elS4ypjiLhK3KfzdSQBnL/g7YfHSlymZKdiOHEo8fJ0qMA==", + "requires": { + "@docusaurus/mdx-loader": "3.4.0", + "@docusaurus/module-type-aliases": "3.4.0", + "@docusaurus/plugin-content-blog": "3.4.0", + "@docusaurus/plugin-content-docs": "3.4.0", + "@docusaurus/plugin-content-pages": "3.4.0", + "@docusaurus/utils": "3.4.0", + "@docusaurus/utils-common": "3.4.0", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", @@ -18323,32 +18249,32 @@ } }, "@docusaurus/theme-mermaid": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-mermaid/-/theme-mermaid-3.1.1.tgz", - "integrity": "sha512-O6u9/7QX/ZapV4HJJSzNs0Jir1KA/LRLORWYeDvbGswqZNusj6q4iLELrKIClysJ3PB3zWUzyKtI/wjIKiV1vA==", - "requires": { - "@docusaurus/core": "3.1.1", - "@docusaurus/module-type-aliases": "3.1.1", - "@docusaurus/theme-common": "3.1.1", - "@docusaurus/types": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-mermaid/-/theme-mermaid-3.4.0.tgz", + "integrity": "sha512-3w5QW0HEZ2O6x2w6lU3ZvOe1gNXP2HIoKDMJBil1VmLBc9PmpAG17VmfhI/p3L2etNmOiVs5GgniUqvn8AFEGQ==", + "requires": { + "@docusaurus/core": "3.4.0", + "@docusaurus/module-type-aliases": "3.4.0", + "@docusaurus/theme-common": "3.4.0", + "@docusaurus/types": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", "mermaid": "^10.4.0", "tslib": "^2.6.0" } }, "@docusaurus/theme-search-algolia": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.1.1.tgz", - "integrity": "sha512-tBH9VY5EpRctVdaAhT+b1BY8y5dyHVZGFXyCHgTrvcXQy5CV4q7serEX7U3SveNT9zksmchPyct6i1sFDC4Z5g==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.4.0.tgz", + "integrity": "sha512-aiHFx7OCw4Wck1z6IoShVdUWIjntC8FHCw9c5dR8r3q4Ynh+zkS8y2eFFunN/DL6RXPzpnvKCg3vhLQYJDmT9Q==", "requires": { "@docsearch/react": "^3.5.2", - "@docusaurus/core": "3.1.1", - "@docusaurus/logger": "3.1.1", - "@docusaurus/plugin-content-docs": "3.1.1", - "@docusaurus/theme-common": "3.1.1", - "@docusaurus/theme-translations": "3.1.1", - "@docusaurus/utils": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", + "@docusaurus/core": "3.4.0", + "@docusaurus/logger": "3.4.0", + "@docusaurus/plugin-content-docs": "3.4.0", + "@docusaurus/theme-common": "3.4.0", + "@docusaurus/theme-translations": "3.4.0", + "@docusaurus/utils": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", "algoliasearch": "^4.18.0", "algoliasearch-helper": "^3.13.3", "clsx": "^2.0.0", @@ -18360,23 +18286,23 @@ } }, "@docusaurus/theme-translations": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.1.1.tgz", - "integrity": "sha512-xvWQFwjxHphpJq5fgk37FXCDdAa2o+r7FX8IpMg+bGZBNXyWBu3MjZ+G4+eUVNpDhVinTc+j6ucL0Ain5KCGrg==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.4.0.tgz", + "integrity": "sha512-zSxCSpmQCCdQU5Q4CnX/ID8CSUUI3fvmq4hU/GNP/XoAWtXo9SAVnM3TzpU8Gb//H3WCsT8mJcTfyOk3d9ftNg==", "requires": { "fs-extra": "^11.1.1", "tslib": "^2.6.0" } }, "@docusaurus/tsconfig": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/tsconfig/-/tsconfig-3.1.1.tgz", - "integrity": "sha512-FTBuY3KvaHfMVBgvlPmDQ+KS9Q/bYtVftq2ugou3PgBDJoQmw2aUZ4Sg15HKqLGbfIkxoy9t6cqE4Yw1Ta8Q1A==" + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/tsconfig/-/tsconfig-3.4.0.tgz", + "integrity": "sha512-0qENiJ+TRaeTzcg4olrnh0BQ7eCxTgbYWBnWUeQDc84UYkt/T3pDNnm3SiQkqPb+YQ1qtYFlC0RriAElclo8Dg==" }, "@docusaurus/types": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.1.1.tgz", - "integrity": "sha512-grBqOLnubUecgKFXN9q3uit2HFbCxTWX4Fam3ZFbMN0sWX9wOcDoA7lwdX/8AmeL20Oc4kQvWVgNrsT8bKRvzg==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.4.0.tgz", + "integrity": "sha512-4jcDO8kXi5Cf9TcyikB/yKmz14f2RZ2qTRerbHAsS+5InE9ZgSLBNLsewtFTcTOXSVcbU3FoGOzcNWAmU1TR0A==", "requires": { "@mdx-js/mdx": "^3.0.0", "@types/history": "^4.7.11", @@ -18390,12 +18316,13 @@ } }, "@docusaurus/utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.1.1.tgz", - "integrity": "sha512-ZJfJa5cJQtRYtqijsPEnAZoduW6sjAQ7ZCWSZavLcV10Fw0Z3gSaPKA/B4micvj2afRZ4gZxT7KfYqe5H8Cetg==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.4.0.tgz", + "integrity": "sha512-fRwnu3L3nnWaXOgs88BVBmG1yGjcQqZNHG+vInhEa2Sz2oQB+ZjbEMO5Rh9ePFpZ0YDiDUhpaVjwmS+AU2F14g==", "requires": { - "@docusaurus/logger": "3.1.1", - "@svgr/webpack": "^6.5.1", + "@docusaurus/logger": "3.4.0", + "@docusaurus/utils-common": "3.4.0", + "@svgr/webpack": "^8.1.0", "escape-string-regexp": "^4.0.0", "file-loader": "^6.2.0", "fs-extra": "^11.1.1", @@ -18406,30 +18333,35 @@ "js-yaml": "^4.1.0", "lodash": "^4.17.21", "micromatch": "^4.0.5", + "prompts": "^2.4.2", "resolve-pathname": "^3.0.0", "shelljs": "^0.8.5", "tslib": "^2.6.0", "url-loader": "^4.1.1", + "utility-types": "^3.10.0", "webpack": "^5.88.1" } }, "@docusaurus/utils-common": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.1.1.tgz", - "integrity": "sha512-eGne3olsIoNfPug5ixjepZAIxeYFzHHnor55Wb2P57jNbtVaFvij/T+MS8U0dtZRFi50QU+UPmRrXdVUM8uyMg==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.4.0.tgz", + "integrity": "sha512-NVx54Wr4rCEKsjOH5QEVvxIqVvm+9kh7q8aYTU5WzUU9/Hctd6aTrcZ3G0Id4zYJ+AeaG5K5qHA4CY5Kcm2iyQ==", "requires": { "tslib": "^2.6.0" } }, "@docusaurus/utils-validation": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.1.1.tgz", - "integrity": "sha512-KlY4P9YVDnwL+nExvlIpu79abfEv6ZCHuOX4ZQ+gtip+Wxj0daccdReIWWtqxM/Fb5Cz1nQvUCc7VEtT8IBUAA==", - "requires": { - "@docusaurus/logger": "3.1.1", - "@docusaurus/utils": "3.1.1", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.4.0.tgz", + "integrity": "sha512-hYQ9fM+AXYVTWxJOT1EuNaRnrR2WGpRdLDQG07O8UOpsvCPWUVOeo26Rbm0JWY2sGLfzAb+tvJ62yF+8F+TV0g==", + "requires": { + "@docusaurus/logger": "3.4.0", + "@docusaurus/utils": "3.4.0", + "@docusaurus/utils-common": "3.4.0", + "fs-extra": "^11.2.0", "joi": "^17.9.2", "js-yaml": "^4.1.0", + "lodash": "^4.17.21", "tslib": "^2.6.0" } }, @@ -18718,25 +18650,15 @@ "micromark-util-symbol": "^1.0.1" } }, - "@slorber/static-site-generator-webpack-plugin": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/@slorber/static-site-generator-webpack-plugin/-/static-site-generator-webpack-plugin-4.0.7.tgz", - "integrity": "sha512-Ug7x6z5lwrz0WqdnNFOMYrDQNTPAprvHLSh6+/fmml3qUiz6l5eq+2MzLKWtn/q5K5NpSiFsZTP/fck/3vjSxA==", - "requires": { - "eval": "^0.1.8", - "p-map": "^4.0.0", - "webpack-sources": "^3.2.2" - } - }, "@stitches/core": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/@stitches/core/-/core-1.2.8.tgz", "integrity": "sha512-Gfkvwk9o9kE9r9XNBmJRfV8zONvXThnm1tcuojL04Uy5uRyqg93DC83lDebl0rocZCfKSjUv+fWYtMQmEDJldg==" }, "@svgr/babel-plugin-add-jsx-attribute": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-6.5.1.tgz", - "integrity": "sha512-9PYGcXrAxitycIjRmZB+Q0JaN07GZIWaTBIGQzfaZv+qr1n8X1XUEJ5rZ/vx6OVD9RRYlrNnXWExQXcmZeD/BQ==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==", "requires": {} }, "@svgr/babel-plugin-remove-jsx-attribute": { @@ -18752,105 +18674,105 @@ "requires": {} }, "@svgr/babel-plugin-replace-jsx-attribute-value": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-6.5.1.tgz", - "integrity": "sha512-8DPaVVE3fd5JKuIC29dqyMB54sA6mfgki2H2+swh+zNJoynC8pMPzOkidqHOSc6Wj032fhl8Z0TVn1GiPpAiJg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz", + "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==", "requires": {} }, "@svgr/babel-plugin-svg-dynamic-title": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-6.5.1.tgz", - "integrity": "sha512-FwOEi0Il72iAzlkaHrlemVurgSQRDFbk0OC8dSvD5fSBPHltNh7JtLsxmZUhjYBZo2PpcU/RJvvi6Q0l7O7ogw==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz", + "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==", "requires": {} }, "@svgr/babel-plugin-svg-em-dimensions": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-6.5.1.tgz", - "integrity": "sha512-gWGsiwjb4tw+ITOJ86ndY/DZZ6cuXMNE/SjcDRg+HLuCmwpcjOktwRF9WgAiycTqJD/QXqL2f8IzE2Rzh7aVXA==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz", + "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==", "requires": {} }, "@svgr/babel-plugin-transform-react-native-svg": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-6.5.1.tgz", - "integrity": "sha512-2jT3nTayyYP7kI6aGutkyfJ7UMGtuguD72OjeGLwVNyfPRBD8zQthlvL+fAbAKk5n9ZNcvFkp/b1lZ7VsYqVJg==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz", + "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==", "requires": {} }, "@svgr/babel-plugin-transform-svg-component": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-6.5.1.tgz", - "integrity": "sha512-a1p6LF5Jt33O3rZoVRBqdxL350oge54iZWHNI6LJB5tQ7EelvD/Mb1mfBiZNAan0dt4i3VArkFRjA4iObuNykQ==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz", + "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==", "requires": {} }, "@svgr/babel-preset": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-6.5.1.tgz", - "integrity": "sha512-6127fvO/FF2oi5EzSQOAjo1LE3OtNVh11R+/8FXa+mHx1ptAaS4cknIjnUA7e6j6fwGGJ17NzaTJFUwOV2zwCw==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz", + "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==", "requires": { - "@svgr/babel-plugin-add-jsx-attribute": "^6.5.1", - "@svgr/babel-plugin-remove-jsx-attribute": "*", - "@svgr/babel-plugin-remove-jsx-empty-expression": "*", - "@svgr/babel-plugin-replace-jsx-attribute-value": "^6.5.1", - "@svgr/babel-plugin-svg-dynamic-title": "^6.5.1", - "@svgr/babel-plugin-svg-em-dimensions": "^6.5.1", - "@svgr/babel-plugin-transform-react-native-svg": "^6.5.1", - "@svgr/babel-plugin-transform-svg-component": "^6.5.1" + "@svgr/babel-plugin-add-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0", + "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0", + "@svgr/babel-plugin-svg-dynamic-title": "8.0.0", + "@svgr/babel-plugin-svg-em-dimensions": "8.0.0", + "@svgr/babel-plugin-transform-react-native-svg": "8.1.0", + "@svgr/babel-plugin-transform-svg-component": "8.0.0" } }, "@svgr/core": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/core/-/core-6.5.1.tgz", - "integrity": "sha512-/xdLSWxK5QkqG524ONSjvg3V/FkNyCv538OIBdQqPNaAta3AsXj/Bd2FbvR87yMbXO2hFSWiAe/Q6IkVPDw+mw==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", + "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", "requires": { - "@babel/core": "^7.19.6", - "@svgr/babel-preset": "^6.5.1", - "@svgr/plugin-jsx": "^6.5.1", + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", "camelcase": "^6.2.0", - "cosmiconfig": "^7.0.1" + "cosmiconfig": "^8.1.3", + "snake-case": "^3.0.4" } }, "@svgr/hast-util-to-babel-ast": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-6.5.1.tgz", - "integrity": "sha512-1hnUxxjd83EAxbL4a0JDJoD3Dao3hmjvyvyEV8PzWmLK3B9m9NPlW7GKjFyoWE8nM7HnXzPcmmSyOW8yOddSXw==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz", + "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==", "requires": { - "@babel/types": "^7.20.0", + "@babel/types": "^7.21.3", "entities": "^4.4.0" } }, "@svgr/plugin-jsx": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-6.5.1.tgz", - "integrity": "sha512-+UdQxI3jgtSjCykNSlEMuy1jSRQlGC7pqBCPvkG/2dATdWo082zHTTK3uhnAju2/6XpE6B5mZ3z4Z8Ns01S8Gw==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz", + "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==", "requires": { - "@babel/core": "^7.19.6", - "@svgr/babel-preset": "^6.5.1", - "@svgr/hast-util-to-babel-ast": "^6.5.1", + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "@svgr/hast-util-to-babel-ast": "8.0.0", "svg-parser": "^2.0.4" } }, "@svgr/plugin-svgo": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-6.5.1.tgz", - "integrity": "sha512-omvZKf8ixP9z6GWgwbtmP9qQMPX4ODXi+wzbVZgomNFsUIlHA1sf4fThdwTWSsZGgvGAG6yE+b/F5gWUkcZ/iQ==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-8.1.0.tgz", + "integrity": "sha512-Ywtl837OGO9pTLIN/onoWLmDQ4zFUycI1g76vuKGEz6evR/ZTJlJuz3G/fIkb6OVBJ2g0o6CGJzaEjfmEo3AHA==", "requires": { - "cosmiconfig": "^7.0.1", - "deepmerge": "^4.2.2", - "svgo": "^2.8.0" + "cosmiconfig": "^8.1.3", + "deepmerge": "^4.3.1", + "svgo": "^3.0.2" } }, "@svgr/webpack": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-6.5.1.tgz", - "integrity": "sha512-cQ/AsnBkXPkEK8cLbv4Dm7JGXq2XrumKnL1dRpJD9rIO2fTIlJI9a1uCciYG1F2aUsox/hJQyNGbt3soDxSRkA==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-8.1.0.tgz", + "integrity": "sha512-LnhVjMWyMQV9ZmeEy26maJk+8HTIbd59cH4F2MJ439k9DqejRisfFNGAPvRYlKETuh9LrImlS8aKsBgKjMA8WA==", "requires": { - "@babel/core": "^7.19.6", - "@babel/plugin-transform-react-constant-elements": "^7.18.12", - "@babel/preset-env": "^7.19.4", + "@babel/core": "^7.21.3", + "@babel/plugin-transform-react-constant-elements": "^7.21.3", + "@babel/preset-env": "^7.20.2", "@babel/preset-react": "^7.18.6", - "@babel/preset-typescript": "^7.18.6", - "@svgr/core": "^6.5.1", - "@svgr/plugin-jsx": "^6.5.1", - "@svgr/plugin-svgo": "^6.5.1" + "@babel/preset-typescript": "^7.21.0", + "@svgr/core": "8.1.0", + "@svgr/plugin-jsx": "8.1.0", + "@svgr/plugin-svgo": "8.1.0" } }, "@szmarczak/http-timer": { @@ -19224,9 +19146,9 @@ } }, "@types/yargs": { - "version": "17.0.31", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.31.tgz", - "integrity": "sha512-bocYSx4DI8TmdlvxqGpVNXOgCNR1Jj0gNPhhAY+iz1rgKDAaYrAYdFYnhDV1IFuiuVc9HkOwyDcFxaTElF3/wg==", + "version": "17.0.32", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", + "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", "requires": { "@types/yargs-parser": "*" } @@ -19470,30 +19392,31 @@ } }, "algoliasearch": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.22.1.tgz", - "integrity": "sha512-jwydKFQJKIx9kIZ8Jm44SdpigFwRGPESaxZBaHSV0XWN2yBJAOT4mT7ppvlrpA4UGzz92pqFnVKr/kaZXrcreg==", - "requires": { - "@algolia/cache-browser-local-storage": "4.22.1", - "@algolia/cache-common": "4.22.1", - "@algolia/cache-in-memory": "4.22.1", - "@algolia/client-account": "4.22.1", - "@algolia/client-analytics": "4.22.1", - "@algolia/client-common": "4.22.1", - "@algolia/client-personalization": "4.22.1", - "@algolia/client-search": "4.22.1", - "@algolia/logger-common": "4.22.1", - "@algolia/logger-console": "4.22.1", - "@algolia/requester-browser-xhr": "4.22.1", - "@algolia/requester-common": "4.22.1", - "@algolia/requester-node-http": "4.22.1", - "@algolia/transporter": "4.22.1" + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.23.3.tgz", + "integrity": "sha512-Le/3YgNvjW9zxIQMRhUHuhiUjAlKY/zsdZpfq4dlLqg6mEm0nL6yk+7f2hDOtLpxsgE4jSzDmvHL7nXdBp5feg==", + "requires": { + "@algolia/cache-browser-local-storage": "4.23.3", + "@algolia/cache-common": "4.23.3", + "@algolia/cache-in-memory": "4.23.3", + "@algolia/client-account": "4.23.3", + "@algolia/client-analytics": "4.23.3", + "@algolia/client-common": "4.23.3", + "@algolia/client-personalization": "4.23.3", + "@algolia/client-search": "4.23.3", + "@algolia/logger-common": "4.23.3", + "@algolia/logger-console": "4.23.3", + "@algolia/recommend": "4.23.3", + "@algolia/requester-browser-xhr": "4.23.3", + "@algolia/requester-common": "4.23.3", + "@algolia/requester-node-http": "4.23.3", + "@algolia/transporter": "4.23.3" } }, "algoliasearch-helper": { - "version": "3.16.3", - "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.16.3.tgz", - "integrity": "sha512-1OuJT6sONAa9PxcOmWo5WCAT3jQSpCR9/m5Azujja7nhUQwAUDvaaAYrcmUySsrvHh74usZHbE3jFfGnWtZj8w==", + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.21.0.tgz", + "integrity": "sha512-hjVOrL15I3Y3K8xG0icwG1/tWE+MocqBrhW6uVBWpU+/kVEMK0BnM2xdssj6mZM61eJ4iRxHR0djEI3ENOpR8w==", "requires": { "@algolia/events": "^4.0.1" } @@ -19586,12 +19509,12 @@ "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" }, "autoprefixer": { - "version": "10.4.18", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.18.tgz", - "integrity": "sha512-1DKbDfsr6KUElM6wg+0zRNkB/Q7WcKYAaK+pzXn+Xqmszm/5Xa9coeNdtP88Vi+dPzZnMjhge8GIV49ZQkDa+g==", + "version": "10.4.19", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz", + "integrity": "sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==", "requires": { "browserslist": "^4.23.0", - "caniuse-lite": "^1.0.30001591", + "caniuse-lite": "^1.0.30001599", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", "picocolors": "^1.0.0", @@ -19863,9 +19786,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001596", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001596.tgz", - "integrity": "sha512-zpkZ+kEr6We7w63ORkoJ2pOfBwBkY/bJrG/UZ90qNb45Isblu8wzDgevEOrRL1r9dWayHjYiiyCMEXPn4DweGQ==" + "version": "1.0.30001632", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001632.tgz", + "integrity": "sha512-udx3o7yHJfUxMLkGohMlVHCvFvWmirKh9JAH/d7WOLPetlH+LTL5cocMZ0t7oZx/mdlOWXti97xLZWc8uURRHg==" }, "ccount": { "version": "2.0.1", @@ -20279,15 +20202,14 @@ } }, "cosmiconfig": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", - "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", "requires": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" } }, "crelt": { @@ -20321,9 +20243,9 @@ } }, "css-declaration-sorter": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz", - "integrity": "sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.2.0.tgz", + "integrity": "sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow==", "requires": {} }, "css-loader": { @@ -20342,23 +20264,16 @@ } }, "css-minimizer-webpack-plugin": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-4.2.2.tgz", - "integrity": "sha512-s3Of/4jKfw1Hj9CxEO1E5oXhQAxlayuHO2y/ML+C6I9sQ7FdzfEV6QgMLN3vI+qFsjJGIAFLKtQK7t8BOXAIyA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-5.0.1.tgz", + "integrity": "sha512-3caImjKFQkS+ws1TGcFn0V1HyDJFq1Euy589JlD6/3rV2kj+w7r5G9WDMgSHvpvXHNZ2calVypZWuEDQd9wfLg==", "requires": { - "cssnano": "^5.1.8", - "jest-worker": "^29.1.2", - "postcss": "^8.4.17", - "schema-utils": "^4.0.0", - "serialize-javascript": "^6.0.0", - "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } + "@jridgewell/trace-mapping": "^0.3.18", + "cssnano": "^6.0.1", + "jest-worker": "^29.4.3", + "postcss": "^8.4.24", + "schema-utils": "^4.0.1", + "serialize-javascript": "^6.0.1" } }, "css-select": { @@ -20374,19 +20289,12 @@ } }, "css-tree": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", - "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", "requires": { - "mdn-data": "2.0.14", - "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" } }, "css-what": { @@ -20400,76 +20308,93 @@ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" }, "cssnano": { - "version": "5.1.15", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.15.tgz", - "integrity": "sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-6.1.2.tgz", + "integrity": "sha512-rYk5UeX7VAM/u0lNqewCdasdtPK81CgX8wJFLEIXHbV2oldWRgJAsZrdhRXkV1NJzA2g850KiFm9mMU2HxNxMA==", "requires": { - "cssnano-preset-default": "^5.2.14", - "lilconfig": "^2.0.3", - "yaml": "^1.10.2" + "cssnano-preset-default": "^6.1.2", + "lilconfig": "^3.1.1" } }, "cssnano-preset-advanced": { - "version": "5.3.10", - "resolved": "https://registry.npmjs.org/cssnano-preset-advanced/-/cssnano-preset-advanced-5.3.10.tgz", - "integrity": "sha512-fnYJyCS9jgMU+cmHO1rPSPf9axbQyD7iUhLO5Df6O4G+fKIOMps+ZbU0PdGFejFBBZ3Pftf18fn1eG7MAPUSWQ==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/cssnano-preset-advanced/-/cssnano-preset-advanced-6.1.2.tgz", + "integrity": "sha512-Nhao7eD8ph2DoHolEzQs5CfRpiEP0xa1HBdnFZ82kvqdmbwVBUr2r1QuQ4t1pi+D1ZpqpcO4T+wy/7RxzJ/WPQ==", "requires": { - "autoprefixer": "^10.4.12", - "cssnano-preset-default": "^5.2.14", - "postcss-discard-unused": "^5.1.0", - "postcss-merge-idents": "^5.1.1", - "postcss-reduce-idents": "^5.2.0", - "postcss-zindex": "^5.1.0" + "autoprefixer": "^10.4.19", + "browserslist": "^4.23.0", + "cssnano-preset-default": "^6.1.2", + "postcss-discard-unused": "^6.0.5", + "postcss-merge-idents": "^6.0.3", + "postcss-reduce-idents": "^6.0.3", + "postcss-zindex": "^6.0.2" } }, "cssnano-preset-default": { - "version": "5.2.14", - "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.14.tgz", - "integrity": "sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A==", - "requires": { - "css-declaration-sorter": "^6.3.1", - "cssnano-utils": "^3.1.0", - "postcss-calc": "^8.2.3", - "postcss-colormin": "^5.3.1", - "postcss-convert-values": "^5.1.3", - "postcss-discard-comments": "^5.1.2", - "postcss-discard-duplicates": "^5.1.0", - "postcss-discard-empty": "^5.1.1", - "postcss-discard-overridden": "^5.1.0", - "postcss-merge-longhand": "^5.1.7", - "postcss-merge-rules": "^5.1.4", - "postcss-minify-font-values": "^5.1.0", - "postcss-minify-gradients": "^5.1.1", - "postcss-minify-params": "^5.1.4", - "postcss-minify-selectors": "^5.2.1", - "postcss-normalize-charset": "^5.1.0", - "postcss-normalize-display-values": "^5.1.0", - "postcss-normalize-positions": "^5.1.1", - "postcss-normalize-repeat-style": "^5.1.1", - "postcss-normalize-string": "^5.1.0", - "postcss-normalize-timing-functions": "^5.1.0", - "postcss-normalize-unicode": "^5.1.1", - "postcss-normalize-url": "^5.1.0", - "postcss-normalize-whitespace": "^5.1.1", - "postcss-ordered-values": "^5.1.3", - "postcss-reduce-initial": "^5.1.2", - "postcss-reduce-transforms": "^5.1.0", - "postcss-svgo": "^5.1.0", - "postcss-unique-selectors": "^5.1.1" + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-6.1.2.tgz", + "integrity": "sha512-1C0C+eNaeN8OcHQa193aRgYexyJtU8XwbdieEjClw+J9d94E41LwT6ivKH0WT+fYwYWB0Zp3I3IZ7tI/BbUbrg==", + "requires": { + "browserslist": "^4.23.0", + "css-declaration-sorter": "^7.2.0", + "cssnano-utils": "^4.0.2", + "postcss-calc": "^9.0.1", + "postcss-colormin": "^6.1.0", + "postcss-convert-values": "^6.1.0", + "postcss-discard-comments": "^6.0.2", + "postcss-discard-duplicates": "^6.0.3", + "postcss-discard-empty": "^6.0.3", + "postcss-discard-overridden": "^6.0.2", + "postcss-merge-longhand": "^6.0.5", + "postcss-merge-rules": "^6.1.1", + "postcss-minify-font-values": "^6.1.0", + "postcss-minify-gradients": "^6.0.3", + "postcss-minify-params": "^6.1.0", + "postcss-minify-selectors": "^6.0.4", + "postcss-normalize-charset": "^6.0.2", + "postcss-normalize-display-values": "^6.0.2", + "postcss-normalize-positions": "^6.0.2", + "postcss-normalize-repeat-style": "^6.0.2", + "postcss-normalize-string": "^6.0.2", + "postcss-normalize-timing-functions": "^6.0.2", + "postcss-normalize-unicode": "^6.1.0", + "postcss-normalize-url": "^6.0.2", + "postcss-normalize-whitespace": "^6.0.2", + "postcss-ordered-values": "^6.0.2", + "postcss-reduce-initial": "^6.1.0", + "postcss-reduce-transforms": "^6.0.2", + "postcss-svgo": "^6.0.3", + "postcss-unique-selectors": "^6.0.4" } }, "cssnano-utils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz", - "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-4.0.2.tgz", + "integrity": "sha512-ZR1jHg+wZ8o4c3zqf1SIUSTIvm/9mU343FMR6Obe/unskbvpGhZOo1J6d/r8D1pzkRQYuwbcH3hToOuoA2G7oQ==", "requires": {} }, "csso": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", - "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", + "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", "requires": { - "css-tree": "^1.1.2" + "css-tree": "~2.2.0" + }, + "dependencies": { + "css-tree": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", + "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", + "requires": { + "mdn-data": "2.0.28", + "source-map-js": "^1.0.1" + } + }, + "mdn-data": { + "version": "2.0.28", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", + "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==" + } } }, "csstype": { @@ -21319,9 +21244,9 @@ } }, "estree-util-value-to-estree": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/estree-util-value-to-estree/-/estree-util-value-to-estree-3.0.1.tgz", - "integrity": "sha512-b2tdzTurEIbwRh+mKrEcaWfu1wgb8J1hVsgREg7FFiecWwK/PhO8X0kyc+0bIcKNtD4sqxIdNoRy6/p/TvECEA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/estree-util-value-to-estree/-/estree-util-value-to-estree-3.1.1.tgz", + "integrity": "sha512-5mvUrF2suuv5f5cGDnDphIy4/gW86z82kl5qG6mM9z04SEQI4FB5Apmaw/TGEf3l55nLtMs5s51dmhUzvAHQCA==", "requires": { "@types/estree": "^1.0.0", "is-plain-obj": "^4.0.0" @@ -21781,9 +21706,9 @@ "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" }, "fs-extra": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", - "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", "requires": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -22070,9 +21995,9 @@ } }, "hast-util-raw": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.0.2.tgz", - "integrity": "sha512-PldBy71wO9Uq1kyaMch9AHIghtQvIwxBUkv823pKmkTM3oV1JxtsTNYdevMxvUHqcnOAuO65JKU2+0NOxc2ksA==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.0.3.tgz", + "integrity": "sha512-ICWvVOF2fq4+7CMmtCPD5CM4QKjPbHpPotE6+8tDooV0ZuyJVUzHsrNX+O5NaRbieTf0F7FfeBOMAwi6Td0+yQ==", "requires": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", @@ -22864,9 +22789,9 @@ "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==" }, "lilconfig": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", - "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==" + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", + "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==" }, "lines-and-columns": { "version": "1.2.4", @@ -23268,9 +23193,9 @@ } }, "mdn-data": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", - "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==" }, "media-typer": { "version": "0.3.0", @@ -24600,11 +24525,6 @@ "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==" }, - "normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==" - }, "npm-run-path": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", @@ -24961,74 +24881,74 @@ } }, "postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "version": "8.4.38", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", + "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", "requires": { - "nanoid": "^3.3.6", + "nanoid": "^3.3.7", "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "source-map-js": "^1.2.0" } }, "postcss-calc": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz", - "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-9.0.1.tgz", + "integrity": "sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==", "requires": { - "postcss-selector-parser": "^6.0.9", + "postcss-selector-parser": "^6.0.11", "postcss-value-parser": "^4.2.0" } }, "postcss-colormin": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.1.tgz", - "integrity": "sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-6.1.0.tgz", + "integrity": "sha512-x9yX7DOxeMAR+BgGVnNSAxmAj98NX/YxEMNFP+SDCEeNLb2r3i6Hh1ksMsnW8Ub5SLCpbescQqn9YEbE9554Sw==", "requires": { - "browserslist": "^4.21.4", + "browserslist": "^4.23.0", "caniuse-api": "^3.0.0", - "colord": "^2.9.1", + "colord": "^2.9.3", "postcss-value-parser": "^4.2.0" } }, "postcss-convert-values": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.3.tgz", - "integrity": "sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-6.1.0.tgz", + "integrity": "sha512-zx8IwP/ts9WvUM6NkVSkiU902QZL1bwPhaVaLynPtCsOTqp+ZKbNi+s6XJg3rfqpKGA/oc7Oxk5t8pOQJcwl/w==", "requires": { - "browserslist": "^4.21.4", + "browserslist": "^4.23.0", "postcss-value-parser": "^4.2.0" } }, "postcss-discard-comments": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz", - "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-6.0.2.tgz", + "integrity": "sha512-65w/uIqhSBBfQmYnG92FO1mWZjJ4GL5b8atm5Yw2UgrwD7HiNiSSNwJor1eCFGzUgYnN/iIknhNRVqjrrpuglw==", "requires": {} }, "postcss-discard-duplicates": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", - "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-6.0.3.tgz", + "integrity": "sha512-+JA0DCvc5XvFAxwx6f/e68gQu/7Z9ud584VLmcgto28eB8FqSFZwtrLwB5Kcp70eIoWP/HXqz4wpo8rD8gpsTw==", "requires": {} }, "postcss-discard-empty": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", - "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-6.0.3.tgz", + "integrity": "sha512-znyno9cHKQsK6PtxL5D19Fj9uwSzC2mB74cpT66fhgOadEUPyXFkbgwm5tvc3bt3NAy8ltE5MrghxovZRVnOjQ==", "requires": {} }, "postcss-discard-overridden": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", - "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-6.0.2.tgz", + "integrity": "sha512-j87xzI4LUggC5zND7KdjsI25APtyMuynXZSujByMaav2roV6OZX+8AaCUcZSWqckZpjAjRyFDdpqybgjFO0HJQ==", "requires": {} }, "postcss-discard-unused": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-5.1.0.tgz", - "integrity": "sha512-KwLWymI9hbwXmJa0dkrzpRbSJEh0vVUd7r8t0yOGPcfKzyJJxFM8kLyC5Ev9avji6nY95pOp1W6HqIrfT+0VGw==", + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-6.0.5.tgz", + "integrity": "sha512-wHalBlRHkaNnNwfC8z+ppX57VhvS+HWgjW508esjdaEYr3Mx7Gnn2xA4R/CKf5+Z9S5qsqC+Uzh4ueENWwCVUA==", "requires": { - "postcss-selector-parser": "^6.0.5" + "postcss-selector-parser": "^6.0.16" } }, "postcss-loader": { @@ -25039,84 +24959,71 @@ "cosmiconfig": "^8.2.0", "jiti": "^1.18.2", "semver": "^7.3.8" - }, - "dependencies": { - "cosmiconfig": { - "version": "8.3.6", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", - "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", - "requires": { - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0", - "path-type": "^4.0.0" - } - } } }, "postcss-merge-idents": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-5.1.1.tgz", - "integrity": "sha512-pCijL1TREiCoog5nQp7wUe+TUonA2tC2sQ54UGeMmryK3UFGIYKqDyjnqd6RcuI4znFn9hWSLNN8xKE/vWcUQw==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-6.0.3.tgz", + "integrity": "sha512-1oIoAsODUs6IHQZkLQGO15uGEbK3EAl5wi9SS8hs45VgsxQfMnxvt+L+zIr7ifZFIH14cfAeVe2uCTa+SPRa3g==", "requires": { - "cssnano-utils": "^3.1.0", + "cssnano-utils": "^4.0.2", "postcss-value-parser": "^4.2.0" } }, "postcss-merge-longhand": { - "version": "5.1.7", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.7.tgz", - "integrity": "sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==", + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-6.0.5.tgz", + "integrity": "sha512-5LOiordeTfi64QhICp07nzzuTDjNSO8g5Ksdibt44d+uvIIAE1oZdRn8y/W5ZtYgRH/lnLDlvi9F8btZcVzu3w==", "requires": { "postcss-value-parser": "^4.2.0", - "stylehacks": "^5.1.1" + "stylehacks": "^6.1.1" } }, "postcss-merge-rules": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.4.tgz", - "integrity": "sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-6.1.1.tgz", + "integrity": "sha512-KOdWF0gju31AQPZiD+2Ar9Qjowz1LTChSjFFbS+e2sFgc4uHOp3ZvVX4sNeTlk0w2O31ecFGgrFzhO0RSWbWwQ==", "requires": { - "browserslist": "^4.21.4", + "browserslist": "^4.23.0", "caniuse-api": "^3.0.0", - "cssnano-utils": "^3.1.0", - "postcss-selector-parser": "^6.0.5" + "cssnano-utils": "^4.0.2", + "postcss-selector-parser": "^6.0.16" } }, "postcss-minify-font-values": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz", - "integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-6.1.0.tgz", + "integrity": "sha512-gklfI/n+9rTh8nYaSJXlCo3nOKqMNkxuGpTn/Qm0gstL3ywTr9/WRKznE+oy6fvfolH6dF+QM4nCo8yPLdvGJg==", "requires": { "postcss-value-parser": "^4.2.0" } }, "postcss-minify-gradients": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz", - "integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-6.0.3.tgz", + "integrity": "sha512-4KXAHrYlzF0Rr7uc4VrfwDJ2ajrtNEpNEuLxFgwkhFZ56/7gaE4Nr49nLsQDZyUe+ds+kEhf+YAUolJiYXF8+Q==", "requires": { - "colord": "^2.9.1", - "cssnano-utils": "^3.1.0", + "colord": "^2.9.3", + "cssnano-utils": "^4.0.2", "postcss-value-parser": "^4.2.0" } }, "postcss-minify-params": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.4.tgz", - "integrity": "sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-6.1.0.tgz", + "integrity": "sha512-bmSKnDtyyE8ujHQK0RQJDIKhQ20Jq1LYiez54WiaOoBtcSuflfK3Nm596LvbtlFcpipMjgClQGyGr7GAs+H1uA==", "requires": { - "browserslist": "^4.21.4", - "cssnano-utils": "^3.1.0", + "browserslist": "^4.23.0", + "cssnano-utils": "^4.0.2", "postcss-value-parser": "^4.2.0" } }, "postcss-minify-selectors": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz", - "integrity": "sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-6.0.4.tgz", + "integrity": "sha512-L8dZSwNLgK7pjTto9PzWRoMbnLq5vsZSTu8+j1P/2GB8qdtGQfn+K1uSvFgYvgh83cbyxT5m43ZZhUMTJDSClQ==", "requires": { - "postcss-selector-parser": "^6.0.5" + "postcss-selector-parser": "^6.0.16" } }, "postcss-modules-extract-imports": { @@ -25152,143 +25059,142 @@ } }, "postcss-normalize-charset": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", - "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-6.0.2.tgz", + "integrity": "sha512-a8N9czmdnrjPHa3DeFlwqst5eaL5W8jYu3EBbTTkI5FHkfMhFZh1EGbku6jhHhIzTA6tquI2P42NtZ59M/H/kQ==", "requires": {} }, "postcss-normalize-display-values": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz", - "integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-6.0.2.tgz", + "integrity": "sha512-8H04Mxsb82ON/aAkPeq8kcBbAtI5Q2a64X/mnRRfPXBq7XeogoQvReqxEfc0B4WPq1KimjezNC8flUtC3Qz6jg==", "requires": { "postcss-value-parser": "^4.2.0" } }, "postcss-normalize-positions": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz", - "integrity": "sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-6.0.2.tgz", + "integrity": "sha512-/JFzI441OAB9O7VnLA+RtSNZvQ0NCFZDOtp6QPFo1iIyawyXg0YI3CYM9HBy1WvwCRHnPep/BvI1+dGPKoXx/Q==", "requires": { "postcss-value-parser": "^4.2.0" } }, "postcss-normalize-repeat-style": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz", - "integrity": "sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-6.0.2.tgz", + "integrity": "sha512-YdCgsfHkJ2jEXwR4RR3Tm/iOxSfdRt7jplS6XRh9Js9PyCR/aka/FCb6TuHT2U8gQubbm/mPmF6L7FY9d79VwQ==", "requires": { "postcss-value-parser": "^4.2.0" } }, "postcss-normalize-string": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz", - "integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-6.0.2.tgz", + "integrity": "sha512-vQZIivlxlfqqMp4L9PZsFE4YUkWniziKjQWUtsxUiVsSSPelQydwS8Wwcuw0+83ZjPWNTl02oxlIvXsmmG+CiQ==", "requires": { "postcss-value-parser": "^4.2.0" } }, "postcss-normalize-timing-functions": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz", - "integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-6.0.2.tgz", + "integrity": "sha512-a+YrtMox4TBtId/AEwbA03VcJgtyW4dGBizPl7e88cTFULYsprgHWTbfyjSLyHeBcK/Q9JhXkt2ZXiwaVHoMzA==", "requires": { "postcss-value-parser": "^4.2.0" } }, "postcss-normalize-unicode": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.1.tgz", - "integrity": "sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-6.1.0.tgz", + "integrity": "sha512-QVC5TQHsVj33otj8/JD869Ndr5Xcc/+fwRh4HAsFsAeygQQXm+0PySrKbr/8tkDKzW+EVT3QkqZMfFrGiossDg==", "requires": { - "browserslist": "^4.21.4", + "browserslist": "^4.23.0", "postcss-value-parser": "^4.2.0" } }, "postcss-normalize-url": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz", - "integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-6.0.2.tgz", + "integrity": "sha512-kVNcWhCeKAzZ8B4pv/DnrU1wNh458zBNp8dh4y5hhxih5RZQ12QWMuQrDgPRw3LRl8mN9vOVfHl7uhvHYMoXsQ==", "requires": { - "normalize-url": "^6.0.1", "postcss-value-parser": "^4.2.0" } }, "postcss-normalize-whitespace": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz", - "integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-6.0.2.tgz", + "integrity": "sha512-sXZ2Nj1icbJOKmdjXVT9pnyHQKiSAyuNQHSgRCUgThn2388Y9cGVDR+E9J9iAYbSbLHI+UUwLVl1Wzco/zgv0Q==", "requires": { "postcss-value-parser": "^4.2.0" } }, "postcss-ordered-values": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz", - "integrity": "sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-6.0.2.tgz", + "integrity": "sha512-VRZSOB+JU32RsEAQrO94QPkClGPKJEL/Z9PCBImXMhIeK5KAYo6slP/hBYlLgrCjFxyqvn5VC81tycFEDBLG1Q==", "requires": { - "cssnano-utils": "^3.1.0", + "cssnano-utils": "^4.0.2", "postcss-value-parser": "^4.2.0" } }, "postcss-reduce-idents": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-5.2.0.tgz", - "integrity": "sha512-BTrLjICoSB6gxbc58D5mdBK8OhXRDqud/zodYfdSi52qvDHdMwk+9kB9xsM8yJThH/sZU5A6QVSmMmaN001gIg==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-6.0.3.tgz", + "integrity": "sha512-G3yCqZDpsNPoQgbDUy3T0E6hqOQ5xigUtBQyrmq3tn2GxlyiL0yyl7H+T8ulQR6kOcHJ9t7/9H4/R2tv8tJbMA==", "requires": { "postcss-value-parser": "^4.2.0" } }, "postcss-reduce-initial": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.2.tgz", - "integrity": "sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-6.1.0.tgz", + "integrity": "sha512-RarLgBK/CrL1qZags04oKbVbrrVK2wcxhvta3GCxrZO4zveibqbRPmm2VI8sSgCXwoUHEliRSbOfpR0b/VIoiw==", "requires": { - "browserslist": "^4.21.4", + "browserslist": "^4.23.0", "caniuse-api": "^3.0.0" } }, "postcss-reduce-transforms": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz", - "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-6.0.2.tgz", + "integrity": "sha512-sB+Ya++3Xj1WaT9+5LOOdirAxP7dJZms3GRcYheSPi1PiTMigsxHAdkrbItHxwYHr4kt1zL7mmcHstgMYT+aiA==", "requires": { "postcss-value-parser": "^4.2.0" } }, "postcss-selector-parser": { - "version": "6.0.13", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz", - "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.0.tgz", + "integrity": "sha512-UMz42UD0UY0EApS0ZL9o1XnLhSTtvvvLe5Dc2H2O56fvRZi+KulDyf5ctDhhtYJBGKStV2FL1fy6253cmLgqVQ==", "requires": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "postcss-sort-media-queries": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/postcss-sort-media-queries/-/postcss-sort-media-queries-4.4.1.tgz", - "integrity": "sha512-QDESFzDDGKgpiIh4GYXsSy6sek2yAwQx1JASl5AxBtU1Lq2JfKBljIPNdil989NcSKRQX1ToiaKphImtBuhXWw==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/postcss-sort-media-queries/-/postcss-sort-media-queries-5.2.0.tgz", + "integrity": "sha512-AZ5fDMLD8SldlAYlvi8NIqo0+Z8xnXU2ia0jxmuhxAU+Lqt9K+AlmLNJ/zWEnE9x+Zx3qL3+1K20ATgNOr3fAA==", "requires": { - "sort-css-media-queries": "2.1.0" + "sort-css-media-queries": "2.2.0" } }, "postcss-svgo": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz", - "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-6.0.3.tgz", + "integrity": "sha512-dlrahRmxP22bX6iKEjOM+c8/1p+81asjKT+V5lrgOH944ryx/OHpclnIbGsKVd3uWOXFLYJwCVf0eEkJGvO96g==", "requires": { "postcss-value-parser": "^4.2.0", - "svgo": "^2.7.0" + "svgo": "^3.2.0" } }, "postcss-unique-selectors": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz", - "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-6.0.4.tgz", + "integrity": "sha512-K38OCaIrO8+PzpArzkLKB42dSARtC2tmG6PvD4b1o1Q2E9Os8jzfWFfSy/rixsHwohtsDdFtAWGjFVFUdwYaMg==", "requires": { - "postcss-selector-parser": "^6.0.5" + "postcss-selector-parser": "^6.0.16" } }, "postcss-value-parser": { @@ -25297,9 +25203,9 @@ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, "postcss-zindex": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-5.1.0.tgz", - "integrity": "sha512-fgFMf0OtVSBR1va1JNHYgMxYk73yhn/qb4uQDq1DLGYolz8gHCyr/sesEuGUaYs58E3ZJRcpoGuPVoB7Meiq9A==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-6.0.2.tgz", + "integrity": "sha512-5BxW9l1evPB/4ZIc+2GobEBoKC+h8gPGCMi+jxsYvd2x0mjq7wazk6DrP71pStqxE9Foxh5TVnonbWpFZzXaYg==", "requires": {} }, "prettier": { @@ -25658,18 +25564,17 @@ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, "react-json-view-lite": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/react-json-view-lite/-/react-json-view-lite-1.2.1.tgz", - "integrity": "sha512-Itc0g86fytOmKZoIoJyGgvNqohWSbh3NXIKNgH6W6FT9PC1ck4xas1tT3Rr/b3UlFXyA9Jjaw9QSXdZy2JwGMQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/react-json-view-lite/-/react-json-view-lite-1.4.0.tgz", + "integrity": "sha512-wh6F6uJyYAmQ4fK0e8dSQMEWuvTs2Wr3el3sLD9bambX1+pSWUVXIz1RFaoy3TI1mZ0FqdpKq9YgbgTTgyrmXA==", "requires": {} }, "react-loadable": { - "version": "npm:@docusaurus/react-loadable@5.5.2", - "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz", - "integrity": "sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ==", + "version": "npm:@docusaurus/react-loadable@6.0.0", + "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-6.0.0.tgz", + "integrity": "sha512-YMMxTUQV/QFSnbgrP3tjDzLHRg7vsbMn8e9HAa8o/1iXoiomo48b7sk/kkmWEuWNDPJVlKSJRB6Y2fHqdJk+SQ==", "requires": { - "@types/react": "*", - "prop-types": "^15.6.2" + "@types/react": "*" } }, "react-loadable-ssr-addon-v5-slorber": { @@ -26127,9 +26032,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "sax": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", - "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==" + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==" }, "scheduler": { "version": "0.23.0", @@ -26151,9 +26056,9 @@ } }, "search-insights": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.13.0.tgz", - "integrity": "sha512-Orrsjf9trHHxFRuo9/rzm0KIWmgzE8RMlZMzuhZOJ01Rnz3D0YBAe+V6473t6/H6c7irs6Lt48brULAiRWb3Vw==", + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.14.0.tgz", + "integrity": "sha512-OLN6MsPMCghDOqlCtsIsYgtsC0pnwVTyT9Mu6A3ewOj1DxvzZF6COrn2g86E/c05xbktB0XN04m/t1Z+n+fTGw==", "peer": true }, "section-matter": { @@ -26446,9 +26351,9 @@ "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" }, "sitemap": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-7.1.1.tgz", - "integrity": "sha512-mK3aFtjz4VdJN0igpIJrinf3EO8U8mxOPsTBzSsy06UtjZQJ3YY3o3Xa7zSc5nMqcMrRwlChHZ18Kxg0caiPBg==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-7.1.2.tgz", + "integrity": "sha512-ARCqzHJ0p4gWt+j7NlU5eDlIO9+Rkr/JhPFZKKQ1l5GCus7rJH4UdrlVAh0xC/gDS/Qir2UMxqYNHtsKr2rpCw==", "requires": { "@types/node": "^17.0.5", "@types/sax": "^1.2.1", @@ -26476,6 +26381,15 @@ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" }, + "snake-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "requires": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "sockjs": { "version": "0.3.24", "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", @@ -26487,9 +26401,9 @@ } }, "sort-css-media-queries": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/sort-css-media-queries/-/sort-css-media-queries-2.1.0.tgz", - "integrity": "sha512-IeWvo8NkNiY2vVYdPa27MCQiR0MN0M80johAYFVxWWXQ44KU84WNxjslwBHmc/7ZL2ccwkM7/e6S5aiKZXm7jA==" + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/sort-css-media-queries/-/sort-css-media-queries-2.2.0.tgz", + "integrity": "sha512-0xtkGhWCC9MGt/EzgnvbbbKhqWjl1+/rncmhTh5qCpbYguXh6S/qwePfv/JQ8jePXXmqingylxoC49pCkSPIbA==" }, "source-map": { "version": "0.7.4", @@ -26497,9 +26411,9 @@ "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==" }, "source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==" }, "source-map-support": { "version": "0.5.21", @@ -26557,11 +26471,6 @@ "resolved": "https://registry.npmjs.org/srcset/-/srcset-4.0.0.tgz", "integrity": "sha512-wvLeHgcVHKO8Sc/H/5lkGreJQVeYMm9rlmt8PuR1xE31rIuXhuzznUUqAt8MqLhB3MqJdFzlNAfpcWnxiFUcPw==" }, - "stable": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", - "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==" - }, "static-browser-server": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/static-browser-server/-/static-browser-server-1.0.3.tgz", @@ -26684,12 +26593,12 @@ } }, "stylehacks": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", - "integrity": "sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-6.1.1.tgz", + "integrity": "sha512-gSTTEQ670cJNoaeIp9KX6lZmm8LJ3jPB5yJmX8Zq/wQxOsAFXV3qjWzHas3YYk1qesuVIyYWWUpZ0vSE/dTSGg==", "requires": { - "browserslist": "^4.21.4", - "postcss-selector-parser": "^6.0.4" + "browserslist": "^4.23.0", + "postcss-selector-parser": "^6.0.16" } }, "stylis": { @@ -26716,68 +26625,23 @@ "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==" }, "svgo": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", - "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz", + "integrity": "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==", "requires": { "@trysound/sax": "0.2.0", "commander": "^7.2.0", - "css-select": "^4.1.3", - "css-tree": "^1.1.3", - "csso": "^4.2.0", - "picocolors": "^1.0.0", - "stable": "^0.1.8" + "css-select": "^5.1.0", + "css-tree": "^2.3.1", + "css-what": "^6.1.0", + "csso": "^5.0.5", + "picocolors": "^1.0.0" }, "dependencies": { "commander": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==" - }, - "css-select": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", - "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", - "requires": { - "boolbase": "^1.0.0", - "css-what": "^6.0.1", - "domhandler": "^4.3.1", - "domutils": "^2.8.0", - "nth-check": "^2.0.1" - } - }, - "dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", - "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - } - }, - "domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "requires": { - "domelementtype": "^2.2.0" - } - }, - "domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "requires": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - } - }, - "entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" } } }, diff --git a/package.json b/package.json index c1d6b8a67..6aaba2b3a 100644 --- a/package.json +++ b/package.json @@ -1,51 +1,51 @@ -{ - "name": "alova-website", - "version": "1.0.0", - "private": true, - "scripts": { - "docusaurus": "docusaurus", - "start": "docusaurus start", - "build": "docusaurus build", - "swizzle": "docusaurus swizzle", - "deploy": "docusaurus deploy", - "clear": "docusaurus clear", - "serve": "docusaurus serve", - "write-translations": "docusaurus write-translations", - "write-heading-ids": "docusaurus write-heading-ids", - "typecheck": "tsc" - }, - "dependencies": { - "@codesandbox/sandpack-react": "^2.9.0", - "@codesandbox/sandpack-themes": "^2.0.21", - "@docusaurus/core": "^3.1.1", - "@docusaurus/preset-classic": "^3.1.1", - "@docusaurus/theme-mermaid": "^3.1.1", - "@docusaurus/tsconfig": "^3.1.1", - "@mdx-js/react": "^3.0.0", - "clsx": "^2.0.0", - "prism-react-renderer": "^2.1.0", - "raw-loader": "^4.0.2", - "react": "^18.2.0", - "react-dom": "^18.2.0" - }, - "devDependencies": { - "@docusaurus/module-type-aliases": "^3.1.1", - "prettier": "^2.8.8", - "typescript": "^5.2.2" - }, - "browserslist": { - "production": [ - ">0.5%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] - }, - "engines": { - "node": ">=16.14" - } -} +{ + "name": "alova-website", + "version": "1.0.0", + "private": true, + "scripts": { + "docusaurus": "docusaurus", + "start": "docusaurus start", + "build": "docusaurus build", + "swizzle": "docusaurus swizzle", + "deploy": "docusaurus deploy", + "clear": "docusaurus clear", + "serve": "docusaurus serve", + "write-translations": "docusaurus write-translations", + "write-heading-ids": "docusaurus write-heading-ids", + "typecheck": "tsc" + }, + "dependencies": { + "@codesandbox/sandpack-react": "^2.9.0", + "@codesandbox/sandpack-themes": "^2.0.21", + "@docusaurus/core": "^3.4.0", + "@docusaurus/preset-classic": "^3.4.0", + "@docusaurus/theme-mermaid": "^3.4.0", + "@docusaurus/tsconfig": "^3.4.0", + "@mdx-js/react": "^3.0.0", + "clsx": "^2.0.0", + "prism-react-renderer": "^2.1.0", + "raw-loader": "^4.0.2", + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@docusaurus/module-type-aliases": "^3.1.1", + "prettier": "^2.8.8", + "typescript": "^5.2.2" + }, + "browserslist": { + "production": [ + ">0.5%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + }, + "engines": { + "node": ">=16.14" + } +} diff --git a/static/img/overview_flow_cn.png b/static/img/overview_flow_cn.png new file mode 100644 index 0000000000000000000000000000000000000000..243065af1addbf99fb8d9d63c86be40a4492cafd GIT binary patch literal 60554 zcmeFYRa6{L^gl>Kh(HKBxP;(CaQ85{+u)YqPH=~iFu1$BYjB4I8{Az3gS!nbJA8j< z|GO{yvJZQ9&)IqD>gww1TUS5#R!y*yf+RXB5h@ZA61udMm=5Bt(*Au54At(kio^d zjo$XZdBWk-_lkgY7SNGgJLMty3cmF5`Ul(DXkT+L}1R-vJG$e#v zRRFR;e=?;`pNIeZ1~x}QdiN28X#c4Z`z6!}OvZSiL!xUMEBqZd{bYefhAF6n2b-IeJ!{_&D;A6y)N-y$VW0cCfG4DXlFj(~2I`R*?;stE5>>X( zFMoZvY*ajES@(AxW2j&0&#z)7xo-IWvTfT3Ux@ED<@1m}^Ueh%;43d=;H&3bS-G9_ zqA}L5Xh<*C)TQ24LjiH|;G+Gz_WwSKDhR778siEvG0^`=@S1Cpd~q}vv~T)@3KQ|F zX75-TGQh(egDo2b{p0uL=MSks;ds=;lzwd^z=+oK>~C3Kb*!)gv;~EL7a-Y>NUuI! zob4)8h=PNTu9a=Qo`?LVWJJ}^0XAz(@Q}`cw zhy?*&{zo&S_5XC|wGoZ^!_%-xqEL%BwHq|X5BkinXL^wSt+2DkCe;u-^$M68Ft=a& z_!w?(&tuyu(A)EV$@?{q7w!4Rv?4VfK?^VsTWPSTUx= z|Evu$ve8*6!yetrG;`Nr`!md!n&enI%G)^kZyn12E-|;zH}QV~(uqA7iIb{5+e9nw z7`uFtH%ooVLYO}~r~lWu#$Xm>pmFSo#E#@mehhuOGNsjOzuCy%Qp0Yba&KImN2B+sP;`7gE?XoLvu7!P0A+zqVFs{_IgXgDA_oxkIt@lI-LpUM9 zvF|t4y&wdxi<_Jjw#OePGmN8OH8H+Z_ToCAE{KQ7R=xFo1YSA_3L^bL6|@JN*UZy7 zH%Upey;vBE(3moc?Whl?+84bL)i-xkFyI>Owx|(#R<7%=qNqWyt_+4B*7QXc2J7h@ z-FPXvBxg+@Lr1WWQa;a@CP+2bA{C7}s%DeGg2l`zm(0%}-DKXg+He6>-SL%!hfK*L zn~C%mbhE*n>+}f2{o|-Y(ag(9V(b`I7=^;9lAVk-9=^+}<4}ecokhLC5vq`XEUJmn zqT5K7Y>dr3j;H>OToq z;L2?8g!MWaZYEb%tkZHUWnX|s;)H!qiBYZgQ{FLBPaR0U|NR09ykpfjW=iHfKQ)of zXHx(DQF$cxr)aROjnE%8u2ZO8NYq&T&=N}97)W|BLqKNnM^_Rw z)AcY?`5f|w+V7iwBfPLSn@Iq=k-O1tH zlomy_$7!69e9YYv&NGk!>j?jo&{=kGIMV{)=7f?eubY{NDa>_}MJ|ItQ?JL)D%ILV zy|aqsr6@uyleB%GuH1{UjcdMs@b+4;jf74@2u8PRR})`ZI(m68XIQerZ3O6oJ@gya z-k>t_XK=|f*YXBXR6>Vysx4~Vn{e?#Ki?x|II+#p&!c`#(1X(crgk zGs&9UurItRro(ZfA9%R^M~SzDKGC7(ZD78}geNa>g*iQBTfDYXw2>7E*okflplkY2 z#-Q}k2SOu2p?8htr$*ZRH$9pA%zfN~hrN;jt5M_$N6uWs2LG2UAw9tLE$T&VB1IkdqO+O(d+Ld^s1Y8e^g(} zuYE)-%@ZX3Xdh|yvh$S5OY$GH>U9`Qx`LLkE{r7(T1FRu+e^wvTiJMo{Oe4Q0Lw>H zp<%vA+_DQKf;&9L3FolQvcnkjow2&%4OeVp*EK|FCt>lJsi>Gp6DSW9%z=r~Pi;!_ zI|9M1e=?eb`# zOfk~M7eWm)Zh-l2dIq3V|HJcvcRC8MhRiQGt-(fjzj1Vln+mvjI6 z_TAbSc7!`cFkb;eMO=?S5`&pGkSl8kiX;|_eEAp^n3q<_`@`Uhu@fmsl1pnUD(f*t zdk#mS#hlRVpoF5a%i7Ot3dur`@%6$NaUzIV|D!NH1f9YAEy?S>H^Zh_dRngf{(TZo ze10Az+65qN0P5UWLhwM#HNP- zl}8+#OFs{tO7_Hg5MYJ`fZAQiV5q3q1@0r5ntIll^pCM=8abfFSW8#BBAlK6LAv7K zFp#AcalYu80WPl{4q(9+8{n~~5k_Kl$)|N!hAV>$8YqqnvSAj7)ZYz)%?+drBXyob z=fC1EkRMv{K!Al9lsxb$=}*)}!Li~@nLk;yTc3Nv^(zPLUd+R+@7R)FQg&irX>jM? zMSuFnK*C!cplaW;@&-8dCIv^|k8@t|lqGhlLe)a2V~a;|8Cy(3>1P?C3~x;0xVC2` z^aH|DXAC{U>w|tvHU|FdvjC#MJ8Y2AigYEL)ZYKoxz99DNDo-h%>h9>zwqA^1wAXvC@F6BXMKfnV!RcO zPaX5S(Frj|uV^H_kLe%R+suF)g38$u0CCj~N~;{XvK-Uq2~=g)xY-F~0#*V=<@6EN ztUeT`DH#**JzZv6N#QsZeY348YJg1q1*0;BZ&e}TbGTy$$Y9}yPaSaT^`?mi_Xs5< zo%)Kmnzsa7aD`t_Ce>_Sa9|g>NXu%=%G+G((CcgPnJv^WlT$TY+2Xxbh&CPu zVLk&8`;%b(=G0=d9rDIBz?0c^dl!zLs}N9o8sa*!Z0&S?I_7|W*JIsHDFxx&beo&`Bb z4td=^uU`V2MGFl(E1Vvf@Gc+J#_n>YOQoHyr&ry(%}sSDuKNw)BrHrO(-wg_pqR;t z?}GvORwX%!-{ohPtRZ0QkY?vc8p08ByV0UCSqC)j5L8HRB_wG_H>FmK@8QDIPBojc z2onL&r&L+1VmmXw+iK8NTZqb7(2!mv&ibmNcy@v5P`xx)CKpJWv@n`p3t=*U;C^TG z%@(jZJ!JV>$E6gb>!#2N0H_84_(Vfc*P4*OKr)(ses)O0kDkcWuI&4&Q2E(Xi0zQ) z$Vkn5xty~ZJ(^`g7J&P(%2bd@t zRfl|Ws;4dZIIguLi7giz7sS_VHH*%bu!2jnhKW({jCvhrHlDsze2#yx`rMLe7s-v!hWBH{POR&%|b%tnR9)W6J5ll6aWKgiF&ylZFW*iD%f(pQbZwd=yH3(&=0)q4CPvGi*$2N+$~Lc>1nKuKsB-H~)J@AaAq+6`rMcNEO zi{s2!EIxaA45>#kn=Kn9rexZ15jz#U0lds`{Zz0x87y1V2*Pp9uD-VYz68%P>abYl zOm7$p48$F&&kQw*flRZ&Xa zhQwc9hQ1RF{EdkOyf~=dC5KM##xddr{~hxM*Ep+4h-9{oOsZMG#`W~DnO_bJna*D- zld~WHBW21_50-$`L*++)w{=(i&f(z$jOV*A754ergtG2(FOoCA0Ka8tJ6QtLC4@q> z+$F(9p8}|Zd3>wU@Eoc>mA{^!XvWFJ5sw@d;V^kmF+b2D+9P5-KG-LSisk4@s5~TGZ@1bAjbV6UQ#w^R=AE4S| zsP7MsS)V_uH0it_I(qW(``-HSseVUQ^t~P+*j(MN16IvuWGK&XpC+W%5Lh`t8+%FM zxc*{_?PBisbe_e1!VSasCaHFyN%@}I*l1^9u!u}4 zxM2FL^5CwSGQ~IjCY=jXy}$n46LHDx2&C#c)j8x;M+w;2&g}Q+v3d>k4<>Y*37)eAn=?XA z`769>tv+J&hgzp3p~3YJ(_wQF;1x%Ps$SV~!DRIoiBuPJVGk3Uv{h`u=ui^LI8cio zYtSo^@^A`@a8!GGAE-xeKi{E9!L3z2^WGM~d`i_N93eD>X@Sjk@F(qp8I}Id`VcP{ zQ1*O$UA0Uz5+mEv*fQ%fSVaxwknK5<5x-m#4kJ+8(@Uu1=h)-?!m#NmJ2U6|tKHyS z8>UGQ$#Z^?=mHTlQV=_0)E&~)aJg^V%$uSkN+}t(K+|$eRjm5k=xZgpsB$$1j)erP z{-Pg4=qS5E;BG?px{1Mfx&aZJK#z6xH$p_3p`rKw{Nu-lj1N>0cwp~l(I}54TI6>! zKQ7KsX%X^THU|nb{HZ`_*i8PRKWg@m_lU$1Lpi(6MV1;^HQQM}X`$MmhSrfG#Z`~3 zwqOakA7ayHxG<=7CsYfNZu1My!s8={8dGuRlUhXbV7|@od|u zY+IzXTG^ZmjwL^N)C^b-c@=5KbrdwCT|7LQz$;GRFt9T?S?K`iQ?#_Sss*&{$IFVVy{w<;>2Fns>%G84bZmw877zW@aJ~tQWv)y z9Aw1VTbmbfdEXqfZW#bxtgzusx>ZURAbzdIgvEhK3Vs{Tjr8vw?fNH?nIH_Mn!%n6ad6*UumE0uSXe|o_{)rHMk%?N5h?uu zCS(vcK!8BTuPvyuV9E0w7ueshlG2>a551#nbA?4aR7;Gh{Ul5yWB4YoM{S|tJ~}|P zde4+MSQGiVwxU9@N#FES&e2HN$C%z>8sFNpOel0~Y{Rc_XuH_HmVj-%r)$&Lo9-D9?$@g=`vh@cQ*nOHAog=d*sW4M!odZ&OPSh0TAoZPWT+{%S zI>m1mMU3i@-fk{^ZOsm8WUA;oK|HY3FMeSa$(!N&OP--{?)9e$)C7X$Bs3c%{u^x$ z?=yPQNb4=lMC5we9A9g*{fHFoCVgY9H%Y>%zxF%12Sq~&DwU~4)j$}rol})eIO=c_ z5hEEVqBvGG#*V0?eKr-A0A?#^=eP;p;e~|eY>aXh;{)w+nKXPE?-Hu+cj}~~On|kc zuL^QN`CmO!eFnB_!;eChG4+1`p77yQp&M5SeevfM?o6)8pEjnfhcpMke)i`)A`+cu z>b#z6yozxjYINmBDH4L^v3Y$Q&HYiG#URZR*v9+PDC5|J{kEe-fa(Jrj{T&5)BSX=yUH!81RchT8w&&oa3zkCu zT-e9%Cg?M6SZQSWC4ml2x{&$}9Q&^C*pYsYrk*5MbL=#bK+%pJfK8rFut{$`Ar21u z?fVct&}n|b#VO36q4J3-t*-thFW@M0AjN4(*2!>``I3h0!m_FpSeNEQn9#K5VE_i-{S<8c*IKG`?-4*HB!EZlJ(GCdAj|1%lncFx<#4^s-?Z8T$vKz7VkqqPbvjRS!*!{u_h`{4ai04$;*T2jEUZM+k&t za&8C+Q8vr-`~qeoqe<&%MkAoA6-bNf0OK;abhkhMe5NFX1MHahb|UoTBMme3VzSBY zFzy~dG%4zyX#f)Rht*TI@^1%9U8H=OWrur)hX3_YlFlPuF|e9-GH4+{Py-%mk5>rXa(P$WfYn*Tet;Jj7`f*m!+Y=dg% z9Mm*vv0+eFADvjh5c=vMl41_tdUm5=v7rZX(U7|SmdVqAa&k%W-eCO+VB)a?m&7A{ zyK2=q-0Av>bJxGVaKM4p<;U_~Lo41wzLVwMj^Tp`Vu$31Sw2V|$5?fi!+5jHAL_cW zm0qnk<7SyhF%tNBWbGBQ*J zHv0^nRZot!EsEWQ5LJ@J-|ksMGko3w;Y@7HRVB1=Bz)p-3Ur=_hh(%JFz z`OkzBmKKk!A&IprzPX+IFs>9l3X#`Hin0)wmVlC4vC^UNEgJ@|3GgNp*ZA2}pNWCr zveOhtSnZ_WG)dnh^ybTVsX?PU-&R%=82V!xw%t;a{zCyRGvrjXa(KUOjn8A9n+X_Z z2#q+gVbnN|^^SIVdYg$ozWypLTIIpn>7xTn`fG$kuKXF+kBlNUH*-U97eP9$M2DF_ zDyZPP-A3Pq9xq;aR6MU!gZGQrWVh8iAf z8CWmL3xVyk^6@8#P!#Yr{JN!6lGdK}*adGTktzFx zdLdF14wv-;f@Qz5xNv1<-$jp+6npEEZ(339C9u3CxT<6{{k8maaWLtl>gC_6446Kf zMBa;-u@M3zflfz5yGr?fFD|oqO@}c34M@mLe*VK_W3c!Q@R;7m*On-=#UyWquuyxL zQus7(gf_rqWMG|ZmzE^bq~?z5(#PGs+Va_gii)GEx>)2G%G=Ji&^fRvf+w8f%IIyOq>ycsjoWG*YL+`!+LW zS9waRJ_QCy$+YFj5kF|6-_Lm^6xL2~ zSV^G4t@HCXN>ec+3OsKJ|5s#2NC)wIM!Nqm?tFnD@R`i#{XayN3DJ6v$ImqXf1>l5 z%Ks|u&yD{U@jpxWZxR2~_214WqM3y#ivF*~{@?8!i!Us_c5k@fIOck6DwHVuuO zVWkuXZsPF!k)zppJCjmR0vI{xiqK#AQ%OmPsqWjMo%v#zZwBgc;$v!&UpTq%@#*C} zNb=O0S#EMhKdF5X1`i~7!UfZ!!M`#YkU?4`Bb_TcwXGF`Hgh4Vy1dmZ83Wn5@4-|)CX3JipitM24m;+5Y+^T-ibCLf za)q#DcarQB;$AIKb{fV(1Nl=u>h?Z1aFA9ICxD6SEsIYypZ74lP9sT6zcP^$7Y}wU z^cd{M_H4XPqh^H)<+>R_JQvZ+mPmszOmAo0waQw}ALp{s=8^4pl7{u#ta$cEFT?Gh_%vdP?O)9tBgXR9cmJDx5L}&N{ zAyXx;qfb`WJ{g6G)xZc4Dkua5m!CJag9?!YVY3l9o*sC>ZpWso%{mTVv^^#gVPZR$ zb`rY-4b#4XIUDD@-An4wO(+CL;Cpp?RDJWdXzU@h%Uy7FBeE3G{yz9K$rBgMz!U~Q zKRtIy@U$lsbl=#9R4JS{X#X`D{o^%1UVSs5tECM7q`*;1&rb+GbU< zsWu5Ue{=jp{fv{3Ec50Zc9a?!dAwND4Y6*IyH~!R3+zZ**Af=4z?DYuMVS0}{WMff zJW~|n0G{wC9u*JSzuriDKLFk3(&URcy&@vF{V>mkFqoGBBE>szS0amIZkJWgWgimx z>p4gyF`sRx#>KTJ*;73%cs7z*q1P+H=@ug!RBvwNU6v3$SUdmc^RJ0q6 zCN=O4|I%$(0pjTuNhS9y!LN$*C65?+pj=+~@EYdPV{|pXjib5t;LG6?R^e(i=JFEy-$Y{ldj%Wo~mE_fXzuX1h5)%=0csmAolQl(#ZEOJtMfM-Gs>SfHDsenIZv? zmELXj}vx$B-LT<}gr0Ae2#;wpS_MB_u! zb+*#xf=!;UM;^GrtB&vTLyuu2A;S0WR08U#7inI6L+Y(-|KO{~Rh~d?Yx!ZB>W!DI zH2Jtn+NDz(wZBK&yTViNa~Y%dJGY#u^h66MxSDM@5WYbtF^8t)03E~+!aJmH>LMSF zoOG32X6Q8_uyb#F+`X$dp6FC6Zf&UFk(pW0#R=5Z!*-=ixT2vk?O+2vREp$wIrf6jIqotE-p z7mJ$YrM;|=)&kx;>TC^t#CP-26wQFZZzmq1rd@A5Y}4b}dO0ZY5MmyI!HZod-G6Ou z_a!_izhh%UCokub?xq!?{gw(J#qp6X z)!iArcNOG9YQn>&2DxdW_iSppBuxj^PA!dgX{6BWyYL1TIlJ7^SWc4;b*1bUD5RIA ze4sV$up?bo-}YX4oNF;(TYtNK-z~9Mhtof+@`H`5hqc^JhXO=G)$^nvD^NXr+qH&I zXn?7I!A>Y+uiveLr1*E<@qMBaMJ~9nAEj=bR>S4J0`S8|txbleTPj0u+a7<2o^XBw%QJDhP>Z-OsPEDG{@DLx0^L^F3Rk}e> zm$t+DHHYb$0&-4)ZIgZ@okry7x_y`d0dsWD?ifik#nqvk8_SRCbRmNgr>^|aBZnUo zab*2)`>>ALYs-{cpFIz|%mLS1#T*c9uy|j`F1N*GJ+J=viw0gdJ|;3mY_Q?@gRmcn zY4OW{V;r0rwjs{ECe@ zV^R9VwEi?khkl-Rw^onUkf+UezF-7?L4(4Sq3npmlem)xTEf$xz8C-fCs$PG zL;{1Ur^{Fq-$5L+ForrN-MbbX(3CF~x=u03wo`HvEYUc~&j`VNV=fi0Bf5=Gz+r=Q z_BQN|p6kenN+E|<6@E{P1w_5xfE03#9sxlG2G`~{Jk8sCu3a|}*_Fan(B~C=yvo2k z7+D~zI`!JBm;Jc>R64E2P3~58a$c}gX?FRT0j{!G15W%1iMH?7q~ht$gyr4h=P-f} zrzO*N*}rC#oZ@KvJEC}8^<76s`7!Cszs{DTDx12)u|_4e0Hzx(@nhU zp)_xZQ;K)Jb|S3AW$TSOvqi<=HX00nCZUz}&Nxy$mDzHf>f!9ca@F0}=$;>{vATE9 zaeA~pS!=W!hT4|ax7MSdnQW8co49_xYTMez0wHvqfs%oXIx)Y7aQssB@drO|sm6nFG>mcEet^ z@u~u+5GQYR6?}niu9|)pC5IEXi(es}2%j6rDn;0MQlHPd^YAA7<;1e9}m*{MlXH4*XrYSfA8D8=w!NqmR)Z7Shgh_)^UCv zgQ^zR{}XkY9PMxTnQ|?DaM?`&EU|zq7HC-fI)*>m4^^)K1e+$vYoqYKNr!F=fJ-_b z?&mAx{)B14Emk68<{(OMdx=}F<@Y9cgpRQ_?BD5(1SJwQ2*!pQ-IGE`r4;GwUIUsv zF{(97Z+d?TccgB1;Y#k2dblDr5RVBltWhik#b5V~ z;GLk=TrhsCI2Q1+D01|H2U}%adlP!LQyCa~xcL43b}tBJo|e!=170*1MWx#)AIguf zAaWgD-ncaU{tr`4r`b8XPwmzoml28Vwl39WvblS#tL|Qcq`gPsnX-_9-;ZYs*$ch; zLA+_B`>Rf^b&fZlTGb11@8g4b-o2{bs)lOU`n;aY@MY>=Uvn>N^>Vn|*8VB2IHX(A z{c)#V8vOurWAENtcbrRhOk{_T+lXIpamAOJg4&~0ow`u5%8&3fH&y1EDyg1}TJiGo z=n0jo-Yrb(@r$?c{e`VJVZQrb1D4UmPz_sjYOIvoip-^LG>Aib66c^hjCZx=+f6yx z&5j!9P<*nv0ori|Cl_qzBFkv=x0;cM9hlMd9o&CwCE{){fepy6vFLD;ad7YDRkS|J z!z!ofb=@CKcJ-nss<%=!*KcoH&F+(GqL|V*#vc2xkel}i6)*W^3+NBqmK!gA3WWZ#Qwz}tZ{4~W4t#X z;9UKnD{W&E{#m)W!FZ9|K7P?Gvt_oJucpLWL$ z>V_Z0}~8aK4gBlOh8Ze?!$=i z)V2!z&4W-y?*`pb3g0{M=(*~0abkh04j(2Ja3hBPq{|4U<~l9)R*1W=Br%|rVyU!a zRZ?SK_*}xFw!z^DhY^o=N|URt<;@$9sU^>@^D_y|EqQIjL$!*te_AMfCC zaB0&TVQDVG%L6VOs*}si+*Qy5%CIIGpwpH;IPh7vnmzxOG0u{=~-pXwb`8cf~sCM^cq zidG5<+i6_19a>x$5bovr#_|OUk<1uRKgpd22@lwK$)Trv~gA z_u`8|nA$Z!kak9N52&?|ZM7+|&;r)A%tdHK-Y&tUMA0Ax!TVHTH$-%4i}8<~n5epR z9Vj3Gq{4?s`fydc^Di<787*^Bk7d$Z*YSS1Z<0`54Mdg3s9p?psJzU)F0e|hz%y=d zS@?mfR|r~$7LPp%q0Bw?$+c;Qf$wy9AJ4C8j0d@RaO3y1&1_vnJ(l_(nkKRMYa)An z)NeEoL@xD;$EpW=*iX=pT3Auy0QhtT9SrJ)76eOyvn`d&(cW;0-$j0Gh92BP>afQH z@J{mzo0p&HBX1lFbLXScQCMU;jN0NSXN@JcI+|XwToXS|38%=#ySmbp3VG-yyXH7~ zJ8=yJ_CU(K)srcKKfQW=G-9w988k3Yn~D{3#XG1H?w!DW{|o{KQs}sUlXuv)AC$~h zb^uB`R}!Hm1y7XV9m|1kyy!3;FU>v3+9mY{g>pV~lk;KE^!6!ur&R=7OD-12;ImD2 zqU+qH2f?-u=E@TszxGNlLeEzSUYNXy8nSvaraYxsb~j+eT=Gy!w=1AQf_Ty7G2H6U^)0n{AgFkN%1J$LpfPw{eo;H@;WVoXlxfx}tveTW>;bN%Xtev90EyN3eQ|}{gzCdk`|nxW zt+`gyQ1x_XP@|)61M*+CdWfqffb;xEO8Lv^b$Bx B$o6hC-{?)6$*8SxfQD6x5jY@YX?`%f>ti!a_&95hf1_D3 z9S|&{w*IHCfu!Rx8EBYrt((C1%~q@1vQD2K19_^58K#8vu}^7Mty6dydKDuRGuTmHB8Vy z{Lk83H>-7;(Z1_=^B}!RDS262v)YDmBWN(fg@_^Ded7|hVO#PTeV;+;QXXrBJ@+@S zsS*&D9*!d^t-JSiMA8Rid+#k2-)!K3F!3Q1@AIOw-tke>`us$(qaTn*$JwjMl&|6U zZ+z<|)93~U-#bKRsAs#Bq3{S&8p7M}xp}E7IlN;3so@Pn`4PIAUyp+Wai;X?wIpD& z<1=+oMFl#$tm3WlhF9;VUQk=oLdu&5AuhPCqegKG%A9O?kC2BD3LuBf`0nBQr=f=< zy$n>`MLhJQ4r3BLtftz<*6E<6!toNM(mBFz>X>rD1h3Q5CVb=uI?cBZtBZZ>o!jFH zAwo`-Y*_hSL$V1n`w0}5VAbRn{>BhJe>2FZYE>#+4FD!ZJw%}%9)+HSy}HMs%?rr8 zdV;9qlPL*ujL^%?GmZf!b3-F7ePQG&#PaFKAvsn zSXOd{u01G}&pREFZL1o46jWG5z#2vP)#t^S#En7%T-A+J7(Ds%N!&wP0&{IQ2ND`b zrtQgeAcv;!*mS(00sx%Dlx0SD-0cHBSJPqRoOZJOOQz6lk;j;?22o zD|>OeuD)S$=O#x6N^n=4eNIQ{u-$S`SHiO0Dh}>Iwias*OXRVr!( zgU$5`H@1Ac<1FVEW!_1~3(~&|Ud3BEi(gF5VW^^No6OytaJ&tE$L2~Osiv2KR^wi0 zCp6o;;EK|p&M;GyO60<4X69Pu({L^b|L6(Kd=wrutU|Z@-N2prmGsFy|K$mK*3P_y1S8scCPi0&Wg>x=$ zCXl0DGiRpVp|!=s;GJl8qn^JdUACw=P~*^wGZj@lrloz$%l5!>Yc`Jh26|Lm-k=` zKbh|X{HnB6%4v?F_3>d=_-^o%fX>RVX%&-D+C$`T6dFY+70MK(>Iqb;B>PO;u`=i+ zk`NLiyFTJJ2Q-GZBga7=_+w_rqsMZpcGXU&_vJoM!tG8G+rSGN1F9L$DWyPPI^1u} zD4qMWExmQiZJ{%}-;!2+Z}6Q9EvlPX>-s~oRQf0c`^fatV>_*ClGBPcZQrCa1?x9X z%EfznJTS;%ZqBTO1C5&?eQ%D~!_5|*#D($nni&8SN@_lGU@&5$tp`-sireu`?MTSN?T*Sf%As*frm33Vl$(*fV4vXz zZ!6629VB%d4z?*uwts?OwU3c1Kr0w&U#HabYk+A380HEr>1zf^6=)SYPvFc?ofGrPmPfb39=5M`@+ zrY)uM%!vcP4K9}de)HX;*P({oq#|8T`t34@dlN>QDMEQEdw5<5d1UIJ2|SO_~&R9ndz&ZOdBB!=Ox|lr5>89eeSyCF4h}%Hlz%*wr!O`wv$P zyFAytoN=Ugce5Q&0nl$9)kIX=b$!y&vPsp886NFi&7twKD`b45GpuX-Lm5rf)r1<8 z|88XNER-rwi}(3u%KST8PDHLdQo-U_NoU_2qQjzA6hG`^;X4n+9-}cp zYDek1NxHdFDlsK0kd;nKa+$eFAPa$FXColxQ)2NLX7aF2ONv0|sS06%7i@Ou9m+X1WR&-gm4qRV0SsXgmD{+>Gq z;0Vk1n6aUcr}un+u>E&5(&Pm+y)G??^o}O_Z`s&pv_qsxG};qq!OlGo4Hk#DI^1sC zvpJiX?babh6Th>@Ud}6+jeQK3s-ZwMd|A70M!V zUw}q?j3TMgr7ley6dMPLq!je|wF7ahe|s#!(9z0dKKVt=PALy#qlQ`a=m&Ij9m>28vpVXg@1ylay`5OF!89A49D4jY18HwTzamiNwf z+=M*wRfMg)7o}zX8{j7O#toXUJTyJ+DS<*7u%_DVrQPc5FxbnR0bSxN6S67 zQK8{mrq_5~*GY(OQHXV3x&|8O z+elINWyJhsNUk~teyf-?-?{vyAw^Dm`y#wQ7Q-&82A^(y)7pkcJqx74(5JBW!HK(7 z^@8R8VYZ{gm&_k+oCA~QMaoWR*U8;gWyd2gJ>V+hXqSs6>j#Y=S7*No;!IQk*Hvw( z${uU8!lzm%ukQkfa`M5>npqg-v-?XE>BVdM3Ik#}q_$}wr0uRoAzGqNSaU%k#j=@$ z(AK?Ir|NjV`Y?Wxl61(>pqT}Pj09I(4cyn-<)xF$Wa9=hSDzMq@m&}>acgtlAw z@#tzdr1KT=5p8>MM28RLE{tYQ!$rfH@{ONy%vgEZKE1I?H)>~JNU!S%fhlS8xIgOn z`-yXaoQSVr$H_ynOCz|vHjsx9a#N}WwMjaAnWMrQY*x*_ zqeA<~;!>Pgag<;k&Z6Vw#DJYS|9o=brggQ~7r7uGM#WLxV|k9dNfUPXN7>^NBbtj; z=B36@hy2PBlU9wrftgPeum0|DnkUyv4PyxwGnEbAkJ-B;!!LGPw%D5Kt2=y__osS1 zkN!mH9Os-`4B4L;f5+2M=;4Vt{+wb-Wy&jBDz4_zeuTceTJk4NzZ_64r@K7f4ZzdWX3wj})OSkw({pjei$wJ({P zu=-{Tq+kw-rPm^lIMEC^?EV*Xh3We+P-JK{a;9Pss*^-0HuJ3T9mkgH8TGlLDWmD*?j8~wPPECex2>GckgsE^Fqj|m!)L(jizJoaLam@li zEnL6*C3Ji$sC_(B8+Yn~*Dhvn=o*chZ@^LgH}dTSA`iSnZJT=hJbU)#&cOG9?|y@G z?-=ptM8Q(Io$b-gH>iH+ZTl_f!~$}JYmacPzvM^ZBfAq_cMR?1K|e{f7evRGJ9X*2 zc;u@SQ+9sUN3tg#+;i5BV93B@cQ>H~oLM^dz%!gT%i}cgvCVTVOVf(WvS7z^o`}rp z2)G~EyvJ*Y5?u6_Qh{{$r#3yC+&6rDhKd_C#ix7tRmU6C**MC{o^<@FOkrPgALSWm z5)F@G^U8(^XQlvnh`{?;&6QZ?K9O9|1zZ7HS)fgOc@76~7Twx6#BkL$W%a%rbvy#@ z?}8h;*GLE_VUvPee|wY*rLtj)`*?!A2S+DikIhcE+SZl9ZI+rQVN@xHk$&N^$p;Nw z3|#9Herzs$x8bED9VxK^iYKmg%vTAldGQUEhs1|17?7Pvtl<#ud&c>=O=UHXN#C0{ z?&s5ByGeN)Z=vDS*r-irY=UlugE(B;Wji4y`?W&#J&ROlm;51h-jdxMEh&%O*VJX} z6+ZJHzkWHkVuZF-(M%S|WGR+rrIGcNUBBna=)Z}J=-^e!epIqOLEyO@XV>-;ad1&# zPor~v^;*b_`gw zA^V>HB&V5m8BJb9sJ#Ein~nC_b!|-68nUqxV~lF{1>?lL?j)0oTCU9BHPGbK60NAQ z64#7K00VvyhT>gLb5L!e7r{%xm__C>Q|bQ=g+O}0RcO8q0}5N-QTlpW)V zj&}Be3dxeAGe&p>m;!m{X6XT<_``wLS-u5$o@j(M^S#K?>LE-at&=XwcI&YT$YGXCc~(=Sq? zcS;pF?$;fd3t3mI0E!riQ!4cHAaS8^#a5#*q2J+;N~iTgaU{>`bBgPM5)kkCb5AFb!J5jk29d*&~K$eoLATExZ^kRAWy5354qv!>Rw2rv`wNyrk5P4 zW#)8f(!3L1r|~W&b>hIl3M}s(n%5`a!Yx+%Z4UWq42d`6E|9iU8YRyUE>P< z5l1BSJNmV3t|&J$a5|2DpFqKts}03UFUhnEMJMm_vbp7ovH*cED&6R#DNQez3ZBAa zsk;{9LKGL`IxRvdLyl3IPZS%V>S4ToI95uR>BICK8OQq8H$~EH+uOl-p zez|qHz!DmwJ&_R8%$Dc@(#ONejWSDpcHwR=1AOA{KzC)>i4WoSZ+8isiTs9po zhZ$u*MKPjwa5NE;Y%SV=4aMw7IeB^_(%nuEr0xpC3>4VUP%MM-QSj>9%jaWf=VM0q zHWkWJITFS^wgp*5g;o#DkeyK>`wr8|7^m)t8H6k7I5@{q5t?|%{I;DqK}NS!iVGbj z%J}=|fz+5BehD=$cl#WI4=Z1Kxx;}1mn-hy&Q%HG<5D5)W{2`~ ziCM1VT9UO+6kG_Cd4K3#Wd%HwncBxpb^N>%JLJjSoHsQtJY~3ij6(++7g8-4ua=x3 zlQJ_&WzyTNMCB0wI&ivME*Z_TJyV)W*gSClWiykF9i_w_sH+z`iwgbR%-TATYSmt1K7Pk1oX7;!tk1PkADGM&y1B%M9hF>p_Y+wY^n90?Q zc)i=LbC6H!F-YOl(~qXiMy`*6$u)-4|Cg~k*U`W<%l z`;}T3HZqYpXJ$KAoqB|xP{^pAJSC)@i3usE8XcZ0!o0#$BR#jASnoKnY*m&@Q#4I{ zyWLJ65bt7cgPsSTqH<<>T|Eo>89$ykozI(fHah0>WYZ5VreB~!?@6l2V?S^zK_h!{ z;T(&eN~127KPA#gT9S^Lkc1w89VxOfEnUIpkggz2GQ8^>=n7JVkmuT%AhW6&vOdSN z`}KiS^~`i4rf8?&Jc*4QNd?w9%D9%_#cD_-{BAv$UZ&~~3Xx0PA`ms=Lh>7{SA`+V z7A900P^XW&PCpOWM?fwIq$D0BSE|w36!@crq%EkB=wB-g5@lP__sO@5qI8IR^zb1kLBl?V zE7aOX^BQTIV_}S>fGK^~s76Qve3G;2 zIBF!AwBpS=33Z>=cGQ)_#TX7vKymlVwJja*F}w@cFMP<;enn8NjZeO`$V@WJ@lkQ zmDp{j=)L3xIr2AS4M^GTU2P?V(LS**n$eZ6%=}V)1hJ2 zaYbB+9|z__8WWO-A*^Gg8tkx6=PcNiS^;ucd@XBBlLE$k-}zxsjgGNV|E5kIOorCH z+dIYW96fiOjOIJ-WMoDeX%gfl?I<-7*JOI_N%y?^c+}@J$obqL)wg1d{#g}dtH&0R z#R{_Zkey@S5fxH{e#&XBH7H`18ctY^3sKZS0`IhzQk0Rkw?{79o~~a(;>N73Un|3H z2e}`@50@+0sWDkval?jmP1CHX&TN)lvP7HabFYaqEn5`E(ye6!$G-)wd9<9S`Y9*O zh}R82anylTK>Q{W73wMy-qajRy!4zd05lc1G`-VoPugG+LRD%K2`4IF^lF^6wlwxtFTU)3mYy=G}%4k>QXI(NGsQJ!sk^?C{|J8?LQcjhgK4s)Y2Q#&ezXp8(2?~2&Hf5F+YC> zp4ClWH!M|xEV{t!dJ`GZXEPjOe;nC>4=a4<9x{~Jx3emdt;+s1geXd?s{3n}F07*s{q9VApvan#&7fwoO-G&bp0 zj*`4lfk)d*x{u^?g!%b!;OM3<_0)qb?Cl@Rg&s_WRuvU$ zKB5(}+31B8TI-@z%0~N+?9o7Rp}%?Uq!h|8u0~|(+24O3rAXzx^rtRDb|Pj>vrRkb zjF>}zT8*xph0^q0pu8Qcf>^SwRo+=4E;b&WuTokaOEtb*31>+--xLoBQ2Vx%Z+mr@~1rSFo! z77|J25S9ZuJk$!&N~!pUMdx+&L1Ih2D?J}MXGJ^xse3)hzG&}V>TQxLN{vg)RV+Vq zv3%cY<&X*@>Fj;1E!3)QUHarr^=omh3b*81H-I($P#3O8B|cfOw&wJ4?S(NFHOb`_xHTRo)pJiHsL2#NH^RqVXJ7nLXOQTz7Sr@KV4TUSq{7n_k8H z;$7$^Ue_|$l~iVo%X+EHA7YG-f`fGH8W$;r(;|Bp8-+{~q@_5peAN0u^K*Qg^}22; zJsMp2T3+hscY@Mbjru}qofhm&sJJ35Wv$#lwj5X%meCmsxME}gF zP}~sTGO1F^SaM|YID`j-xA0mEErp4WgVKK4Ib!k>Bv&r(+<9$f7dtB%t#@t9{c{Z7 z7|viES}%l1D_J${EqingXUntTQYocA3p_*Vk+;t0mgX^z*W+%KtF<9KLgv2xvHkJ# zK*jVbTP(6O(1#TY!h#1DHMUVLXjJB0#tAuz5*JUYM&y{#-V|-3( z3=-x?Kb!@QBzWVD=32?qhXsx7u@JVXZsdfk`v1tpKS*Xv1VlW~4^aTkUu|fv)KEl0 zg_Ie2aaEE6?ZKL)NRv%qUfexvD^yfhQ6V2*q-(-pl@kVk(dTfr2_Dhiq zrE0)@DQ#V6TI9$iJNEmx2*E5@U=vsIv}V#p(6y_OL`O{xU9Ye*b!sOjkvZ1zNE~Hi zL10I6KfV)KOQn)*7n`MM%H={-$g1W@b?-ENv`RJ`RR8H?+@RAM{#N(o`$adZ}zx-3g_Th=G%EWRVK3o6^kT&PE9=-j`)w9TIaH z!s27E>1>G$@iv%5qJRH=(uk@kg;Wlt^5|p-)vHO5xEGqR(dr_2E6}Cu7Nwx{5Jl5C ziN?{LlZxnL1DdXg3bnfDc5yBribO^9aChY4JxU`OHToTeaJRg`aIrJ%i9v+VKkZYLO?NXYl&%AjeC;iJUo(de_}9B^L%8i*w=)FHvkM+K^glr!5m|GZA^{#f9Vhn*Nh*eH0dFF%@ z@AGHU+My`3D+)q$X++!-voL<$i!zo;Hy6ZDoXAoSV2$3L&u%s*H^mK_uJDSdkCtz? zK1!IRoYhDaN=w3NQSXL0OR!Vt$kfL3(l^+G_O{Vp_CT(%MwdFLyRs=zA$iv(*3jBB zDx~psbDIbE+t;y*EwJ)R>&P@Ry>HUaVe@tw0Wn_zu}@wc&PR18aXZd=_!S~X7iVr(WWnVvY7Fm z@pIR7F^H!3N1-H0){x9OBx7!^TW;&{rjfaw_elm7DrD1R&u-H<(nUw@Gh?yy;rHRE z6jxF+)~qRUO>>?m}?zIPCs*XYp3;#UL3I8Ued&T!@D9J zuBFYgVW&AegMMyc_mfpmtyuSc;e1?fI`g)!oXjMSj;iTbVKofX<9K%%M^tEBeFhB6 zrrrMt-*T-Ms~1O}+)2YVW;dL$kV(Nc-%&cxvSQjKzEEF5(wR)W-qG?WbEWV2T9Qqy zx3#Ce?Bz!d!c3Hb^%CuN;`$QSJ@1n0RWte&Ix02QS+GktCwl^tjI$f!Z9&U=E5?1a zc6J&gN>oTn=wh+8)xEFyUD?7pBu)4fKoRwKM+DuQOoiy~WMg10-TfR4JyqWH;^P(D z!7lW~O(K%tT4KSje6q1;eP2(qSN68(*OkA_;m}3#x*dhD%BdxOg>1AUW9h6IXfTPXWYdx$OX4o7)2NE+oz!hhHrP`_pXwcZ-ny*| z{Vvg^gGR7 zd~JWAD&KZ}kqR*<E`1A@y5va&>UI;_?4^*k^PJh z!(acL#-a^R+(0dZ>%Rsz_N2W~G+7fdkQf9}SST_PC(aQ}ket+e$&OMiqP=8`)p13s z(5Q}RPPeaRkg89jOmT9M-+MSLpHB``U8$u{E_ZGkrW0E=jr;w+>yF*s(RZ)S>+7D2 zk+|gHVIcW#nDS%KlXoKqzI!TDp}YYH;e`rGCyFR~A!Fr#G)MOHXHkjmVnK_`W$^1m z<5IbWdhHn5G4Wl~9Q-hO&wiZ61)QwJl6YE@LY9tsFGq2<>Xx_=KS+UwZUR}Ldg!gx)DLP9`xX_V`VWa9yH@&7 z-SXU0QLT5jDuVzOTBJhKgY3qlTOOfhIC|n!OhB6frQ6|@U^BhR8ws1Zo z+qhC*S3J73(_^X7cd2urI>aN%}brN6lDl6Lc$^J~H-d-{% zL&de`Qt7s8ndlK~WWlSu3PDNPU-wSiSC8r?M7P zGfI$Ab~QRYrV27Ux>(`5oFJpj$!<+|il^>gM9adfuGwGB+7wszT{PirV{M>GmDtN} z(?;J4{`KzuuoN#6Df-tDMH!WCN~=} zNj~mUs~TH5x13WUvOJ}-8DF*(tD{2U`lZLAwP#d_BCzZ>9qpnxe=2G~{?)zZol=+* zI7GBgxi=Hj^`0kz7!|Uq^eeUI5y@i9!DmfQkTs+fp(Q>aEi>4?TB5964iKlMS}UZ1 zooSjTjlSJ}D~RRmQG`84K!d+tjz%brNFtDsGYGY`1Q6IT?HQMJkjF(`0Ywb>5_VQ)`(u z(-nRZDmtO+fq&=tzsTZf(o#Mmy{9s)PrpPI9T(4uUa}LrU>I|Wuvgx zl3OOb;1NUUaz(NV-)**}r*w-iN|}^W#Lf^->Ps#s8iK}^}Vbc$`g~D z>${^smL+D7c@Wz*U!LDs3YCqI)1eUyf1Z9vzLO;)Al zB+Ut7Kf)EFgSNTk+lBRA+~bJ|Un*0RF1u4w)pdu1xC{#wA~7|4;!S!t8LpO4Y9)K2 z@FQ2bTTq>Zu=?!8PKExR>&s!yZ0PjD!FiwNxpcu?`}e&arJSS(GPRsgOw(WOCm$ zUi(*P|6ZpFdReY6pASuH-kVF?r1GeINUG`a)HM%6cWAD(XSyGx8^UgPZbywL)C3zZPYLXoa#&Mb$z+ zIu}MEky`#5@pWWTHbUE z%u@8{;tE6?mOcmi+0Q!_OYv&s&!i8g(2UZ9Ow~ZnWE;?Zq-V!GaviIh{n2;Pq6HgVj+gsc??FRN?ejo*lH z#$GTibH0D*Qy%t`lg%xGFZPAyE;vaRFaAQOv9u&$2P%0ss4+m76E#vWk?NtknNvlw zHnDk6oufkWuAexcx8{VlO7zaD5Ql4~k_xHI3R{2CYxU}0?NX~)>J?9cu|XRPawb#g zZ%dV=^qX^O1!u_v(e*T`X${ye`UbK1kh+U;epUmUTInXi=c$YhYjkK}g9pxGxU&i} zQ_H7ht@iJD;&pkXDOd`U?im*PqbkT=MoIPFCKaOFN4jnN)IB^^K#POj7_Az-R!_(7 zcXde-Uv{SF;XWBOq9~1@OwuWjhBxnt3Q@kzCMSx4n@q9IU7ZI}pX-7O#i)?g%~vP8 zIu1w6(q_fomUjdf&gWu^HTe(5&@V@YHZLp3&Z3V%_xjpR{$Y^RK7A0_;Ch|vKtBer zW>D+3Hq8|U&>1&oH=2V6-C106GUHfKA-?c28ttsgOkGU0b8@ri8K#%SsYwl_Wcj4& zo%^B=OHzK)c}=p&jxk!uF^gUrCy+IeQ0G(xC7Vf7|GH$cxTfu>pQg5p&UKbtX`|JH zP$9z(wDK^tyge;TMYw16x^2%E3d*hYZf1g~DkVQW8dGeV8uMf7B4}+{)+?G(YAwe- z8YJCG6U5%F3ayY`sp+dMOsxO#$(X^a4%e1+JTF(N=a^K4Q4b$lHV4gW>0PQF&26$@XN|3N z%gZS}^-g!ocBK+l7#?$(S<9E5B(K^S(fgYEj>+Wz*Q;B(ToK6}!beZ>Y7@ofVDZI? z@t?2f&ZXwhse;Uz94|6#?zI+^Y{wQQ{^sSqc^WWLbEx?{54xwx*;tN4y` zd!x)G6w_(h-HUCASnEi>i#^H0{d!&I5A^(*tTj^e&zbx12FnSM%&1NG&?HuzB0ywQ zJZe5jI#nqsJ+oL$EJz}7-d}9vcIK;2a!m~twBA(7N>HJy8*5G=UVd*@vGWoINX0^y zpM>~0bZ+HbQu)amwA5a>jls@vqmj95cxYCh@51O2>!k0U=9>bSw>EoqDaBIRbTV+{ zJ};8%m{B3=wHUi6(9Nq+p)Sg%^;OtjGlWz)Jj;_yFrUOD*`mXJBf%XaHylCQtw~MCJ*BM5xV_a|vhi6f zRLH~FuBf6yR?d`TSsiIL4fm!>N{MJ{9PU@@T3&UWplC|r&YNp|Y}uIp{)n7FE2KnJ zlKmBDK_R1CrBr3al@*8NmTA=zs*mkSNsGK|NQ~7J`y(-5o0rvo9>5SP#G?Bcdm~+y zinl!p%!Le#J^VaGB{OzbL|0TOvR^KJSZ7M^3X4UiLYCvh z6cx(8LJ}3?QJs}S)>c=lAv(4vBO)g^0F~s>p4)5PsL&;5j`aKv-P(&cUAtcE242N* z-IQ9?lW)1pE+j)QEid~+wBnTai|>CU>4uVFIZ&aq zcC2qvp~z0CK18~0NTtv3L+rufRxy_u#jHa&55}%FSSXyECm?EI9F?iLwlWI5G8Kw7 z^exM=hqX{46XVsX#4tK()l_I+K}LN#V+EPf%89qm=bCm(6@{#PdD(nY531WO)8M0YXglECd7A?OtWH6oFC_AS5l!Apovt7Vt<~s&Xnl1 zleD1ka|0tbPmmj2dOIW&wv?rs179t0gKVY^aY~=sH1W zV*M|f3(*F*{L$KBs~c!SZ?$x(AVVp>QIT-_$j%$_Vy;-7-K|wqV$a;Dv(f0NeaA#q zo4gTZf=G(d@b+*7lHS~&0(g|&=t!p3#vq$s$JjhPHE`GBYTt@a9oITaQ#Y0jDzQ+z zn6x=Eri!I4Q6Ub~gmq}56n*$>vL;uMF%^o$x)*OC7fU~~5{#)(vRt3or)W(6<&*dg z$6n8MNwn{lsE`)w-`8lmh_Xn_EEv0Tug|u@C7cRt9d0Uw8ifr1b ze|zlXOIow2jB-?>*EM<%QQv!t8in~}kg6g&hpF#mL)}i~2PSE=dARNNO`h2l~a zTU@)<%R@VFJU11}880!fl{TKabRtusqLrtJ4-V=vtu3pfJ#kJ>@K)9<2bx8NQV}3$ zPW#AT@*Op$D06A|oQ&{Xsp;3bP0Dt(EhNu|okr8y7*Hh;fgN-%C&+|%v1FG^p`zP# z+UjZm>2RQuT`^0yISfkgS4$q&8OS@>D0vu?9%T$VSLR7lw? zn^o(4#<8DBg-V5$F@?`?*@LX>-2*OfB+>bznwZqft+US?FWFHN*}s1u=u!QBkSd2T z`J32xgB}72a78NQ5*2Ec3bDaz^1O+)fxbJZLQNr?F6GsZHACVl#y!YVJJwPwWNIp; z>8Lhl@#yt;z0|ko#+bTq7xz%xANpSV_A-H@m0`_3K$B+6<8)DodQ6^9=!Pu_IKNNg z0^?G_nOCbK3PgFak14rm?m;FmKAPM6Amvk10;x~%TYa{^XEFVYxgjbPdkndi(}_+D z*QusHa-nf0ff3fCd2_brcx@KXU0>MDkc1t)YYx*2_Xua(&Mw|#F|5fBCZrZlNm;ER zqkDK1i_qT3a5xpyM=E4(l18y^+I;Tv9MqPu3{?PD(;p>1m$E-Y8 zc9RQA?HkfGMK5(D#=uGI`XDNlT(a|`nca_biBp;+C_Bb6A(nilJ}O~?_|=Uov6%0) zc&VE>ye69vYChYE&cj46y22dUrh<$?D~G0VHCl1s<>*nri*}6tnynY6Xp%TeHILWQ`5 zA+}3y)wjYHE9bo75;t9P^r&(|;P6WxRdq{{xR6b`E4C;MzYm9Si54X)WRr|y=0q~1 zf-@>ql-!6P0%dDCO3GF$(luL~qli|B1C8xkwL+fbbFl}kl-0*{<|tHuVkb3D1#pok zQx*24lbD&rl-gF6*U?xp={Kw@`n4=I?X!C4UD=6~g59eW>Qd$Xpz9(l`DdNXb30Za zyP~{zV=AvK31!xaXN>}=P|<_TYdeZ7TqtfuSFsm8GogQuziKX|1+yF!)G&BaL$3zV zQV~HP_D!qa_F-`Ed7|?^l5ANJ3& zS(m4HahtZuf{>NjvIcR(R$DIl+(;|-E}u8U+p)_@D#2;9#`uv`D0%v7A|(+a)nL_B zh#Jc2YWc5B5Ha(~u9*@M;|wAfM7QGI;>xIy&96SgDpw3rQNEAW=uz9piBB%J8e*@M zB`Rc>sgN|__2DmGVZ+P#ii6X>NGlE--XlpFVwu*_#Wii^rD93NK5Rnl)CD3^Aq^%Q zp+|#V>_O)49S}0=!Z5{)8nq}!+8dwoAuCMi$ZB}KoUh3iHRZ^xJQ?KC<$2oWvp?{4 zWh%rEgz-z3$m~68FHoVvUnl9Vdg;m$-twJ3#aiXZ88f=2Z0EsbNp)>iuVl0!z=$!X8>lLTic_J9xfZKU4oD5TK3he`ldm)~}3Tw{uGZ(jus zW*r(+IEX^x+Jo%Y2fpsPp@j%*al5}yTK&(mSfot5zoFY7zn4id&CZH1*;ZC^WRetN zrjei0w)x2r4h_qxTpWj`XrBT#!vm zS3QExe}!yX9=o^CXPFAwESr`(%jP(KPG|b3r9xTdU{V9mSSU_{K`N=u_P;BxqEBrd zkE8ldOX1N!CynE?9}pWG^tqVc6;y~7W7Nl|HCn*R2{X%*n;}(fS#1n8YXO?6P)d;T zE-?BEiE`3MLe^()?YRYgNrh~sph;B77I_Bg&Q3ZP>8+jP;XHK@(n*QKK7AiWwQShu za?m_ep~u0dt?Q5yDK7SWpIT{0lBpPNl{hA(b=Lo!63^wuvs>}I+x2Xe%A2EgM-D^b z8uSCHq=sHIp{QI6)(5q8b%zJG6Pnc zqgc{>liu5lQ9>U}+BH`pZIyj{D0eSA?MtmC`z$`WIoW0#L zy4;zH^($||y1^L(Ds)DLTp!)0uaF`AT8s&$28%0-3#rnTxX`G{uU4|RLsd4NsSr1s z^t^BPQYh^?(tViwwuS4dTtZpEg0iuuE2aPc zzkGFu8bC;PY)x_YzJ0qLf@K*H^>K!Mm}c0^!HTIzC(m_8W+bYGNI8f?r@v03LNZ%R zZ21X=I#-rW1xBZKC@!1MO)YVTJvY-Ai8pe`9c~yADs63Qu}X0+=h79$3KWVrh-ePA zSywub>~}oB9O8={6#h+BLt;7Da9LNQhj^~Ri1KG`9G58Vnl#5s`;Z$cWS1t3LC>`> zYK0~)uj&ky{qbwYnucC#C(F|V@W+{V(c|L9a!m03rNc%3pw#mbym zX5UJBfaOGDa)WzYxd?bC8eBJ8FER?{+QLyQFAu1Zx7>)|Lm(9K723)WmZF19yn%Qz zkm<{Dyh@*(fhn3yLs2N+*u;ky&gQl{0a)ta^)zt{kJM5`?<5`xWL#P7PmeF*pSqZ& z=fv{d744BlH-1qMXhumyP0aHX`sasdLHd&;<(cm}V z=w6T1?d+-)7m^&A)kh&6;cLspgH01s>G`3j{(v@Iuv}r$vnvdL$2o(Lrn^YHk|M71 zB8Y=GqPuzW6XKPUg6tAl`2D{kM@F3?%QyyWsgHt7TO`QjiXwKQjxMRKT`r9@)~dI? zR`?F*%ckSw7v01=x*fKwxA^QvC={*?84@OD{n|I!C=rZNs3`d^QAmenu-h87iwrKe z2a)v~HQ9IE2gAkxqlq`mZCNX%ZqL@;9EJF!94n?{EQr7ZxGUBO?C)`PRWKw z{DpjUiKLmx6s4{C)UN3N>?qH?W%{NZm8E4};@xYb%ZgMQODRbB_{)$dngrHF+vbQu zr)ChzBr~=sQVlnih|u4(+9p<@5GPZt`p@KGlpj4Q*%1oy8*I1w2;Ma^{aqv66jyAv zBhIU5P>7v8u2~IM76C(~ zTA4+S3i6C17UNpU%omX)up~Gnh5)r>5c7vrJni&pytDwN;^$b&qvJEfX?|PMMb#Cq z+g4|IWMYz<)WUCOG?u7HKKBQ?^PDs_e5OWT^FGW%JaG*K2!%WjXa?|UnKH+?K|v_<}iZ)BVgpbMvN`Jtq#-&RtaBR2?}qC#{n z@=_Xu$`Tc#=EEj>^zz6?zI)0rCuukqi$u-g$H5y`Zf{MBtJ^e=mA>JF_wzv-MX++D=OMWZ-OT$3djh-sg5@ z(^SQ?dZQu-UzB+F9F=mWX!qSWudn}US5de@=rnAa)4+JdH;BrM<227`3P-fBw{0w& zZd2KGo;aq?(K>CMTcWcYgNApFwmGvue6wx3803>MWZTaVWN+jId37J6SJ+D0WEDs7 zMikN{Yed5~r!nSHC?$yp=^Yf~Ta@n-{23a?XcB+H8 zE*qmfL`0!vS=7YQVH8*8q8IHTv(ZzUI_SB}UWda=LQ4ErT(;v15AjZzSwBRcH_|mg zk0`y9=?PTJ)(FkUk1rXygdaO~SESSr+OhqL7`aO9O00;+ z1_pNAyWq&>!P#j9{`0Z=IMa3|(}EBcnsks29XAJl0GWKNqlZ3rz@zM#whhW`)~$Bb z^8=mm#)$H*#stN$WuV7DF0gBBP6sf0a1_QR`&OD6F6|AqCd_F*OfOBetz4@-SG?gN z9CXFiRA(5QTt{Woa@VtZ4;yG&jLqe-@_x4G-_OTUp%i*w{Nqufw0_nyjoE0OkcvXt zxn6-=Qhv+rb(cyv*33fxOHm=sjOLYxPWPa$u~IItOW}&optQeoR+XEx)stFSY;Hr$TzH;S+qXD2hr;`Ag6vafjJtAM!GumBN~n3OkpGJU~*aU84F@!e$bLF107v7nX- z>Q0-}>BA##Dq4tLHq&$SO951-7))ZXK5;sU9`k#0HtbyP>C)%pID;&0+l$Ne@89oy zgOF7$)(cS=*-m|78v3-gv{dx`P+xYCm2Fja+^nxDN}cH(9mS;MtfxzWXWofHfr+ET z=3I?AThq!*Qw2{Sxwx00pE@w;Kn`zYB((Lqf;GDO78hihAS+w~rcx;{4h?I@j(X^lH&!ow?8`n4`enG0C%wM! zm;F5f6-swT^gvOAc)8UAAus!SYJD1mn=yyfdG-g9D9NT-#X+#5#R zMHn>nn!gag%#1?(E-Ja9*psnVhDR`{A9Ba({hlj)WqPBeLc}G-bJ<;i>7PO0U(+4ZpYS& zE_R;UWG_^nxQd^&>F>GGR3DYtazeU{WP#%NUbI-04l-S`==bySnVZ|J22N@Cv=j&m z(RHDa$y4*%ULy&TROd+H++?QKnbAOuR!u;fIP)a@*sVVIb3RQ`Nb+(i4pgF$)mH1A zhN@-Wt_GqF=UiivZ>r?rJ6RK(=gQ;n#fr(;KUw-dlguYG_O>{w8h(mnk7lZ-xen~N zd{n(r6sqDGf8~!VU)@_8>ett+a2TVnQ22cIfXMC7O8v)mi({dV=dW&?&dLNJy4)4vZSg$e)ZaJ8w&xyc3h$B0#QINP4`nxt z;rrWixYh;rqsFoNk8h29my?7f6B~u`s9YS5KBjx9UJwGN2c+35)u6rkKuH5kYw$$2F4jst- zQ-_aPH)@(T_2u`ob@trhu_ljH=Hs~V|2PVz8rtyi-`jEVU+iNjqYmMUIv%;xI=K?? zb#~+h)k;)5-E%Zq8XMKcI-0nvJcIOnP4_A1%Cc$AH@I>vo-5<^`u3^#j7lqYmYIzg zOy4kyP*o<8k1rz5^u*7(zxu?@&+w2AePg|Vkc?`SHhm6dG?9kHmW|7%qfun47f+Mv z?Zi3RTyv<<%;n8Bzi#sSY0g(RfMc_b|M+ws)`rccJ%@%o-5@9?wC8??t+dkX!xE!V zPoKHiOrIz$Jt4&QMw+>{D~UG^F6FExPCP0*l1lhI(T%6-QkxTp zYFxdKm8o$|Yg(<+?v2dr&4YpUQ9X8$MUtT|%B4^rhnM(;ST6q}S_Qlu=Fea;c;lKi zVEw?z=*wj``diP|jPYX&C$fFM?kbK8y~{(3ZRP4#veVC?oL54j?dUnd+cZs1f+}NI zf2pxp7L342**w{%bCY8tiS+zT5l@@79Ge?IShP9;m!%OtPWn=?a?69G*l3Y{>Q;;2 ztvu;dFHQI>UMf;oVIRva_-RX@c*3J#cm(e|x+rT-zEnnM602Rge5XvYPq(7eJlQyu zX8trPRD>VJAy(TbfkLOF=|8xWCf|v$M!ixQ0ELp&P1jX)t9#W^G%k*uqbIHM8EnN( zhZ?Bqd#4dUbrSBhdA*MbvVi20V(sN2vMv1gd1gJun|q0p>Icg8Mv5ejE8Ep#D_gI% zLRxJdqf|27u8$ds`S)Wl9G`d_)I;*GC&&t}wv&cXlcsnU51amQe0@FcxHinkdQ*DX z%oQ9}IrrDa4l*{bscnoMQ5H?xJ;ol3vQbN(x$7s*39-48OABpj zJ+9evM9MFz?1ghFbj4}H!Ivo1sMj?$Dk}&%33*T`iX#2Av8{*Lo;5Alwsu@*l5~ViY4S)7EeX*ukZ4xB*%fjeRq7NriuldHF3W+N9rjhQcJ0F zI89qkkQE649XZ0IFxcTsTYj8=YGW%`-_hE`^`Jd3LUfG9b$+Q;(9>F>IKqRQexzKM zt;(G|Q zi3+7E+pCgL^taCU_2_%$=dU96XsY<;PbGZ`g~)A)KdRJAPnA$jD?Z9R*xg8ER^6t# zu6UfZ;Yyi-&n?wA6u-ZE)zd>p=}(~{MU5)b`{1`C!&0@TFk9ZHjzcLJYm3fx9oHT| zTYRo1wHF>!`hl!FX!KHND^5K~oXEyoj^jcoQ#7`!*QTs(UHYKMj{srSmY;ZdUf!fX za`m>Im=C!;-|xhm)|~$F4{y%lP9|;vs1+LJ#}?tH9P1eDrCXvr<%Exey1f)N3zRaZ z1_GY6u$9$mghFzH-`#Gq!Jc|ntsmpnL+KTFc=(Vh(M@*Le*0O19O<62tZ#aU?l(Sxqv9262SJW!S5Da|?`3B+h>{)@| z@UV2y=ketph2$aq@ zKhNv!rEw<%EU5%{+s4pQczyEZog7JnL$WGG_)sD`VDP7Z#!w5qGegkqSj90MF#NIlo5kAA|ZWGlB$9anJy zSZ|1gDsH*z`#99`K*PSMzuYw~`HXaRTbfa(-znDaZAT_qar$Za_45VBMg}^Q-NpJK zlkX@R71B#{zovrG@GdG&thx~j$$wMg)af7>F5ENn$VHiJITk!3hbD12d80k`LU&di zFh?jvuS1VE>0*?eDz$sI8qlhxPI}~5pT;<^P8#aVDZ+L%f8;Aygv^ZSX#`ehfkHFW zSU;xB6cG)N2J6adcry)lt9A`nb+F?Lca6rWFy-Wpa5cC%XWQ!xf;gm^J}+d@#l4qw z!iIK;6&8*ksF_`s+JG4zqIq@6&McDZsM{E29frZg4l>G6h#!^?qtex_k%7$giT3uw z`{|Vf&#wJe`^TNTvBTougF>U&LB9Q#Rpx>b)qc0mjTNzUqFRUBt9Zs_6ykHX=5rR8 zM`=;^rkIAs6(k&-r;uXJ&wA|0IW4}K$mzYpxx&!Rh!v5RcjQq*>BQ?`c^<%L@?6d` zkS!NkxJU9cv&}PxOiY~BtyJTrqPK~$h4=~y=1;3AZ;C?Jj*D%_f%_sdq>&nm9W?7I zue9-U*zKs-+QxLFmGi;NLQCfadikcER*UXWF%MnLvMqg2y!pS3dSbAKg zrmAA3$Mw!?&^xWx9XJdaOGIek8@tJ9u!QIKN>5sEvXx`>?+~7;wbi07GKRH8e?}3N zPMWmkc=W?Cjgk&p6M0&$Y$;sn>LQIqaqc^1MLtjg=TzpBHH}AM*k%C%XH*~E8y`&|diWUog;rV&; z@J0*J2REX67}YtS28KS%L8qPwV&+lD3>i=;FRcETdWzf2T?#f)N$eK(#Wkv>GQ3Ma z=p^30Y5Q@4Gd#WNgPzU7bxRiL;p|X{;xJG%R~?=Nq{u*0aK=uWX<|zcneWT&mN*!> zVtamlY;=|9CP^wH((A8_GlqNm(;!Y$`m30k_tWY7xtt)2D?QI3bN{m2bbOpM#f+O# zSl7e?ozy>KDSvLXB_&-KVM#SMj|rAD$kVKQS8->mm4vAbwKL`HYU-v9r@y6t^<=Lf zfLUm1bmaC^0^4+&E@N1>?{d6PyZKYy4X3K` zpdTchwN#VRnlYRIqtwnj8Y$PaosV9NmMWy6mZvJ6MVNW3mcRXUC?{_8t;rU+Tu`gj zZz>)UnO`}vG{32t=+L3Brv+Im)Zojb^RE6ICuc}Xs)d68Lq?)7w)<-TSNXo77x1Qo z#lba0I`&I3kz#H;OIqgy*KD&sy3(L>7Ab*Yyy16L4ti~Ar8!k=#h&Gl2cGqPWl?uq z{Nx|HbXfxRZm}=YXv<%7&mu2Q?y)9w6FoifgQs8Wgh5P2q<$bpi@09JI3_gkjyf~&jq_wd;W%?j30urAQVz17DDwn`Ara@BNykv_x6Xuf)25D!u7 z{SB*9p~!kCv0iOvI?8!WUgz$$e$pP*8hVBRS8wLE@p#tFm6m$^1xY*h-q@|@h}h-e z4JZC=1KlEAI;CktKr<6tI9@2vZF<(h`;UBHdy=ESJqj&%x|iOjlm3aw`{=30*KV>h zBjvNp8~9Pc=~?mkKA)GTd3+(^P7Oj4zNSz)u&6hB9XihHpdYpA5cmyco=~_1vLcX1 zDr{zn7jn^mtctR7-28$$E~5X)QI^cHb~3`a&@8%eou?bXKi$RYiOAQEr&N(r%eb_{ zmAL+<7vw6DS0CtiHOl6D`2rYz`U=Ft*G-r&YFAj?8BbQ&dR~|z0bGk!q`{Jw#{{VU z=CiVZ?J}-8!9Sud>OTpO>*9!7ax;E#Ecs^56p{|@kojzf3U#L^XW7ZlJUZgXAR#X@ zws(`Oai6#&5NdV*S%MDrVg?z`Ksuj_=%xb?cRSoH^XMlmsn+;|j>Q1^tK&O4?}M(q zU%Ca)?QmTyw`|SN`|L?az#@EWt$-O8n6=;-NaGDc9d}4w;e_7v<#E0R4p zXlAHbIca&@iXegJoY{qx_mA*@a`(a7t{91ehXFIMKpn)m5fbxbDN=%6!#&}WK8H@4 z4uiC+F@Z&P8fjt(LK{U{sQaP67cSgtSiy1B^Iw6c8c*OOlB|=C7TSfHt3jR4`{dO^ zk-|PPM6C^%Cq?z*6a)|GcXT0df7tI=ZxKw(+Od&)3C(D(fJ&F(ar2Vh^aPLMbTWXO z375g*%1hMnX=&^Mt?*A)2R*Kr#$<&(3txS^bs!k4_hfZFI3+%<3k$zxvYKqPwS8QP z<5UlY+3jXrZ&USX1oxQ(9#nDB{Gvi?#_8GZlQY3n0rJ}V!vzO$3lDC)kA~qFy+*aw zEYKfZ=HuM^(9E06)7C664|IqER-Ef~Szb3+z>S4!&H1$|yasS00>1`(#Q=tQItaD_ z{>mf@#<~<@9tUC3*={+kLR2w+9w$P9UK{n=`G-!r4jvT+n)_pxUh=CGYB|b8&zk%? z{_jBx38;9Hu)a)Dy`(2{D88_6z3U*OXCTMRP70j%{xU#abkg6Hy>`x(H=)sYCfE3b z=M67mre4N3OCTlLF#zu-!SyXp%eTr2*0i<_R4g+izhGHhd%tkLeicf$xJ(g~?-Xy4 z{Y?v|hC1RUp#F}-))h^?R5Q%6+-Ul3mI+y|MT&zH968F6=chCLz1N^!4l}m4*Eo-l z?hHXlp)ERU{Y~&HL z33N7D*H5gI;$E@`jcjoHO5pgsikcWF?}|39Y6z3v$XJ85CBDAR;uzS^=Z~y?5VV!c z|9r%MQS=fZPYU3S1lo123YalLNBjONw#+`&#BomtTE?pxc5Q!-?QY>27>oJaFr(XZ0z=wtYh1 zFy(|dNT7xD#0vO0c!Kp5=o_epx=3G1oHJn`0lMe#!P;Wkd$oj8oe<~)Wg{Ep)y$co z!K)d=%|5sGGh@+YF9-_shDPoENcJH?zA$?PCp)0<=$4gd>Mhl>nJx{QLYrTP7t^iX z+lY}5;Ep+dXNGi3IR;{_bV24<1Anm9&wn$?nOKBXS8S_SuGu~dD;wokLr-3?tM^** zV5co}Zck^~f_8>27NN*`Fwh;E+p;gF0`_zD7{fa=YnZdfx~p=XaT4W zTDD5>rEuLBz_%>dek1N*YU%rsXnU?qlww~x8v{F#Gkv3kp0{5c7y4BMe}TK|yGxpG zQ8{*0wF|QaMV%Z9Ys{<;W@iQjAE68g`96x^XZ?b#udL(M1KFyq;wV~;wFhp;G881T z9MYI8AL#CszzRGwZ@iITgM=T?;~al-SxSYEk-en)bO$;^ulKSlxSu}aP5lDZCgj91 zBZovtOQLdRKpqzY>pJOOul-x?jyhHjc8k)p_8(hL^!dWAg&C>pm0UZiZ#$PD=B)nf zM&%Z$08TCXp0RzmZyt!2M=L*^Gdn}G83oZa<(+c3=+3eq2@h4}r*lw6Od9@Q6oRAivO zS^V8&G8pI+coKrF*G)wdt#~a8j@>*9F=#eVib;N5{wRH1hTfeZot~hH$G2JYVze&e za7OH+M@+WK$VvEPREMNpGGp5K=5e^!Ct>iGK%)y{*)Z(>ktun3iO*LHXHzV~xgj$W zI`N{kLdG|EvYq?mj@W?WLF*Sx=CT`nozn`ZT?$ZM0o*!FiYevJ3+!S7 zWqO90=1{k|#A@<>3>5`d`s}O#a?;PZorSG@f#vurD~kT53sAGbRmJio96Xz+-1SQ` zBzCEUiiY7ec^2O0*})8a@9y>xfMTO5)b?~7Okw$G(F%vW`WiI%NG&?&$=s?WH&>)_ zhAB`hv=T0KtL2F6a&Bj{g)a)m5E1Zq%Lfy)mgmf$n;8tXHy|=PEn>taIhg265@_ufIO0vH5qGXLiOK#bq*0D_Ct*7HY$ad z;jcW#mkm~8Ud&sKR^$Q$zeQ1X2Ic+uI!!;E3K=IBGa>f zc6Qq7+Kz+5Gc*k^RL2XDYG(FMjOUwHiH_Hc=<#DyMY&Ji*$UZlvZn%!SnT6?(rt{# z?23vJZ$%c3Y(e^b$zyBpLEA;|T4Yvb=~7|Mq(wSwR!c(PoprBAzBpEQZsmN`8=+!w z!@zmO4SXjNs`|t9atDU&Im9{HXonIW*}wvP4wdaF&ba<#{kzK5qGlCmne^5(@51z! zl0uz3j?ZAH9?IUf$dHsFz4t??mh@@-)X}Up!-dDDtut8#*w$93!~H<&oSd%tv$<_G zak(05e>+Zf3}c6paUjhHqL(Hklo!)SlNQpa?7nXSuR$yg({!=ItUH*7+Gf@Fbx!}tYRWy>imNjDZcD-MF2kUD34x>?0G}ow&{w|z9f3F;1MSM%lhZ96 z4i!bxNLhLLs0{vGE(7{yg1kH^VRmIb`{{6$Yjo!;aALiZT$$V6BKocAJ{9Zrm8lZs z;N@hCwkqIf)BpiT7CGMos4roUo!B(cmmb?&YeDGh-UTn*a%HL-s&Ur|kwHKf69vdF|C_Y^A9C&gWdi&vza-NCR{L+1%zuv~|0|mOzoPAb-$rblzJ|%n{|^-Ve?#*LjkBP@ z4My?*fL#OyWcN%GEv$|&;+(x0{35cJeQGc-m2@d_L$?sy@#ZOAL?F+T+T8a1`OQCE z!Rfhi%#L@r9BiOX($CPdlR$i&#}UuZP?1ky-(TU+sKjq=Uj4JJ6XnWQe%;J?VX0E8 zwr!a@uGLWdJM!v%E|r0$$;D`+rF+)Mm(DuqfpsfA^5VDPFThxfq~(m)e}&Tr4tDc) z5gUiW;kP9o+;L#yOL?^x8ivlMRY;dSD|UPtJZFgv{>Jja~9T3ppj4OotXYHdEkCoa>I&;eYn4B zF;)`n_r$Zy$@o!sbT7&0?}n8R3xUXW5@`iqj2;n+cu#><(7arZKe2lzU`8XvzkbBE zHqKDC<=NgP({5e?*;AlxpzU$5%@4*Z<$JYMmh`!g#!!((s4=X$kKpbe&~==9z61HM zt=zu=nBjnqAKd?NuDUwlA47|tmTmX?3oG;GUYdI{N$G;BpomW*KWz{xuHDpwi(%IF zkshh$xi0-X+*r$b<$}%`XRPS*ff1JQ<#kaV=~JM0AR{UiE0VGr~BS`fS-EF^YIhR(INPM}i3-RPLQ!{G$RUvUYfN7kW_*iS!SA_d2x$?^CrNHEPs zCA%$>B_SWB!N?uxANaVWE%a84w^V;ewZEWMe+m{kc}lO64R|i|2lyso!txp>P#kO* zPbOoJh9&Y>!0~tDq3$~c9wYxjw-}%*t#OORqMN?$@cU?siB@6e`HYEFI}ADhX}x1u zu1tWcfBmqt!Y$BSJFZo5NqsF-58>oxa2itG}ovS_*7?yL&B^YT=;YU8@-uRNejOF7g} z;&Jps^#Na^&_fHfYssXQ&@CC#Tj$Ih|ha*lPBOb@l-k0VfpFfWWXBqSR z7!o2t-OReZ!4j!&;b@y}s|>fQT=K_l{SeouJqw?sC3ot%r>Z!v4(~m)s7ky@{QciQ z8D7qYV~nk8E0wv?U@!3_FD|OAO#eo+aF9;;4bfW8KJrb2=wT$!rFt=}^2xLBMiJ)s zLXz*2MQ*+`q(><=(<5v6egH=swW+^~r<*d_pm%%-D6V)v^7e1sfG7njUM!hP7gzz3 zQ6D93jLfYU^Zjx6c6$2DQKnJU(G2;Wj8Lad?;BX66GcVrRUJt|k0pCpmC%2nlnfdvb^{7BJd|uAP z)6whq;5~70vdtaj@v(zg0{81*t-&fmdFmj$0ng-vp<`ySUMCLC%p-Tm^Z!+{TDHv#y^$p*=I$#@u!E|jWAs#8QL(k;a)Yp&t*Eu7L1dfFuuG(8NV==~} zAXj6@r&nG+te~9hu0}$m6<+&hj2=z?+z2nPNU3(h$8kZUF@|82R2NZ!lO^hToe1Y~ zBmM!Q+&E|K^Wo%8Wr{H*{zI$}|d7!^A)`XOC8B*JRZ9^XCEojifYPXjAWC1Xp~wcNBEDXhPsN7(pCGg3|G?WQhFI3R8YZ8I1TnZq#U@vTV{s!%QI!FT&M3@>yH) z$7t}j1Fab4KE11%E%m?`-~>iEG7U1&=Qv)4k~XcfMIjcIBpQa75cLoAYWKG_a4;H( zQc}FS7FP~KETj^;ZGzQ628>J3+9la58$N>>5l^P#qfH-2G7;kc22p>_1uT582}2h0 zE1ylXgwARaM1OmQZ1D2j19eIe%gqyb_WK(j{biAZh*vj-_$?|xU5@TqYs%I+m8@VO z=V*uZYO;QViObpH$lwb$%$@&h*ul$rm`bvt-J7N=18F|Z26INk(GvI7E(~9Rjf0(Ak7jwzk=F+$6Z8S+p7rKdf=**j9ku{`(1~4 z*nrf&F0n%1zw3GrPSiUF4RKwM_t0%*Jc=xjM%Mk}u6Lu*Hq!HJ79}ECoZ!it!M)c8z&7?cAdl$D#!lQT3uRnz|ixh^Vu_D%j-4hI6G&; z_4fCUgOIC_^(zoW2M6{V(V_^DQZAP5H3;<|@{pm2js@zB06j3nb0gOr6K(dsXKmKrP$B>eVmiEQMCmdgn0z&{PMSN+`<4o zFZWvGu{u|&apOBUYw!f})>Z!Wbnd5Rf^oq;X{#}U87>?AFrm7(oVN`J!uSe1|I1># z3e|rShYbl^c@rpV3JE|rd^j~)UA#{>XLZ@Dv+_mXaa1B*!?rFSv?=M+fOhB}6y!(+Ey+IHm5Fgd)8@Ux_EO;hTyL}#@o zL*=pMZ8poOM@al}>VitZlX?Emrmhk7rpe>(PxI~z+p}>xOjfb(y)N%7Op79^&1wJ+ zg$JteTvnR@8xX_*r{;E?tZ|bUzom8_?;pmy-1eE-Wfuux&FLpWX2d+pjmbv~)=JOm zlXV@TjxHdL=0ZYftx{X^_z%=Yl{qu+Kc=Hd)|cSS(ON$Q(A0RVgZloJ#KJ8{oLfW~ zdC)@d_3|nsD57oWm;>f`0IumMyWl_w|J+u6bt{j${LCjaC5IOda3057>K(OuXcgA| zvUkNWhS@DDvH>uj+gM*fE5ZFC zSji;VbIjL&_xD#tHNHqw{S6R2q4GZG zU5ZJyt~AwaF>oA>uqwCQR4TdMXg{nlo6V<(s)0w~{~_jmIOPM?*qiZQDvgYIVuiv^ zue9T4t9AJ=r6Z*Q44$8rhNfUv!Y}FgUhVS1j#7EQKQHc~yHrfY3hD2;!C+1=+*_%k zTI#Q#iHJnzo5NYXO!VMeHfy%(S4(p#h!YA$InXQan@`NVFM1lszgS9r+rl(ff7?bL z^F5_e;(C`!RGS%~Bn7~$q(1tRs~sGxi7QuesZz~-!kgef^C!GX0YB)_{^!~y`E{8D zBV~R)IrTdR{foFfO{*5!Gq5f#Do%dOqaoMrRY+U%x?0j8seGImX zh{16L-(%LiAq&sX#md?*51ktgT8Yc}JM$X%YPb+J1<2-gEM4H-{BCCsv`syb4tX#U zLf_w070XkhSH8An%Nh623&U3x)jzxW_1lS3k=eM8Tc99EkZb&xcn| z&@uQz>f+=#OHevLi4rbF1^akJ&c z#0=&Occ9?71q8eX+!u7)#!=d6KI%jm++cUe-lGG11$C`7D7FrY3sbJzdk*n!`~>@l;u9~HENZ`1E`egWI4ohN$K zwEx`P2dsGOZe&EY=c*cEzxg08z9pK;{N{BLqd`wZZXd4`a20)w?@3I3V9H0NsW}JK z=Fw^wDox_}a+IIxHF)<&1%2feM6+_Sqq73GGai3QLyne{t$7V9!-L@IDy41}(E zY;FSaa5f2I@;iBdsCj=&eQ|a@$L7>I=1$Lig?*tKfedmSuS@8RHFU2QIDQb=T-R!L zvXWF<6O&91dUEUFTp@o^wte#)mNBU1CDvCza=m14&R z<6ibOV6MnitwKaF!F;P+!~jl5aq_SO_Z6GL?a@v{wkdTx%KMg{9P!4LtQ^eu$s3-- zpEteemNqShmEm-myKT`n@VELXOJcYjtZ-k4HXzw7CqbEwj)JWReb8XHcPh>C*Yd1w z1sJ>;5`xT2HOLKfoUep*i+0Ywo3`4>Q%~9z&_SDqwa|^|&Y&-zW)_8B?+f;k(W(VI zVmc+zk#FozEbveYx}C`Ml5J}3jU1ODYT|!{i2^(m`w_WxcVQA5Q!*3@v4UO4AkfO3>}Wu#a#*W8(O`UdU#l)2!VCL0~wV8 zYqtj1>?{5qN1D=u!rFgS`(@#S{*X*w<-tLzYjHlEjS$r$~8$*?}4P zR{LZ*lo;KV$vb6T;+V|K{>Sk({-P;k{v>!c6}V36yHRym1x6J%mhRO=IF*trS@u+U z0gqr)P~*ywR=1PS=gb^THOb|JN}^X`lK@Qt#EZ_UPTzLMsm8&xef?Tyj8}=$k1FQ3 zGAWG5lhZ;jgb8uvF+y8}X190%Gt%s5C>sh5XTp)0+&t3G026t6V#Hyj-YEIh?HZ1d zW{>@{6C}FUG8cHFn)RKge7|LY$kZvLl)2U3Y#{dW5DTfiulSZjU;wTv_=*WvWR_nL zi`npY!zTw->o+1R3J_eCyzXq(JYkV}8@1j)ft^*e#v=fEV>!Yb(qrA&s~@zu$;UrD zhPw8SN600{Bb%5`UcmhlZJwU%&0j~Va&>77wML)z7`^fgY99!NWcPu*eCyXH`k?mE zEg#CoYBUUvjJ3lj@Yx$hrpB*~KHvMy;e9kOv%dvfi61p(X64EmvsoYU^%gx7NFSza zY^g?AFX`_3;YqaADS3sWnQz`tDy)s9chuWY<5OAN11VYNk%mbTLuN3l zQTF>b3iv5VE^s!~m+QJT_4(`{=56lVzdK7)SLB3n;{&LnHx}%t861XfrPbtbLA+Bt zs%)2B!-WU+cW0IG{hf_p!{&6+Y1qK9dM&^A>M<_pQic|13SM=%zh_%k6(r}W`dUPc zlDOihlfjK~z12QSOM@H5&%93lNz54m&sLd>HO1rE?gBCVyFU(ZjK>owO8qF-M-6qm zpJS@5s+0B=xjw)HDZizhuW!MFXjQ<6=1RI};Wcx<@s@D9_b)mRy$Lh}5$LEq@aQ_A zR^=OdOVyazsW(bmIv7TO6MIdA*9qrx+NyWyUx3D|rpJ88&AdHjv9O~AF`78rI`X9= zzjSE+&LqVHB+*6aX2AE#QR}ik!k#$LG>%U$sM$0QE?vBjuP6|gJD?k41HDk=O9bY3 zMv@XInz~r$SESX*{dkm(e$pM0$L!A3eGO`e0J7zZ54Ptb8GchPEl6lFiiVfjbMs1N zM*M9nSrB8${*1S=r}8Q&fT7*gr}kO#jo*b5E{hB5YrkR?y9@5dVr(!L`oy$JrAnSo zt95!DW<$X(KuSCui>t8UV%DF8;t-o{|zc-RG2QcWWYwdlcM-AN`!n`vL!qr=4Zb8V20_5J>_jDiOGutfd)OlLD zW%4bl8?;=x9vjLPKjzbpq1WwMb)@X| zm8$m#US!1kgA^z!q#jQ^_r+OObx7?jz1tP`dEd8of-OFyKgsFpZ8*rxp%B^bH~&%} zN1nGX6KeNoK2&U9egR4FYvh6b>vac>4pV*v<>tanP)38I57-hoo5882%MDt<@Csmi zj{vXlr}soGO~%%v(a(HyyZQJ5k;a9uSkXGID|s@+HkmUH4{1&(WJ$mn=~(h^CrO1b zJ?w=nZCVfCsdPGqcEj19?_*5@1?a>&Ot8Xu#5lkMf*aR+aTr;0wp&=Zy{{`F3Qy2MSpPBY&N@O^; z`{!&+EaVTo{A6AH3XVCB+mo47io?3FpjFYz31L9_o86*Lj3d+21^*x+)#!V zOUBY>*+I4JWE|S*nmk=L&NALC>)^LxMbpYYQ^jb`=36~baEO%}kX_o*swOR32F=hcejaZ)w<=IKGkl<{* zugtr-y5h!_XmWcWi}djmFa8w`G3nC&Ff9}I*lAM-e~CJ5UxQ0}iq9L3}h>U<8(rz>~9ru#IM} zv_c@462srf@zJC@?yew#^-fELL#P^aZvYO(*~3n55EDp6ciJB^^qn;I4sf|Y-LeCU z(#kWID-ihJNQ9B`4&!FIsyvsa#A4aa{{y*#6-et~)`hR__d=EeAGxC(IS`IHl7(fQen%RQIsd^P@Rc@!2XNQGdr%mt z6Wm|7dZoj3Ns;X&mop5;UXMa~i$vcs;11Mx@-81|`ga)esA%s1y;1M>1_Gc-Y1+DP z1E=``)MljS;l&AAAB0E$a6V$UgJ>v3-*TWynLeU^^d7WHvMo|cDTIC2v>c4;?y@^Q z@|vcRzg*Hy4gvXeK`V%QEYx|Xz(c)er8iqcZ8Bb)Fnm3_p-r~TvOr^6{Q4LDC%H>2 zx$!!n+a>@_%hf7(=R4rhQ>$^XFnq)ri_E37-2#%<;K72C2;kpHw9fdU>ax@upzrO? z9(j^Rby!DK#~w=63!=97Sy7g^A~ppI+V@0f=7b0CsQ~6dYV~a#G=1La+%9Rh_2zF3 zwGdU4AWs&0t;d$lQ3v%G0v0$e8gB?&6$G%;7h7oOky5jTVl?(l)JeK^TvsuSqp4dA z7Xa}SMbfFHUc)iSx$~iSR$M(yRLD3gDmmd$Ip-WSP*Uz2uA75wkLl2Cf7JbJc@@fw z>>ECT-LfbB-BiZY=DsBZ0^@&4H_s!A+AWD6OF_IW`SsKKbotcVr3-xHw7?ambtDh% z=vC=G9A~NZqr;w^ZV*B#dQU|`igNo>){LK>Ry{G=OwR75IjO)>rshf$cz2AYqAwn) za)Yt^fl;B&iHK20EZxeH)E2}mX3_g6E>`MMZm-x?M*e+X!Uw66{Gn8im*w-$1-hcQ z3o&wmNSMBj>vC0JRN_(wY)n*H@C`r*!#5 zlI7}*pRjp~S+g?;W7UKD?4MB3cr{!LmRJ04?Es(P*R3)! z_g+{P(YA{|y#|{vFY1?u!xO?v)(d~o81e@(>V-!qsPP_|RlfWnwVm=~h4qFM@CYxY zyU^B8cuZBpSDI~HPUL0ASE2MraAAk!&OIJ3JhO1|xCbmWWbEc@*T@T1RiCDH$uciX zOhec^eyEz`;6#|Wpi;+FgPK5((`r+tXad;4DgpM&8p&xNeRLUDthX{5#7Tk!-AgO% zEYiK^TWoG-t2t^iRtHHnHV(lI_5{tYf!Fr~90O+W&2n;7W%nY;xmh2;lhfYKZQRH* z(sHA{(S0CDV*$zsE%|ga_W3BHm04!v8F`~7Txaij*)2c%>z&0`(VxTja@>Wq`-M5m zY4Kf_-VP*;Gy^N;QFvQHL9vU~xbuv?O&dF|H-w4vbFL4_)r57ZsCi$M>y-`poE)MW z)!BXIR}1-UB@@LmIF>Ia*lsNSy!mVp3GJ83(Q!49+hr5{Xx1V#DD*OX3~L)rZ`=UX zpW6;eM5Lpam5Z{qPD^G(yPT50$2QZm@BXW)K%L9<)dVBXd=+2!Hev3GHiQDP&urF|75kaPu5F(4r~ zcV!4JyA3(T?~ie+G0v=^gUrG*E@lMq6sMNivkH_l3PrG+HNBzgK|EgXY+q#Ie2fT!%EfZQ363s0huRf_3UyyY3mwi%@XK5>Lsy{OR+p!No@ ztqEU%?U!kFtC*Y5wp;KS{u#v+u*f)tT&1kW?`zS{LT<{jXlMETh2wg&MS(OMu`xaq zp3%|{CLDIa*D3mz^9!$-ma@b1hxfv%wj*9WPA5@g>_@sTpHQi(sT4>b*c+C^*x4sD zbAgJr1n$h@ng!|uGWAIKU}fw8%T)?=$-KvckiCT7PZ=>$?|67!z!qw~l86zr?@NN~ zU_DgdCj(N03(`~uQo(uTr5da1qf=E9ev2kE8+4G#;g8oT>+R#o*}+Q-jMFU(EmA>(Q`ML& zzVDEGAN@Z#yXY$K70-t59Vq2PWJY$zgMb7aOFt>mFTKdsno-dApxNw6|8lhD0zKS!l>tk;;G77og^-R9u$eC6n8LqODi zRldUc;5`nZ2%5}KAQLFQ6iWB+f*iwXI-`67yea20Nvve|WzIZWVMdi-7|Q#!#*}+I z2Iw34d(&V`3&`_x$$f7F5w?F4{30+i0ItLf2&Q_2X{?;{HTpvoXW#`YdqZJ>2m&Uu zp5M-VXw}}+MU~~xpZP)~-fEcaRy>r=>G^-tSK&h`s_%ySXAMlrO@zRGO}yLa+vBlIXj+Z~o7~j=IemN!`xdqW|7bPF#j+D_ zwv1=STS+W;R3O&XpR(~lT*7>sbpzKBFLcy`m5|HFpoIRhRXOZiurso=0~TL9*=AwV z8z6}CpY9OC1Icf^vO`HW?8hTx6Dc%|OvF!D*UcvQj6kO_>8OuU_GW;3uMmYS5xIPb z=|{0=Z;G=aMr-wmFIy1Q*kw+;&qB=KN?WG29mPx)BSQ6D5CX!G|PO&+4-LV|^&PRuiZQGW`^OtD?A zBC6esRlqu;5N_%foC!~px~JuZPKNLCZ0Lvd!qoVL3A0B2itGwc2{i;MkV{_l*2h}c zhg82|?2k;dxlX07*ZkY-tc6Ddr!5q9iFUYnSWxLl%GaPrpZXvC;2GdqyPDo0IPsDI zf$K0)&n050Rzy_1mZ$iKi{s)K0C!w_Z$*iTx-XH`Z};ifc}ru~KSm12S~yRO#gDFQcTj6A}G8crUNkPbsG7!(Kgd;zh1JNX*m}RpKuV^_uqG9#;;8 zh~hj?W>pKl^~9?Q)s@ylNoC!=>%XJYW4hd3y)X!AR?QO%1wq(xWNFph_?q^U&Q=EB z8m|tJbTARU`kPwq&z%?yjZdJ&RnX;ipuxV>3sIPSiFQ#TYR44W>nNg$i%jmu({1EO z#xK%Q@erAosMf!&wn;mq3L+3tBF`zCvAR1cUOuvKwU>OL)QRqjSmF?>dL7M%rBK`( z9WND%nJ&7=j zrG~kUmM_*D5A*@cH~uBwnEJeKbrba zXYS;_n-vvwKXJd~c@l+QbR^T>C%@sqyo=PBGL)nR4v8TW^;8QxU0_Ir-**atGY4dy zdltTu5Y(eY^Ehemp8+ZRnXdb4Gs~Z5dVfvcT!fDJ{!_CPW{;bF=_&)nKck-=8F#9s z3|>Kf0}GmzIp3~S9L6B)K79esP)Gni+bbMUID0Baw(a7ol443kMK|BbP{OCRk^uU$95Vl`c~uJzVJSqf1JcQS=+{g#r?m_8tqb z=CoU?PldO4H@-zH`r80Xi#Gdc9VTt!9beo6f#bmqk5$=gSG7ijd2Ngtln zGk*3|HQ)03+C)-(@ZasxuN}aWJi5vuoa9OX?X@1ZY2_wsPm&L!L-wu>(FrLUieZ9s z*d|2~vvP40^G5?^Yv%MUTyUYSwsE;D7sIyX zw0-x5Sc9L)% zewZvrBumME!g?(4oienjg(xynPtz~ywtI!_$UYY?)Dg~j^M8mMsUp%~ zbswXKif}isal0$k17kYFt3MsWWz?4hjhy>&{sb2|19!q)VqQ-k--Or~xvF?|o3EN3 zWTr`7nmy(g32P>|Dlr{gSG4Riy7lT+N$$3N$LvM$hAY+j__*#d%+&M8+EfFe2FxIV zRjv3842W0bin)K{qDBR2HftF0$);UqZ0Aj>bL6Q|auDO%)EAAeQ8VN#ZeMzUKuv}N zhLu#056XZT(^aXbp3x(Veqo?Kw?TCvXJfbqj0H+{%Gs1;v$8?@gvCFts%f z_2+n_4K3mn&Kg!6Zi7`Qy)SbkxfIRn`&Q;AUjM!JRKf2>)vPf5#hHrwdi|_?SA<9_ zwFd~DZc@%6K?`rn(#+1Hs|%IN-8qc5O-sd(_>i5;%H*f6B{(p6R5|79t1-Pppt{cD zI-P71G`GkvNojd!8!^LsWGXKg8E^j47ML&G_Vo3;S_IH6Y{8jTRer*%e4;@Omejrz z)t0QqxVL00Ij4@@=Kk{mS;vJp_l_9F%9KufeU`aAVfmM+U3TpdVMhuwqpmdUVgzh; zRsG@`VLEJ9=4pAgd4w;r0S?EcTh_$H?i15)I)_xm?qA(Yg8g*4nGa^m5x()_HgCU5 zajyO%`~187hpoy)uP^(}E)gSRHGZ;1v;y69i5`dG1{_IfcRkv9n*Pi(QZu`aD}*%)NBRVr(mY~g_Lj4yg&LnQX<21 zT)?@K=p`6?_d+lgLWpff5*$V4bKr8GvGDKf!cN$$+TxH!?XX(Y%6@HJIUa_+?%IV? z-6J-XUO0oH9~UE&S;iqp<^}DMR}OhHJ8ZN-39i!LhfeSj7U$A4nmPc@17(RY4z{g7 zW|wa(vzWKSw=M|u7tB7ql9?t7+^KPIknaqm=_SEEeB+Vf7ANz6^!8P8Z9d)BDWwI1 zmIf$JXz@S`1cyRHf#Ob!7nfow1eX>F?(P(KcP}mO?oKK0?kDYg0wH|FQWa3QLfPnIjQfHeS1Z>nETW+S|>N2obmCVvbfA`yhV+dCZ8x;6s z+WUL(58@TO&Cgn`Taa?qS)y5E3p?N}b$^+*OQxbE%nWV2S^r*^Ozh0%dT|>rJ6db0 z$V5kJ&&l}KH%ubhjyoRf6zZ8#(d|4r#a-9#P6Sa!i3i)?TUAz$>#^|Pz7;@!P0su%ed6NR^Gf`86*0wSlzO9V<|W6w zbZ>~8`5Q+}@$8LXFpl`2LBGDXD9MjMq&|MSO-)Heh>yn|Zvu5!aQ|Dk+)PB&^P77B zeP>xrP9K(%GLGWB?jD{t{`ZuTr`ToN zwMYcVup&yonTpYe2}mesGjou|@SQ@xJ{K&WjuHn-KP&nUO@h9{VC_nV&sr)P3LduV zv6v+LweAT+`Bg^@clcz%q-=d~F>sg~nA=VD_-&r~gjsa8KUTSF zuK_OR#}1=WS8{MgoOoQHlU$gj?b8Z{S^sVJWwO}b$L1ORRkjAP^a42A`?S?aqI#X- zzOxhw<>OkP)|d>rfD45M#;IXZL>PMr{;f7FE_lHcnSzPzdTQ#HInw%5uVtw$AB3*4kC!>GqfMBlmMfBG@IbTy z*$5It8j{63b0}R;$uS3v^@V51Ci>Sd?;LnDf>_5`ei{>O@rC|L8(#?!8lrSb*8It% zqWK#m2BF~uB)dI$KVU7PC7lS7=RLG=Xy(3vCy1Q_lCYWM;3*^8$dM4Iziwu%*PaFX z(5v>wdM<&>{F80f0N@z4J>nYvkdPBE`n-&QNXN9r%X$e|Qjkkc^A$@o4@q{o9(W~X zH9L~V(|c*5PSZVIV$PQ_`m}O6_TD5kgImS&;$ujGl+k6vq~1@l*47%t%|xk5V24mT z-A<@i;H~|O)+taJe4ya15*PP?SIY`z+;i9tA zyR#>3!t5m9WR)Qin8$=RVei?JViLy0%#KlGG52j%ss+>xx!W2G6F(t{(9T)C zkTj87AP+a<3B~nln;l~Vz7QCgMay?ak}mInSo0_Qn<>`RO@r2f#pxQ;6h-fm*Fys& z6QY$A&Byv1O^+dt7ixt)15EkJJX#k#5tBLU zPqjjayMkV2&{e_15lx2uJB|mBvnF%NP44;4g^%kPKG^arov|{^+w-n!~KV6 zqQ`)TU(-A9-xOv1+?R8lIMN)CKXc-EyYgBr&7xICbjoyiKbbXML~WiNyM*BZFo0FN zHiMW>ha1nV!CZ^6iNqKjf~8Ih`GH|eNlahN@D%?upQhcMpOWT#CcZAkF~UvzIvIt? zNK7}`^L>6a7M@Y**WobpxwwjzxA}yz6=fp#cKOoVeF&i%`5&;u=V>OACckc%$~UEg zJQwI(Z9hn)rb$WpO;INlxDpdx%ahQLgXqYdxTJ~*kY94wg3Qa9#%v8E&8bmu0ssnm z!76vL5;w82H*K2merAxN*b=Bp%H9FkTAYk_!nyD2_I7wtmO-!F#h|k&^(|Mlrvj&2 z@(Lq`!KUdB6A>D`XvndrX;~eM!#6D8b^Y8?zg2(`LQ}CXbGe*SD%_}R=`-zFj9&R> zRlJVN5P3DuXF*fQc*r|3-vW$)i%QKiW)+g}tPCbvkYcr;>E5t0vK%HVLNVkBNNpA$ z)W38<(Db-uYu7ZyLTHRD9;XeFqR>^A0}#OHFJ>c46VaNsr9^y%aqpQ3*WU&|i~{x0f2=o`mWFteZx%Do|aP zlO*E6?h4)P(7$(jx=7eUD80`*7#l-(Ghk(nTlW!hIy%-PTeRlfG#y!Gt~@nALNFWT zuFkhK+*y;jZ|zwM zOpc=bf3DZeaz|4>0_odFkQ$oDyn7_5S$)tAAyixPNn9q6HOcziCmIytLP<-lFv+!MN&}P6PKlwNY zLfX@$X80!|PK7>J7KS%|%jQR{{Ka~r;@zZinc&P*nOiWRYAm__(GtqBhP$2;9TSBT?+7e0hY-ml`# zBc0zlQ(9I`37U#CbU3e`B=KZqU)JH3n^* zU~4-K2rp!6w4(#1k7*iB$bSDAYv4;b5V8FN_e}<#5Z{)l6&W^z4pwv$UxOltWHYgH zObm948G*!weO0{H=Xw?XQ$9QfQRhq7bA}eM*wPHY@3_@6OO$c#)QHE8FGE+?8&lg0 zI^&_&1cSy>qC1tfQ(d64;qz#$@=~*=;U=zS@#Pn8^OVA$;E}H-kT-cHLw7bGUJ}bc zBMLyN+e~R1p>$qRTY(xQG@mcLJ(k*?1)&*aR?Leh6W|V2r9otHI5pX8$VwLszxt&L zofsyYb+GW8t4qf*GMz0ovnk_d4DnrW-Y9iRl^{~Gb)U8Q4e@;Dz$>=8IMu$^L~jhi zZ|gDQyNNNq{Kafa1-CLHbnX@Q>lo?~l~ymMFRT_#^^x?;D&&bKcg8Y0xc*h9dE$Z_ z@jPSM3=8SQ9m|xp<~%>5lk)Knv%2!}z8sF}0rA z5>V3}#us0=E_3OpkmQ|8L6m>vtb0k47g0>?-jc0&AppGKFOXIy0+Mc(xolxZkT7q(yv;OW+`5tsM;NNy#(4YVSP=ZifZsU2@gMYSX%8YDn3#u<4XgsX8&ARu*pxT|zDLLLaD|;DVXu<4IUxd+(oDWv0HhmffAX>uo$EIskS&<&~ZYxy?Oc!8-nEG}+9 zEQW!&vD~>B?_8!RYfTAzavw^vUH-u@v9C|EZV-pPPXbvPwxz}xg%@hrsALo$BR`9V zBeVI0?WfE}=~d74k2jy4S1jP;wfa`uzoxGGKAOCK6#X(~utb+-?8wV2mFnh3`hEzH z!~a238>!w3vX=2Kg~Hogo7iZ~82q!VLoL|fV?;U^OYurHi#@ka(KL{-C?(Ir`Se%cgE30!mbrTI76gbd=URC%1ZTbB-D zLarO4y#%A%Y3&yD;Z!~ZL?|wl$0Sa9=ZS!b5m)r>)u%SBQm>{bz&CV9so&TYHgqMx z*Mc=q|N7-vUQUq(T?hwRcx!C=6_qRPK=Zb8s@5uRs~=oNvGD(;&T_s*QxOW>K=a-~ z?06_)kPfU+$|cs3VE;FTR9-i&vr_Pj_fBQ%4oePO@nFa6LCZ^wl$XML`1QA6AESg9 z{vOBhtVX9%h1e(0h@~VJuB=NHI7Hs6Y)s^Ai(=UP7tQpp9;LzT_(}i%4t0AX z@D@3y_(U;HB0Y?#X4Cp~G;yaZh+^6@YA$>K?9Bt95G$~$5r7)o(4Yu-yUGh9C(>!| zur3fQ$ylkq#XBMFfm^g3{eoPh%gP^C;0JTy@AwyC6{Vrp`@U;Ek94TXT6>tf%zlr& z1Gd8AeOIO_flOGKAC3Sqo-{Igqlj^sSL|5jf?hZ*8yV@a7b$Pfve4DZTuS)3eV;lt zjMCxbIY3YT$3ToAol*5~L#un*f`(&RI}`7D{r0}_d-W}8EgTqjs~_e5aW6_yf`P6} zZJA4v^L7bGozL$!kTG-hLs2VEc_PM9W7Fgu?zf=|^RF5xa_vc^g6ZX1wj%06KVwf@ z*Em)`rl1T_`;Z>=qps24)oJ3D*G{T<8Y;ESh<+P~9-pO=W=P7@(39{(-@yX%-u>a( z{m9+s%PzB-nV0aW!r__F{ku$D%cI9-_hy);ny7*!&Hu1P=oW2d_nc%kbt_4r{uNdK9MlNPf~*`?*j z`;z@Z5Bqtg4lCD}WSQp%WegDtQGutd^uLA88eZ1evPZHma-LD|Q~wnIrB`Rk&y>e` z#`a0q=&W+7xg?L5?mNL=O(;oCsA}N6517mMi6S@dA1FMIZX#Z)+9Gu{6J^!B3igbQ zS(FUk)IH1fw&xsAuhFR6dSgdJSq4OIknKzPhlZ%%aNq+_`e%9~+x6WWe3U}p{rzkM zd3IYc0Pu6TkFbCy?eqGw&HcSKcQ?JE{}A76!e!SK`~3O6B%U7#T^`+jdc_5u^!N1Z z?y{wT;(-28=k9rTsBafV!qv>G{_yJRmVsvMP|NI4vo8meH>iGfG`oRNW5DDXalL9? zrxlYXbXa}mIxQ5FK{_ZLoak|PW8_zGa5U*k$>y$}xSLZN*H3cTYoynt6}xL>n095p zZNF{Gt3yS~(~`KD!VKM}I^rH5KLNAA+@z$4ME7U=<*j}gAKy{wMutKfigNBG!I?vK z@2SRJ?vJ-H7Pyf;Vy8~iY^ciLs(0(B+Mj$@E1s|ZPUUF1OnHAhBIp5WIYIWVv{tLz z4)!toPQb}CoPrCcl37FZqUd&<-6Y6xnw zfn(>P`uWeZlrWi~M-R%Km2;D-dJ%4O**fX5haPuM6PicXXTgid#H*|I+ z*{UcAGEnTf<5v%%yH8IXL?htG_=ERciR;}(ZO11wz5bs!R&Q&b)w8zLoaXJQH#=m1 z1;K#xmu91?xLiCT*iMe>5Mm>j;Qn6MGCvG>P$&UMsvuxgV3=AqC42B@XVCRnPZWHrpe1^Nb zt1!~~z_hLBJoA>E5r;QjwU_HCXPVn=K9VR9)GjZ8%*=DWf`&2#u4&?RHe7GUc{sYK z!mb^DB^a6&gpL-G?P4^m9NS8(<}?U5?ka0oEz`uK2Cr=s*lrw4JBU#e708o^HNEF< zd+X?KICo?F+kxkK{Q&mNB2P|3b&D`T!UDj^A$G0yy!2d<^LX^~))N1ywZe>^UnGW3 zV(nsNWa9_JP7{&U@Ah!myENN-p4mAo=V+tb=QWX?UccvUW8W9t#Xs4sy^QVK?<}b- z&c;W~U*pp0rt7wyr$!s!gy?$VA(nU-(<;E(a%GCi1rKX7DyzjcPlcsk{K5|X(?LG=y9Le_5 zPJ5jr`iyFNAd5h0BSkq^SRsZ%K4)&Wofk^Qar`pbV-3_ZD*f<$^4Qge-BGytp1MyM zdLO7{I)1OVgEilgqRVSXFa5)m{vj4H|1Z7t4^#T5Soj+d|7!m2vHCA75d2vd|M#(469dCn z@>^ws%3%|Efi$?8GLe8O`LwNlCZ!EY|`SXeu+0PLCx6h#0?n(yS4Cc_)oS{8xRbany z1%e76^Px!oA`!o(5Wm6ujZ;-X*v23c*?!xm-TPb|fX%xG7U*3aOch5+w7`i|FvR}w z15Tkfu&GI?pG;O9>{bAN$RH6Wz4LLgo-QlFm4md^~ z*Iw2Y;6@v}Q?eq9;Cu+Rg?wRQ2m|Q{X^@`wC%fZznmo8@BtFF;%$5PKo4B3Zj#5!k zW%Jn E4+lh#=>Px# literal 0 HcmV?d00001 diff --git a/static/img/overview_flow_en.png b/static/img/overview_flow_en.png new file mode 100644 index 0000000000000000000000000000000000000000..be6a887f55da4cb780397a0d4a15dd65aefc012d GIT binary patch literal 29394 zcmeFZby!@_mNpuJgdm{_!GbmJ?!nT*UDG%Om&V=sr3uh@aEIXT?n!9e-66QU2fIyj z&Ueo|=gggN=9&5K{p0@8-MjZLS@rH!tEyJLs{$3}B`{Fmpgwx^2t!IzRQb^(RN$jW zPw`QnBJTL~$dNvJ^w?TaR#oh7W%>U8e(`MW@bqRxM^iE05RqWs+hfeg;AU=SBQ520 za&peo{q*~{^X{&(_35pzX@_++OQFFhKfWEl#j`Jgl2XvkxH_L+ooS>()Ke@b59jyG zORq+TC#@{(ilMIV-rkN*GtLgHvC)hEpKq6!cJg!f`0$XQKDk<4Kt+1G5+2&P(7wup zDWgoYI2;}iBRNlr{GJ*Sxe zC30O>mZhDGn3O_JOf)SeMI7ZzHF-IATT5$0gYclxl+ciD8qCFp@~*xyKNsh<#W6iY zi_)rw==iUT9qkgr!t;sIGyc$WOC9FqLrDa_j~tcdRk-VI`yM^A!)}s*ECjLeM4qjQu^n$j3H)&5gV3!bnx+ z7|KRLCoTKdgOeh^%$nZFv;5Nn|5EyUyoYN3dheI^@cRfL`tbb@@c7{eg!J%@g!S0MXx#`dANjy}3u0`H{*VFqC-2{?c_{Nw zUh+Q`{%<7tS0esR;{SqR|0Fx2VGl6;7X=i0Hc z`UJIs8uhL3!%Kbc-?+c>xzxkI+Jf?o0q-{H`E2lELsZ7s zS}ai{te@XlZ+3Dlp0ijTjZDSaU{R^1Aiu5(zef2BSP~7?^{_qJ9L{86PJIV>Nc~Y^ zd&ghjF>*pc450O?!|nR07y&fY^()ks-?s8E!bt!k5eNxCpxPQtfo^ICb%l3|03HM2 zKTww2Yd%_PApX`Nx7W1*=$PEH0eD4q;)C z$|a$fKq3#?szZ2tp$%!P93IDmCWKgQpv__?eHUBCA3+HE2+3|7iS4jLtAg*yPYCHK zOQFO!WsX9BiR+%&O)jF=fm;tvrWa*-56gIUf1ukAO4~x_a^4@ea zOszY!Q@Z#!KVD>nH+!8RfVZV31SAo}C?gGXdCo|+eWbCp*N!8cI~)Yhs^)+$&?(}7 zjl3q6H!X8+6RxKNRn06}Cdktohh3VHdwp*$8WU2#g3`*cN6HA#^t!Xs%vu&cPo|S2 z88-PaL2~xs5`>QZxIqtvhWvA#Tt-*+w>1KhCI9>j`D6yzP#!RY5VU7t^v~*@jr0 ze*t4v0X7!YakkIb>aLF&$dT4hPgfn5C_jO*{S3g5V0XF{2Ikn&gNxAf}yS$hq za@_7D3xia`=_m3g$V$CS6mqP*H7nD56f#kv{*$VHs?I1NBwiw{f7HB@}w^AEtx}tL?2Q}ry zk;(4WSa|U)+h@!-#Z33tI>?T)9BG2RW5E3p*<#DC+KGopnctHSe3OnsflppTl?FK8 z12WIsY+D>CZJebqpEMFO!4hiukNPEDwHVmA)a1TR1ywppG)x4!M{rzkZnqb{N$S6# zC4BB6UD(5{+-L%hHb6rjhGyx** z0uIZ?5SD1%en~*Mko%74ScEq_-JZCv`4K2BWc+BRzZTQFg9nPDh?J~!0y{xwvt?1K zTJh_y78GkvEWyTD-+{IA%YSvm=#-Gl*c2Nu`(RO%@9CkKx+L}Z^)C}TnL5{U#<#~O zIHfojAckxFumVnxJ3RdHF{R6El|t8H85ugcUAqEKJINZu$xQ1@`9tfrtN;Q4{1`Q3 zq|5mKy=x>}X*;7aT zfz5MgPyMW8H)mw_<{iNvq9edLr1Y<{iTnA?jCmH`elvc7CHmJZR0EFvJB-lcxJ}iz zpv9q{*Xcv@4PP?BBDs+kOq?01^JUHKo)DJ1x7q3nbss6J!)xp%zk7(}Ru<;}sK{>L z3sR;Y=N>e3u9fIRnA7F#^D%36zP;swQ}!)U8UwYr-tUthd9i~VlFE$)71txRWs#1Q z_AezgblHQi#B>S1t7*%r9z?8XA1A3mT`8^<$Am7)y?2zHXiBzMXgRXmQ@ZiuqGX9c zNVH)-r5t9V=4vqo9B1;{-<3=Qlh2+Er^2eOGpitL)xd(s zE<5WQ_|4F^=P*tY>S&H6Skg2;M)X8Yz~bAZWmgNPb;vgQTGYr>g&bUnyyhqg+@~%( zeof2HBtChow>+Y`i?U~;XQ*}|wlu7>*oVTI;|j{L-PNu|0P+MbdTY(U-Tqp-7B(dc zc>EIiEFVe9N*XQkNq?35+4(C`v1v|F^a*JB0tv5}z$TPxtiNh+>l)zwsc98kYYeHJ zta<3e#`n$a_LJO9()_Jg%m~4B0bXLBwjYw3JlGQhv1}PSn_g)&ys7<>BB{!{Tqh8D z^9)|gc_EF&rqU6rzDm+n+Ke@5SO3<8CuX`U=*2eFRoPikZ?2Hjc;aHV(rE>pDS zES)|Ohi)&5xyhQWO++7#sDwl5<*aYNiK_kacmL?-hK2NbqGrr6c3ZaXA#QdkK9A%6 zhs2Boau0IizTZ;$%1*Lt{nX0WX0{2p&sK~r5xf1yQ@us|3rSyYOj#y1xepI6OE9@w z7+h|ZI&H{v#=^@d4^Y_G(Oey=U2c9G2z&yBWg-tt4kD%M0?w#qbO41334>14-zH(j zr4eqqxqH5M$j)xP~WMuGu|X!Dhv-hWcR-G#2T?rU$T* zXM#<#8#hV%3#6h{pI&P9z7jq2!b9k@h#%L7#3W=C0~>k}`mcDuMXnY7FpoVE76uqX zRm@~5VK@vE%2C;>k3iawLD6m**+<>X>#i}4Aq?HIZFt?gFN-~RbwgMXb%aAeebHFl z5Fym(xC@dsA=CYk89aeEG%y;YUzXKX*!9rMiE0kDYV8@ssI8x#J;0wJ2`~<@Y)HSJ)&_}xMZ2)xDkEE8YDnA+M4Xi z-)Lr^${-f%rWSR|+Ud}2{#mZMxFjv^72GoHJ4D}bd2eilS*>$7XJWvGT8LH-FxXXl zprKhKa)*l;hV#r6D#vt1Qbv@zrKjM~%`D{d95=)Fg2^KzG95VU3Uu;l8JQ&F;9qZ) zgD>diti;gOzJ$?qA!eLwy}qt&#+Ihye3}z6%+$gAI;#FuE%Azg(t_S`DyS<*CAnTC zc*6qd*!dxyxH74BjHNDnW0|kO`(U`P3WEJ2L$(kx6(XYlA=N64oxvjxn4fdH%6TPP zSTOKe-l#;Qzg4mS-Osn(LT|S{H=4Xy$5~jIJ3DXIgl~IcpKxkqv-jtNF%A zM$1n{jSPqFZ%KbrNvY3U#cawewrUChE~rM!N=4rSYeVvOKW43Bj9+!^u0LHvi}vIy z0Mgf2*{m^@CMRUsFBhv?+agBtLqOP%+35RSF*TVI;I=bQ1kgltBQeN`KCptYr@)my z^xe8>B*!!*}th1~3mfI`x^boE{g8uOI&6c)W6j>@CM!4@6K!I>^6pOuvl zk=3F|R?F0`+5;(NE~^ElWzMfQi=(7>sF+}n0uHEwbBuM8+bv@W)l>U3<~kO68tBcx zTtSvJz42LVg$;Dv{5{GZ=^g~bjq{f(`lOYK^&McQar>B1Ib*zN7S<6`G#;MHY&Yu? zpr%}#Oi&Io762ZJx*W=D9-QoIC$0g%yi5qDVQJwd3B~Tif|q?)Oc}aK6$s$JJsZ9vh-?XY%B0dk%Ak*Lt=hI(=(>v-t;J>9Niq z7?_-fmNO+-QU$IyaSag$hC+}JREv{+ji$K>b>?-@Q6R%EvD&ydZ1&EY92Q4jN68?J zt=X@E^h}CD*m!1lh9|{VkY|+&Ko^qD^QtqaP`AEYwX%?w$xWvtIrD3ICgOHbd4@~5a%zveBBk+RO)(_xukC(zHE zF<*^dUH2(I6{ayQWQJLD`3ycn;O@k>C{(bK6`SL74el5|UWd%7RuHcfEDx7M^YUhn z@PdG;brDk8dE0pv)G`Cs;wKUr^6N<&!XfJ%G+}m|hg84C7L)ES;VP**rNrUUmCBY%wM0znQl1~pwBwPpoF+v7NU3veTXbLf^g0il2HCtB zU9x{&kRaAY%R}WZWDy(B=GUSfyZdPMurU(Qo*_edQ1eI+UnM`tReCW=DCGjH%uALeGZ<{`k$+HC04siWIC1atpg_m zMco9I3Up-9@~4PGWM3c1YxfuMK%0ja^w5H2op#+zrULv6JtuID-H(nOSJlj*>g8~? z*{zWu6!s@!v@w`xDv5I{7gRsMYcbxpYS4G^2bpL}M2!s>ow+rPz^x6EAyWyWbKmWg5=WlME z|Gejk7nAh%T2Jo!7BDOPR#)L>=H|i!Ww{D(C?eIfe!p$U*KIH6s|6K1jKm;daz$@4 zh&ZH>8g@|p!S7U5!t}&_X7Zpl04OitycNfEue5gdy?NhRn8~qS+6W*Be_2Z7YK43C z$+#WqNSGn!StQR1BwcC#0swz;Rkfqo&vfAQ$>vz}*|5m&q5F?XXFEtm%q#fCLU&CA~nEg4Bos7-F^wJd!F`+ zrZ<;F$O{jRJd=^fgo8v+WcSgAcRTVCi-D!mnaSBA3i<0r%%~%yN0p+OR!)xs-UAN6 zTFi0RQ2GISXkCCUkj82RtmUdJS?|gO znx2Fa0oi+?YXE*lz*}itA|yNE!?c!ym!E2Y0QiwfNX@;iB77``fA@e*XArOI**KR=coFavl#qedx5$Ut#;}-w=X6FB^WDMTL<(*eL-Z zkude0AMi=7E6^P=tR15*x1zmOAOdNFKviKxeDBNbf@vPK?>RsetPDap@%9jZqq9|- zcJ=>AKSmB7Ix0gUHzF}2?wkez@_RmYJu-NHPWq6%7pXG-Z#Mv<69&(a?1FmzKdAgp z0T4A|qj<1}kG?JIGnxD`nvWhyKmHfa=OW#+c8i90rwGf5vJ7fRg0PA3eJtnvE%H-5 z`2NSnq)I1`A)TZ6k5n?T!U$FVW}D-rpK;Ncvqoyht=`5dOr7*4&_d1 z0(a(wAS0%AH?W+}^?oC0AgXhR<|%zv#o}^qXh=0rX@#j**@=6=^-X$k>Xw1&;c9I% zZb)^SUa_6YW6<0|jB1S@<~4!HuFzGBuxInKKMW{;irwF|{(Z(pt;CbB>vAdlmgVjT zAUE?A!dBWm-FyN1RfH+@R`3Q(M8I*FFS=K^8YA#Lt-C+FLemQ763tyXsGp_-YqJJxj1+=rgI|j-1-R^^yR@oyRs@~5 z73&S0wO$rBfq(Xo?&ylIhlT8aYfaO;r!2R%>aBOf zD4#Iz828n}Fe8JtzZM<6KQ&oL8fda8I0(4q&y}CwI-p+xG|paJYvFMZr0t|Jmy*pm zoHoQo)>9Y)_cwCoy++gyHs|-^ZXM!ynA4JSF^>Kk@H74#GYOS(g1`qz!f_Q^0JRxt zZ7nmsyTUf7r8Y=MoE}U+`mB-csF3XLM6g}DO|^P&&tuF3T;wm(bj}UG^T0`6P`#Porjs() zT!@UptPD&UL29w2lz272gp0SlHz%3WPF?i+J$(|jRKvzRB+OJ&aVHFz5o_U%<>5P> zw39*q@V4%3-*6Jlv=qEq`P(w5^Vg7<3+pY+wZcI(2VtV95K%rGv82>&{- zQXgj5E`UF6C#uyw?`!W(n`Tx4kNclV05J+@3Wsadnd(qyuMv;VbZodAh2h2mzg`-u z!FL|fEBx*2rTShF;|+Uqb8N!8HY~I3csns?c+1T~7N3tN++Qu9>3mvs3`SkfQS*3Z zZCYPd-x4F6Ea)g;k_T!MT6}lGp4YQTKZ%25=zVPfMGBg=yGA z<~{Gj*x=s4znm^-IbFAZy>yx~?LUIOGWjV7&&FC-b%_Gjo0wH0DMI8~#(yI#;C|`P z8!`i!)O}eC-M*Xe)oP2Ai6^(ovgy_|OJIi9u^>=C-aU+B#mypft~C@swYj}84fCTL z5Yr*@<*z6Sv^nL2PMwQ=1hr<3JXO|mOB;*x6)NKxnda+AW$XlHEP}QBN!ld4Z?>*`ZhtszbNT7AD!ljRYT-Sl+Zx|PU{wabn?Z*)5kTn)m{ay`hL7G$t= zM>L44l8jb4vt{Ph=d7&47i#oNy9LOc_pBn$9-?b~+Gh$=xFZn?_9wSYxiz=i+h9KyT8-s zv3Q$OMVg~Bw-*PT@VY%P_v)+BRH?VSKS4!Zj?6Tb7rA-=CIdg<(%~lSX!`Kltzxc^ z0=8$g8hzh+>_jt$*zB@4rz6%3s3@VkOt)88~Egf2x}+N;8Np>-oqya_U03TL3(wS z_s}W65GZdh*}f;4jWoh4+}hqObtK>Rp1jCVI{u!?4Xyj}d$jW-^KX00J>Y{|Lz>#q zlKm^ak@?xOdy*U*L}sVZ)^~L>=~((?VzPa=1V|i;cgK(aW2dMz_^~j#R4BGp(BZu? z!t!cZuzZLcti`QFGh}#!_#(ZFi0vDQ?Qv?_~vTZgAJM zv$lihF8t#SQUiVbo3Wr~y@9k2C>gxnTR+F1>T63~rR(GqNlD1#%YXwjOiQMu(2vIa z^mhYp=c7qYN?92$5(~u$&lW|Wjfp~eyKU)|K3c7Ysk_neyO4^Di80km3eq1J<^&=i zNe&Fsv`n0h9BjwCUMMgiq z?wL30DfDBC0^*u+D|O=rt8pDzI+M%w zni#nFYkzTtr1V`Q6*yVNiMuNPA!KQ_Lso$wZ`gyMF9=@oyT0{m|b^Q|MWiiJhYdqXL4d~|6q83NjvS-?q2V9lhuT#$(8X}{Z zVf#(c%?=tqyCFFy{z-gLQFR4B3yEkX>APoPzbN>qV5wqo`5}K-zI?GK!bDl_&#TgE zUX7z_8mFfKsy}?F7viD8f_F@5l}-7vVuU3XEOB$>vK9s39P@YIQ4c3I12dLqS28kE zuO81uKxt>jJhn@x0-uaR)@~@L8Dym{IP`NHj~f+8f>D<3k=c3zTWfI$OvQWpH9HUN94L-us+QUiW1~Ja= zjAh0Q{C9YrRr2Q~P%`qgX5@L7c-?YI{Y_?Tb~0x6^XA*-a{}s#{q+n*%@Qgq`gHX{ zb3C`VyZP4ysx|$oD|O;r#`Gq$cH5zO*4BZ8?l^<@hjWH;yT{@kKgX@_<%!Th2K-F8 ziAs!@8$1n11jDgE)oMo%MhE@s%(;nr9okD%tTVr0bmokFjNCG?UPH|=44&f5K+>W> z9v0r}cW%Z*uUDF>^*sbAOx^|P@OKqa*gMrX0H4KyGWU6{I5ZT#-TP@V9o;p|OS3m! zvl-7+|0Fk)TYNRjV7hzhk8j)<_^KlNW(Zuut5t*FsTFDa<-mc^j?lj;;%YLNqX3PX z2`1lQ%tQst!8U?Gg0bl)dd<%*yz$$^U+x{g*7PpM+E2h ziK_B~R2}KB#GYMG*$ehc&04-1tSJsDf~95{eN6nJk!j-`<6ca*y%^gB7LCeOs)gze zq<{gr1>LfSqtyWTQ@vn5lRYP&oq)q%*ZPI`?o1BKT3i~VJ>0?+ZeMI88>|&TuVbo` zZkja`-T`=4OkddOAsw{>e)QoG=yG$Ewi9W6-%#q;X*`+P$Vj;|fW374Cytjd-L2jD+feO%!y&FUCz?sL=2g5JU`drr=VO&r1@!0R(%Pp56>&!e41u)?##R%!qArW6 zOdGHdpf|QOFR6x8X{y=GsnFe38S96~gIJ(&k9Qz;htQg9d=*b4i0o;nk@=Tsm&jaT8Q{3W!C7_R$Ev# z0c)aSNvveQL_ZQ40b4BIYDkEoF6&eR!C)egB@=yxbIWF0Fv*<{{&`wyc4<$|E0Gl> zB~>^1S86G%!|l@h-qy-s{?WF%Xs((h8W?zlIRQ&lVeftU_-zOw&D$fKp`x4_XZPUb z^ErAuBXsBe<`c$OaQ?PC7sys)nYU9g$AZ#W2W(JGzK_<+7<|6x-+C6j>%W<{AP=!A=&wsh zU*F8n<+h%elb!t_l(P6L77IQEC|qxdV}S*dHuR{8yEJO%a59||DvuHwS3%=0Q`t|4 zlrl0j38qI3vheCZ6gJD-b9LwD1!?9}*Sf8tM#XHQr+9R`PH7m}t%K%JmY>|%QZzjh z>)7>3B+Id$Vj}30+k3e!PYcs!X9XvHh&OlUekluBbl=ImUG{1AzI8YCP`+#Acs~4P zh`>By-G{O;C?4(2*uwlBTaORz3E4~Kv$_}tT@BC}XJG<5zKnKB+}vM@34W~}75;m4 z6A_*fIOt>!;wT0RIvqR4Zp~Ol1D#bJEM$;X zwcrc|sOnTfsmSdEGbi(|DADRp3J}i{Q+I!+Jr`^7jhY;c&R`amwR-lL)sI!cb=gTp zH2EiEim2fgjN|Nf=`o~>()<>Zvv%ngXXKb1T3N@OOcslbVf>aAN?eJoIS)Hq9p*#qSO)`np11!&}m`T(k zKrlqkTCffxxV>p`edVe1Ai*+B^{VS3A>So(v4znwj>f-6Vp$f;Z<&=FK)k-s)SG1? zma%*KF9+lm@*&aJ;p_+IxS+mgmJAZy_Q=B{a4M27U&phyJ$Nv+b{&h;qR5W*g+#{z zgb9T@X~_&Hgl3pTM`9(ReEF-~=lpKYK@3lRF+$85m1Utr|sD5IS_H`!kW^HUlE8G3C_yq6O<)8Gw^SqfCK;_qFu_J zTsT1`!a1_~OqHnb*KS_T%n4%4u;W%Bfn@!w*GR;e{SUWcAnbZ4fW_E*_k`W>+}Z#+ zwWiGj#egfOZJ@hD-15b=s9ulNJ9D<7H@RB!#?e5q%9c_H(WeLnd6V<}hRSe-mh2-D zDvfTp1k598_Q`MgCU3mXUi!*kR))h8V%>Y#<*4muVKbeUY7ULmxYRI>6=E`2zK9x+ zsS8;=-kN3ro%3hXo|^!>4ol<5sU{upJ;$EHpBH;(reNLG=F^E6$is_7CLhe)5*&7E z(=5&HC?TlDrDIaLmONv3hPPLe3=4`9Ed^rRh+RI48OeHG5#2YT$-flj4<>VN#c(WH z>n%>cveUZNBuMZ30!cmtUoM};Dd;Q)ahP0?-gDv8z}$t}RwI+&0aogJ6G%)Z40LON z*31n)d;{UPfqn7K*V(e^S&IanXc=)NVUZNDF!ljfnz9Tx`4srhD-h?5yf+TZ?Bir@ zy7J0uPcHtDZq$}e%R!;Gf=7r2-FaK+hecJZqPO<6(qA6a z^)y5Zm{LNgwrM-6X+e(16>_Vn8HJ<`Y+yj!l1=vvQZ_90>c)0YZZ{xJu$K+K+eM*+ zYz*8Y&Nx0bq8^oJge^(;B~UY+HErOLnB-489{1YNK;tKqQyE}e2(UN;d#EoFSS+o2Iu{uTT6^g%pXaz0G(T# zW~IeCN>J6whmW6uJxezJDv6s3EY3wdrq}wN6ZwO`4&cF@)Xja<7K2?V8Af}~NJWLF3g2c$%(sYieHxTg-!ifM}K--QA9 zbAc~~?hWv-m!>BXs@`!`8ctlHk+voSkw8!fJ%0Xr(MUsdzeFS7u`GT zhP5l4*t8GVzmB^Ac^H2j8qWfgGd11iXI)zYE&84G@$v67$_sPfnnVNu3)C^cq zmZa5OOJ5`3a@dy9237SNv-GOqVr%Cb#mBsGw#XH(#|&1ji@sf(T-hP zxtIs1R~mW;YDUUvo^T-WnM@^;jw3Eq9SG{1SUo{3{%`H^uhP51n<+Rb?nAX@;JbVIlAfi}Q|CiT-m4ZYhRYLl=rY zar;iLr4NB|-%E5hPxNBb+>P}xdlIG{3X#Q;R3?M(bEcrv=uq5#5r1Y5;66Q6;Rf&tCN@rm!@>+^~= z215XZkBwIuFrHV|Sz;Nv=W-P3=Cie35;q3PG@vzWwj@>+s7JGER~JM$QKjkUiHBQ> zv~4H-NW~!qpJj3oZnLIQVmesL9G`y_0&tRv-WLW)c7f3NlPuv*X?h4tfK~*l^_BM& zu_wT|Zvmx~Ox$HZkS3q=BvE#xd>DYNSufaEl4Czn1vN9hs7&_?Pu`pC>5s+&zhB>M z-8-6Unj3fA2npG*ITg9DX>sytx$yb^hA*Yzxp>)Yg*$@L=T$vB zI<2~EV!EWJK8RH!zdQny{IE!l`Je*y<{MhZ({~D~{GBKU%Bz=sn)%_=pkb5a7sYo` ztus^6+)<0}xQJ#?hvUj1vBkBHcyH=Owzm7zvYUjzhNr%{wu`OkMqWyN`< zDHMg85uw>O3A4e_6*rx+cvEEw@l~QDbr2Wly<>&>>DAPDA3r#C=cfRfmOL76)*B|8 z{R(`X`(b&#hT7Y*h*!@HOG(!08T8H5c~+80>n2OJxzEJC7M}9`gh9zW=_C!q_w1~} z=T2m3*~*EBaH zE8%X1#(a+6K^8%fGHOn-sxnuIR+9UrY(|33`kf2M{nWBh46Mh!GzZw5g+gvvS>gJ; zw3o-=`{3`iAk|4eMR5AiFBx&v3_Y1iem)*$6E+q(z+O}Abpo>8p11IM+fq0wGRCm4 znp(~BRpCX;&NdneOe-*=UV?`I3x5s+Hldtw-rIzMo|*;qc|}|U1Z&E}!bL$#XI4yy zolMHuGQ5R?lOfx1p=-)}>v}2Q*1PA>dy0{qui<^GE zx^|f0ImdfF3aqY?KM!Oc2;a+BGhu4!BIJX(gx}^DPvqQvNExmU5L~xo8;Fb-8tv!x zwvcXg8DLkK&MV8v2(fFmwTP^}teph6FbekP)O6y2yLA@q?~&M&yIZF~*hTHhy-^@SXAKII#L1OMd+>swb^nPdnz%#I1%q~j()_3&rO}1`m&|*)f
( zQEgxwx>qdCwk)8&E*gV`?k31mw{p^?a8$=1l?9WYb}_EsP~WufW3&ooU!}_mRNHM= zE;ZZ6O1A)?O&V8vWi4=zOL*Dg)PxghxOBWQ;Kghe$_HAYb&*~NV5P2Y>YhwdK zpD9Jx&j8PNare`3$5%Cl70g1^&K9Cf%BofR(+n6he<)^V z6yHC>DU2H+R1Z(o7R+80YnEJwbKOZqxyw~qNvNqaHT~FTpN_2ND@j*9=+o$OotpXK zFb{-LpdX!dwv`@O=6mDUkXg@PX>xCezk-`}AFKBGvAUSx+higu!Sgf{Q~?)VDtxO7$8cX*po_>Xy;>QnD3T8|f(3 ziu&DYcR!r~S@F;JsuK%=3xqd$ZmCiG{RdkbQAOp|5-bAMOVBV~ZBu-M=NT zqPWv{j7^kfGm%})!MmJ^IVBbcVe2tcDi}MzX?1$d;AgwnqNiq{J`K_rAV#=mUoZ!} z^;%2ombCV)>(|HH`ywB3PEQyv0amqAzkINoB47UW7{rb?eotxiONs?|S>o0(hke(@ zD%7kyAU!9uL>tGZq#(S#+xwYTaSg$>_4I_KmMY_=%PB9Dl=hh?jR8o*y`1D;-iqN? zhQ2jV1x$~a5A9eYXltxG%L2y+R8qyfrF7vaSyC3s&g{J1-M|x9kZcu~}@DLL(YhTWK%p}b_CVs4MS31TG_i81f1_Khw zpESEx{(2^H?b(cl^j^*`ICiR?wPJh|J78g(HaOO8`;KbyV?8JWW8=$bgm0tr)*~(V zK%s@~{Jg8WsY0}#8s}*g9d{y#mv38)1X-8A_19@d1i8@HanThE%=?hQ!qyTo zzgCEo+&f}ncJ|y4o}3{yP0KNL!9k9vUY3ErJ%O(anS$g{*{dWrzk!--nmXw78Tu4U z$}|VfvYiiqCIV*B7%@RaRxr}rWJ9QLn(&+5m8&yUcK936oPx*=+qgHr;FlYLO)xb? zFqN*XZ@f_k_alO+fpK(@Hc?T2`2C{P-taMugn$n+=#WT%LlbsATlk8vH|>kHg>O|{ zOUVY(7~WiG2oN{8!{FAZCLWRbwq$nFe*5sdd6l4vB$}x%~P9WU$Jmb!ItrOB20VR1l+3Dj#**4M;bxC`%b1mmFqLFpuiI8mkBR zwD}2&nvp)pMkiM1*s*Rb0Eb>4td%rW1B$?p-yrrQU&xOULD*Qt05T0=sVnQF=L7_0 znh37dYZ<_3!h0Dqn6p#5TdKNLHfyy=X^ZuKLK6vy@iXx4xG#1IGD^mHNr}b8CIWB8ZcFP&!S8YN8W1xpw32Kc->f@gDpcpi2GHpmx&8>c#0s-PcEG%(`bV%hj zQ{(6A!S%H20}E2Gu;5UXjoY4;!fnW@7wRp!&(jt1?`U zBKcDQe?Z)yy5U~lUyPYd3haUGe@%Vz{S>1LfU|#R{M(JkNDgLzfnYOKroV`lWK}O~b;S`h&0!o?*~78Sy&KO8xEexT zhW3B}65M3Y#o%#mn#q2FDp60b&p`PJMNy{~(V06puHcJz_~QPZS9IQ|s=>H|3vX!F z4~MZ8>d_#5;sA-`R=@f9;fVU3ogI?WTrZ|4%dLo3(i}g^wWt#aXFSpGVESnt*p+t` z@reTWO{Qiw+Al|?e8obdH``1{Un}aqzeHHmyFF4l{`$KW0{y1cn`$qhx4JjzbjMyM z#VJoWu1V<2Wb77hMbE{-F%x;_;qa)%YYu0l&_2GmuS>0l3#x1CzL%ja*SZ;(_5MH!CoIuqPtX zMRL)r!^QhEy?X=!YvoL}6my@l7z+GFKD6VMzZ`qmFw5lH0xF%PW{0H`;3%y|{`4dc znw+=xDwmr^AuoDVNcIrgEcW;z{`!yBKYkzsKZLG7bOppSB&>%ZSH#6XB>5fJ{*d9% z14M?u_n6<|+YcH3yOKQA@Ta_gd-adv{C`5?2e2L>`vcp*y84$;{`<23w?p~+W&Yzk z`~9LH1o$JwzqfplE+I@87#Z;Py{~{j&=HJqe<) z2i^XELxuluwf@g3^Mj84Pr>_lEdIMx|DkI9A5jPiop%!M$7>A;0{MD%XXpct zNJ!;hq3SmDYKAeB&o3HgZ=*dBuMI&LHaJ2$8ZGgyVmjaoLix>pR_6un{<_aZwxcUe zib!|he=sGFL5$G|S4&Y{&TuTDC8;iH7er0)`5(5lIsk+|56^h7NnI`HjX0YD(a{iR z4v;q|A%C?7d)y!nrg>mmuYx{LVcudxju8~>htnHyMFI2N374^9#=lG_)?EHpi9hh} zfi|uxF4Iv0=6@hKKZpoIQquOTB4Z{Dk3f|FP?{f>C`)(y^Ikt$o(Rgr%rie=KvRVR5;ci3>_1oIA$2sPE` zkniCzhRXOQRwWzyGsl?8cK7)`Z_T-(tDU=jep}Z%mwT2!2JPjGG-u+^MN_501+ZD+ zblHHgv_B?-_qF5g{V-I0YSP3$HfSRtd^lUAYXxy8z$4J-wxs}^Nm=py8KEZTK1i*y zy2xA$-ix+dpC9@^?^eVJ$sb;3H*)!GiX%lxaZ6Agu7m3v4f2M|;ZeFdX5@yl>m}iz zhll#CtEZe$#3>l4zEkzB99@>go>#MPc=onvm!;{`>@|BnLBM;^y^)=FIQ!&s0YQzq z?dCp+;CUddg|vmp`^f0aQJY6&X>Gk#oGu`pXsq1~Zcu$<9bA*ui9eby!bx<%oOdko zpwma1eZDDix5eHwfaj#z1TP1Laxuzxp_?^=12P0y=e)WLx2?m?p_wR)a2yQmFH^IwX9?@F0 zsw=?2`@+0PvTf5DfXoq*O5OJt8N^$LmRo@10N%4dC8Ur(Fr?>Q27UGj4VD8MZx!U5 zulGAJ+wN?9^F1`sxFfd}(K0qjJ+QCn(flk*?`71VR5;Rt0Cwc+1fXOuru7X|1ee$o zG*@@ybblmK;d2#q5O6mUs*=|1V4TbE}xE!r7kKFd2WiA#f0cUf3uRHJ-3i9OeZ$nDz+gW|CtLQ;^E?H8yn9@ZFk~SMgTtGo5LP$Z-k2fIUfcQ`RGIy zQ=K*-E#f(GAR~NZq$@1&8LHk@9iLA5n#uh^qd`cnJ6}rM1zh(CGd7LAFK+6mFazxF z?d3Fb+I{xuO51Py(C$}?YQ>(3vbZ@^YVKWKG_5|>wP|kFCAGsWy7tj34V{05vsY4D zDs$p^dqHUv=$yUYn!XYGN>s+PBlP@-vQ5u+%Rm8~vHCf~D?!A{>mdl(9)!Nm9g6_PUaYwoE~A)5@EYqzBMX~KLBKHfjf zEe|o0(aML=Th))>@5^1yNFoh~@_@C#U;u~55~a%;c(z>vzZ(j1siR@O(3~qXlK`7sU)0ph?$ANaX736u+U~=N=S~G$zvMtZ2+a1m z##0sb=S^Cakj3en`)Lu?X5(j+z4fX5zl!Dw{Ui} zvu9?{%ro=bvk4a_pmQGGbP;K+n9jY|6t~FKrIkmL$8nO!u&bzC7vFT1vvQw6zoj;H zHJ)68JBt2qHbdaau9-s5dgCwE`hq7TS^7SnR|L3+WzZNIRZkx#Dm`ca#FThDL4Wko zee=a8_?6+;A?h z$n?Z-`ns)CsD!e`lF#KrmI?>~Tb)zhAY3t@t=M{IP(6J|=4Z2^m92YECyAVH#_HCj z&>~`;nZUuR#F3GK8}UQG>CZ(m*-Fu=q@oK`Hh5gs%NqU+5b8}nZnu8}wR9a|0U_Z6 z@Me1PXycQf!P5Qq8Lx0&)hq(a7373*J^E7ZuIYdZRu_$1l$+x8jKsnsulQD8#kK3}G{$RjJf|dg~?^GtY`csTTd_6tED?HxqT|`c5d? zl+QPY3{Y(JSL}jo6)XxUt{)3eC{$MwxQX0KuCkg+hk~{pqoR%@9gjba49yK1!y)4% z0hS#6A09sbck`lqu{V}{#gYbY78J1qtZ-$?y$l8teg4;j)u9uym()-$cKFyk67-MO z*CNTc00*Z?mS-{+PKIyw^+6p(T;Ywi#4dc#x|&K6w)TFkqqrXDrMPlWq-AL{M5hrb z)Q{*hK7yMofilRbkaO_Xk9x_6aUOaWr5)stXz(g3A-=sKPwc}E%*Xl$w@)`uDQO*pa#7_SX^gC3n6(5NiI|zJQMMKG3Ve%;nJHm{G*bVzjud#-2Isb=SNR zOIYf&+%_adRF(+JAt;RodFu=}gErL3;FBF<+7 zc&%|*Vd5!QkvXBZ%*|r<(E#2$pvE1{6sL81!0=Vl%9qL;iEJ+KUsQ;)xI%uy~Lcx)| zpd#nY%O7TidEWTMyrW7O1=@GhHyMBQw(q(4&^G{a76UUbq3ii=-^WFe_roZv=we2? zu)Capw@${$2JW}jUOi9I)7%$c?DR#^_Hi|zHAK$Z5<(~8o_*2JOQ*@D-5UG6>zIVh<|j(C8KvDw zO+*5&=(YM}MIs^RyY%x$Zk+MuwFU2nwNLUMAU?64gm3j&1!P1A!Oa702>pgDfci%% zQ{HYw`-0%WdAz|l(B@{wrYld%D`!D|e|=rhF6;tRaqs7gPzf)MaZ~3+X1sii1Tv-! zW?2GZg}AA+hF~LCy4Kz(uu@|Ie@FY46p_#mR%@oA#J;cXHe-*CpHe1UhRTo7;=paq z^euO<1RV}CJ`!mM<}n5el&AgS;>~*&Fr;}rF_~in&xkg2cEltM1=Y?VJT^v;+T7q# z;H%O3CQw0p;4nq(xr!h2!nfbo_!>sT9`|!74MdTmBRb>EzTL2=LVmJ#U!b^ldBo9f z@ZHSKO_<>)xERtyjSUIO(Nh!rTS$0CR5R^Z}JDm-**?+3;LYO+b`@W`8`&o@7+an8#eD^7nExFd`5?-%iq&H z3LI3aIF)kCUy|$PF|xZpfu@!Bo-Yg5+{CA+Fn>{C!XE8zwW*!3-!;$CyZnNls$9X8ka#_Op9VL#AADdC?dAv32&UX}!tB)&*(JUp}-Ql79>#AO|+evFmbW9T! zjw_S$%FN$(Q%Rn=d(y4vQ|~&{8r0aHQA@BnGJSGccED`Kk_B9yd0W&yeYwy`_7z%4 zW?~<_rPMyb&*=UDBM3u?eD=C)1VmdS5&3)7FLk|IH8U%<8|D`nmX z2M6+@*9+C%V0wss!*wF}4Z|8_epBP*=q~=O5WB+TWq!nom;_wDEp~R;+xk{J2H3Di zr~#jd9HK}3*-g?hE5a3*zTTViHQXnAhnl(~^~b^XnMX%dOcN^hx+0^BG<`=xp_8VD z&%&GA%C}#-CFc{%#hh;RWn9QSrxiHW$cEfDJ@d#?-_ zkVOwj0D#4~ikzvb#TQ{egq4?ZY;qhcp%8kVyA{j(n(xIObbU%-8wDMvB$*1iZtY&( zw09S{Web9GmqCgQM)==>Z|h9evnjMxdo8clZs&yOf?Z8vVu5D`0blF~8OPdZM+VBN zPWFslb$Z^t=lwbjmNPwDlLN6UZJ>5L<1RlP36aUgq*DZ64xV&d`xrn$#k&@++dXSW z*Nc3D6per5js%keMYTsN5j^DXC#yNKpBHQAX2=6utBT$uf99X1LtlHa&?g8Qfc zs+N#@a-stQve6`H*3i?v$;$cQBV5WOcpIB^^5sAw18^jSdgX_z=vk8RlKVm(YRDu* zK4RG|hP1Re&oy*(Zw?ql{3Rmam(HeR>jqTrF>i{4=#U#<@1XYqk-Si1ircWfIsg~P zvjhN0G;~IGIgk`*jvn`4i-^{`1zdv;l@P72=!k*r%<#8?U=j{ChJ~}QWADY|RvS}`B*UItC5p^zJpf4gm|uO$y~Tt zNa>ck=~5n`f&*#dpY)QTJFDUwmqI06_?pf?{)V?$iRA^M5CLj0QIu5;So1!kS;Au} zVIQh;Vix1C0aCW+Rax7X z<-{WitwW)cU3AETm&zJxpISvuojqy$jEgvsxM~Jl+>5B;o?VJiGWeNQs>DvIff15Y{A-(B=61ex!5rhQPvt)3}tTaOcA z!21@<^vHFq%DFh|p&j*cUA&4Ax+~45F}ydDkSjCZW}zD0RK9!PY5K>#K-g~9mlQwt zOvFWLuz&HZE;^`)3cq1&c&=Ob#5Oc`1EXXMc{N(~mkTWu-QCx2K)9dBXd^lCA*+YP z%KEO{lSFo5YvQun#3z#dhH{=Ijmr8JqRI20o(x$h>w$KqV{LcVE4rbJZ+be7y5uphZ*-IyQl0Cf$%o@?W3^M>nN7LA?{VfP zKeAN3cc!iSm@&iN!cx7ldopD}sLM3gu{9n0@ugb~q)zgRYAO!(>Ue@C!oK0F_7nbu zc(eBpj|F}VmA|Ls<8-x;??srUQ%q18Qx!83IWVw!cXPp00*kR56T!2W8;6x{G%_uR zo$V39rlg*N9B@QbU3*0!*@aSC(ES>gD-5O+EKT=r_Hs`F(@9C>BO=v{2*;f{dNLfe zSCOG#h^9H1(BTzq7^ur(LmNHioHbb(Z>3$oe$-1}y`({Y43X{Cd!o2$yrjO3bjxcS z|E&YcbzG*BJqvAju6*k~94z{+KyrwdR%hLMoUoGKLf5(z5=RU5gX#;jNU9t%bC^4i zR)Mvy5+sF}4L&k`m~B>y%quq1lxqp#(-q6L@v)x4ip=iE3KvRHBO{6zpG;T~p*pN) zMjFa63*wZWv2rcU8ggf9!1P2IW~0{t677Sbvk6wO0-uYLUw7a)0~K2w7c^fg;%1j$ zf`X?u5auN@v``06io%Xw_zbU$?THK_PQ0iJZIAr2c(-l_sJgLfVmL-m=-a+bT> zUT2!s!Qsxp$E^r8a5X19?`Xe$m<(N0IqAH$+DC!cAT_O2;!>v4LkzfRO89QNl8e7g z3wXIRPS_4%#LNRfDh(i#r$q?Y^n3I8sYYA0oM)9$O7t>74a6jxqE3rwY$sN!p~_Q^ zJ!VgMlSuN!_kS3xG5FO!AEN2dWF*2k6sZcaf=>sy3%%IjT67s$_Qb~G^Jzb8LX1fo z8+N839_W+TJfMg>!V^YYIa%rAle4Oae-rrj`5xNN&wKKu{p#s@gyr|G-EpNmc|cnw z*EWcxwso$FUNNNJHae%QrY)8hI@NyPuqb8gWy7fXU0i9} z8C@n14L3cTzuHvCg~sOu!8A)gy2td;vacA|hNJj`6n{!UhBF`c`7y5oQnEjb*s4jX z7QPf^#J}bL1}EN)kokUAneUYrVUJ+;#ADPGJMPQX`PqQ}A$(sVpeqG{|GAXO$ejErqC?6;(J>NXUNf!lz_%OJ98~FEZg(pxutZocp_f->R13 z3I~8!$pD`6c#3KIyKm4E5E&m4J?<{Z{QUVDnlpBrj?Ri!eNmvQk z%5Tr%a{BGX^B3vle4`ET>7iL)epgQdJacE96i#BvbG9>+1ih~A@46$*)h+N+BOKqy zgG14RuM8S~@7yvKjGcN0tPjMESax`_0|2O_3_BTqR!}J`_k@!k=wYBNmH?sW_wQ~< z>;@N)l}^sb(IIQ3Va%=o3VnAY=Tb^s`#6fkD#TG3Z*dI)_)o%v@~&fr1TfY;QwXbx z+wuv+H_#K1t9fKe+}zi_GlOMMt%-h4dc+;As^^Ybmr2kjUau>GK` ztt13{mH>eTY)y(+8fSl)qBqb3ky&4d#X9Y3ydAP;8uj;%HlPWW$BR>diw^t(Q_B4?;-uRaE2Vv->w#Hzz2zNg(eB36~P)k32N&!@@te>We4?1k= zkZ)>R<`q!bpc)7)S>(a8mZ(twJvmybUY}~?#wq}Aq42A;XAo{XQ9=V%^4V@XZ0}hO z7nIpJ&yYq4lN+FbY~yA(vlFY_dkvT<|JI^@eJgNdMx%@;g*QzPl+{Hz^ol+O0EM3? zF#0Kr9r3NOLACsdw0-ABxE=aX38B8?ohvXs16x*hc(5aXEas%N972!$RRu-ri9nnG z0W|+`XgE@T66|-nU-8@Y+mnL;lY|FzjOJ&J&zd3QZ)vn9&=13S?>|HCy~>i*e?{? zp29LY=AgdUPG4?k)18RcCzbW7WkZ;z6r-Q{tD9@6O}_|DZOGhfuMAz|?jbR5jj`bE z+~gGRr&1Oxfe48Dt8u{*u!F}!Rc`s_O9gU!mc}3%eHtg_;7heB?~{@SJ3?mhPH5 zMuUcgFy)%Au?$spQ#H`cP={sghNb3tsy#JS`>F^*8K*6pu}CB&5d*ndznnfa|L$rm z52aJ!Ho%@?1WI9)vZX;r%(DhC*>znh)U6SHa&Eh$yR(x~>@%W3sc5yW9x^C(ajmj}qiwg`Z9I_rW!0r?JQR-7uOL8RW10m_RSbC${JXIMp zeI-Su&KD{NRSZl(WG}B3f(;(vJKJ^_JV`U2Kp|OxM2PIvKdfAhE8%3v_W|hsdKeBaB zE{{H2h|pR@j#278eO6>QaMze_d1*yvQ7iL`{UEY9*s(v=QyG_TRC#vtQWelovO$)x z>jY*T=UP9|(quOL)Ea?1(R5t=c@VJs<5*$7)cIK=_e|ZQnCWuuDk4tHsoCm?eB_D0 zzf;A8NV;Br?jH#2nQ|D}>zdDHngg%e9*j9!Mg$~oy*#h@L~o}xvs6ogxbQz6Gd~tF zuGFu{-_mDfbM;8_{{sggOaOpWP*pool>GhC5FC;Otp=q8Rs_m*5qhBb(u#oYP4kT%o3V+B z&7`|0B}d}{%EN^fTwyz0)X2;%lH+ej-(i^ZgbQ<*O5EI>ZKPR+E#XjiU{lP0$T>gw z+8m|_&ZG6%v}CF;5+0hICZ}n3G1hmz{hXh|tb(y9db6;eut#5a)=t!}K-F;jHJ5+# zmifc=)tam?c`V`+B5a%YhV0SeK-XH{t~SOvPPqADt5O#oK3#!-%b>bj2GMM$uWks5 zU<|v(SZ!Px>hfKYQ(dzZxHSDV$DpM@cHDeM0hA;dWAKk7aj}v`LJZDrcgK%eocacN zGvovK#ygdXWfjW@U5?~@uL?-tZ0mo$soD)`a2mcpOmuT+H76Y1`Ld>_j?!sp{6N>Z zOfhz=_HyTCrV+VTi*%abeagR7$8FAe7jK{x!%Fff#ODqL&q}t2&88SlA#5NbHUbQV zdB^?ZJc*it&xWGRJ>~8CRLgm$6wIq)CW(c5ji#cXJcU3CV7r+rzINK5n6{TWB~|9S z4mySrRwM%nx9tfk*W1UcOv4@8yiDMhHkRVE9Re|5%zSucLOSN#QoC+H zw?CFb;65`@{m!anCZgU$rDx-tL+3(2=PQOF)17mKTW2-X$|J1Y(hx-Jn1n)fvaKy> zD8~tP*tsF;3l1_JC${>ycsz^U@XQwn!AYPYaM{565D8}k1Ep#k#SG1{!Iv7_W~9g_ z6mvaAxr?}>UpW!DwfY{oASm4{7$zY@zW)Riy>N($^gr)!_DoG7SDf`;%_S7~8LF|g zc!#p*`opQ5?(dmfB4vB`*Z7>MlJ%ySM^T>Tdqc7@TvPjE4Ml<&vXImc5w8q^!92$o z*y0`HgsfJ2tBe|vw0tmMPXicxS0MkT<~gD~UqR3KCO{@FoCKLcf@HgD2k97zXs2}2 zz%59TUe8S$o+^!VJ`K|Oxh%`4tP`h~;ppc%_2UVMVO1tRqbqX3#k3_i(g)Rb)T|28 zA3AhwsP{dOuc&Wg5b-!&wimP`%#(#e%%wn!k<~|QFSZIM1c=dAp0;oO1Ao4)I_w2z zRU$U>9H5b!2XtFm?%6I)5L&R1pKnWgMY(IUXSXi6TLuh|@e{x%Vs z|L4{%p-hWii$Nto0jS^vR3W}J0YaUK$fzZx&D))x5Q(=JaPO>*qkYwoR84})HMHoi zCTWG{?HqG*IW;bEHs0w5j(iMj~_*ycGgcj^EYf zOX=jG1!q&kOH?5{&0Jk~b4vUf%p6b@RwtQ^NC(JD3V~3p#o!3S@Yrf)d8;Gln*YEI zZw+Nv1e$h@LVR7%KmRy?j79UTti}F$=KHBViA5t%r+1T!82#n(qM0Y@?HUDrd* z_mNnXXy2v;GgfxCEx(8wP**@!OLn&~X8}UWmnl+J!}Djhu2EgBi(QR0M;+>F=g&5W zOy@plFqgJ_jM^GWi=B2%c~!MfiY*2l=acVzd&8h5e;m>f_%IES5>*H{z+!PvIuDVo z4_ahSEIHZyoLr72r%RZ3mr*(N@O-Y)>em!I7Z*Rp&7SIH+G~G5-n2I0TAfa6 z1JCzd`4c4IxIn?FX20xDN&Q2Q3DoU*J%>tlH_Yq(##84lL*QfIq0w^dWW zqb=ZLe%QoF(vKurzK9;9^pRn|l-Q%QuLkYbJNp|?h_T}Q`Y0W*3cn$@uhf*S_m8d0 z6Md?K)Upa^3dj8(?bwT^D?AK5)#O9{mGdnH7B>$8_Ho)2GI=<-a=L0O*-OVc^QY#r zoyyl0_L271Bk{g~ThR}8#87C!o)*|Nt9iFZYO%>Y7apl+)qKR&+!XNbGv#oNw9Kj5 zJXBN^MV$u0DZACiZ{H+Yls%3; zEH-YE!`)5lB%mThyKkcHT-F|B(f3h{IyBwwc}=bJTYhsv zRd9~*TQ{ujs4;Z>8QSUUOwSb~&Le>B46Zux?UaV0_bgHLzKS0XiR0}F zh$)l4x<$C>v>+EaygdH&L)`FgnKt$Lqjcjq!Bx6W=1mps`*erMZ1h<*53)Y@1?vXZ zj|dH3fG(cAU0(^rSr$!v1hf=AVL;n)igYmFzAINdr>8xq+sxPHt#@$TYQX zX(teM`sC=d-$h&S#ap`m^`fDh2W-zhMJ4Gn!P)uq?2Q87nkI*Y*pMW$60%3()!A$a91SAz+)#y$wtx{AtXJT6$THh63zX?!h~GkSA5n1I=SEj0RkE zkZ)KHA}GNeBN}I?mUT3EBXk;Q;nRsK+2(r{E5K=VPGh+0`v6U;CJ592a zXKmfgL1UKNO2hGrAhLj2HHF^Fkg&?)-K#(Q_Kz>0+xrGH zsEe<~H(qhm;Jue`O>~MT`7XMC>7Zwna!5%u)H%F<*S`7wkVVenaO~NHMfIgBCveOM zaCLqw=DltYK?)~3arR2>!?Rt-aVeZ=Sb*RUJAdpIY`jG8>U5?!M{gwd^lZdUrvG#v z?-=@fkLIKiG6E)(#*LzIiVR^akMGYLZ9=g$G(wZ(h42g5UA> z78*iw{fWfX_Zgs~8E*h<59#&ZfPmy6e~$o`0pvsSYc{`Du*I>}?Q8F=(u-f~myN@J zz6Kl}+wZ`Jrd8i@RWXR*zmh_)sl@*sKk(+Ss6)_mR|i;v3SLcu^sjzoDn<5-m;-$~ z&@bw;F;nqzj30wI@B{(Q?lpSj{6m*QkRb3J_(8yA`wnswx7D5aup|)ZZi@dY#5Ysk zGq;=0h0^@2Gh>WhRnF_yW zKB0{GN2_#jpBx|g?a%eQeRBZQ3DLa-m~t5WScl4`B5>xY2fq!8cG>3-(Xe2_>GC&N zbJpu&FK$uY_>XM;C#K!RbDt1TO?o9wD3DEHyfI8l|0i1{5 yfd{gHb^@CO`13D4{_mOPZyNqHS}dk;H;7U`np7?=3js*k8>)($&~o|TLjMhVa@9ou literal 0 HcmV?d00001 diff --git a/versioned_docs/version-2.x/api/01-alova.md b/versioned_docs/version-2.x/api/01-alova.md new file mode 100644 index 000000000..08201547c --- /dev/null +++ b/versioned_docs/version-2.x/api/01-alova.md @@ -0,0 +1,314 @@ +--- +title: alova instance +sidebar_position: 10 +--- + +## createAlova() + +Create an alova instance. + +- **type** + +```ts +function createAlova(options?: AlovaOptions): Alova; +``` + +- **Parameters** + +1. config: configuration parameters + +| Parameter name | Type | Description | +| -------------- | --------------------------- | --------------------------------------------------------------------------------------------------------- | +| baseURL | string | Base path, empty by default, [View details](/tutorial/getting-started/alova) | +| statesHook | object | State management hook, optional, [View details](/tutorial/combine-framework) | +| requestAdapter | object | Request adapter, required, [View details](/tutorial/custom/custom-http-adapter) | +| timeout | number | Timeout time, no timeout by default, [View details](/tutorial/getting-started/alova) | +| localCache | object | Local cache configuration, default GET has 5000ms cache, [View details](/tutorial/cache/mode) | +| storageAdapter | object | Local storage adapter, default is `localStorage`, [View details](/tutorial/custom/custom-storage-adapter) | +| beforeRequest | function | Before request hook, [View details](/tutorial/getting-started/global-interceptor) | +| responded | object \| function | Request response hook, [View details](/tutorial/getting-started/global-interceptor) | +| shareRequest | boolean | Share request, [View details](/tutorial/getting-started/alova) | +| errorLogger | boolean\| null \| function | Error log, [View details](/tutorial/advanced/error-logger) | +| cacheLogger | boolean \| null \| function | Cache log, [View details](/tutorial/advanced/cache-logger) | + +- **return** + +Alova instance + +- **Example** + +```ts +import { createAlova } from 'alova'; + +const alova = createAlova({ + baseURL: 'https://example.com', + statesHook: VueHook, + requestAdapter: GlobalFetch(), + timeout: 3000 + // ... +}); +``` + +## alova.id + +The alova instance id is used to distinguish different alova instances. It can accurately match the method instance of the specified alova in [method instance matcher](/tutorial/advanced/method-matcher). + +- **Type**: string + +## alova.options + +When creating an alova instance through `createAlova`, the default configuration is merged with the passed-in configuration object. + +- **type** + +```ts +interface AlovaOptions { + statesHook: StatesHook; + requestAdapter: AlovaRequestAdapter; + baseURL?: string; + timeout?: number; + localCache?: GlobalLocalCacheConfig; + storageAdapter?: AlovaStorageAdapter; + beforeRequest?: Function; + responded?: Function | ResponsedHandlerRecord; + shareRequest?: boolean; + errorLogger?: boolean | null | Function; + cacheLogger?: boolean | null | Function; +} +``` + +## alova.storage + +The storage adapter instance corresponding to the alova instance defaults to `AlovaGlobalStorage`, which uses `localStorage`. + +- **type** + +```ts +interface AlovaStorageAdapter { + get(key: string): any; + set(key: string, value: any): void; + remove(key: string): void; +} +``` + +## alova.Get() + +Create a method instance for the GET request. + +- **type** + +```ts +interface Alova { + Get(url: string, config?: AlovaMethodCreateConfig): Method; +} +``` + +- **Parameters** + +1. url: request address +2. config: configuration parameters + +| Parameter name | Type | Description | +| -------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| headers | object | Request headers, [View details](/tutorial/getting-started/method) | +| params | object | Request parameters, [View details](/tutorial/getting-started/method) | +| name | string | method object name, in [updateState](/tutorial/advanced/update-across-components), [invalidateCache](/tutorial/cache/manually-invalidate), [setCache](/tutorial/cache/set-and-query), and [fetch function](/tutorial/advanced/use-fetcher), you can obtain the corresponding method instance by name or wildcard | +| timeout | number | Request timeout, [View details](/tutorial/getting-started/method) | +| localCache | LocalCacheConfig | Response cache time, [View details](/tutorial/cache/mode) | +| hitSource | string | Hit the source method instance. When the source method instance request is successful, the cache of the current method instance will be invalidated. [View details](/tutorial/cache/auto-invalidate) | +| enableDownload | boolean | Enable download progress information, [View details](/tutorial/combine-framework/download-upload-progress) | +| enableUpload | boolean | Enable upload progress information, [View details](/tutorial/combine-framework/download-upload-progress) | +| transformData | function | Transform response data, [View details](/tutorial/combine-framework/response) | +| shareRequest | boolean | Request-level sharing request switch, [View details](/tutorial/getting-started/method) | + +> In addition to the configurable parameters above, other parameters supported by the request adapter are also supported. + +- **return** + +method instance + +- **Example** + +```ts +const getUsers = alovaInstance.Get('/users', { + params: { + ID: 1 + } + // ... +}); +``` + +## alova.Post() + +Create a method instance for the POST request. + +- **type** + +```ts +interface Alova { + Post(url: string, data?: object | FormData | string | null, config?: AlovaMethodCreateConfig): Method; +} +``` + +- **Parameters** + +1. url: request address +2. data: request body +3. config: configuration parameters, the parameter type is the same as [alova.Get](#alovaget) + +- **return** + +method instance + +- **Example** + +```ts +const postUsers = alovaInstance.Post( + '/createUser', + { + name: 'alova', + age: 18, + gender: 'male' + }, + { + // Configuration parameters... + } +); +``` + +## alova.Delete() + +Create a method instance for the DELETE request. + +- **type** + +```ts +interface Alova { + Delete(url: string, data?: object | FormData | string | null, config?: AlovaMethodCreateConfig): Method; +} +``` + +- **Parameters** + +1. url: request address +2. data: request body +3. config: configuration parameters, the parameter type is the same as [alova.Get](#alovaget) + +- **return** + +method instance + +- **Example** + +```ts +const deleteUsers = alovaInstance.Delete( + '/deleteUser', + { + ID: 1 + }, + { + // Configuration parameters... + } +); +``` + +## alova.Put() + +Create a method instance for the PUT request. + +- **type** + +```ts +interface Alova { + Put(url: string, data?: object | FormData | string | null, config?: AlovaMethodCreateConfig): Method; +} +``` + +- **Parameters** + +1. url: request address +2. data: request body +3. config: configuration parameters, the parameter type is the same as [alova.Get](#alovaget) + +- **return** + +method instance + +- **Example** + +```ts +const putUsers = alovaInstance.Put( + '/updateUser', + { + id: 1, + name: 'alova' + }, + { + // Configuration parameters... + } +); +``` + +## alova.Head() + +Create a method instance of the HEAD request. + +- **type** + +```ts +interface Alova { + Head(url: string, config?: AlovaMethodCreateConfig): Method; +} +``` + +- **Parameters** + +1. url: request address +2. config: configuration parameters, the parameter type is the same as [alova.Get](#alovaget) + +- **return** + +method instance + +## alova.Patch() + +Create a method instance for the PATCH request. + +- **type** + +```ts +interface Alova { + Patch(url: string, data?: object | FormData | string | null, config?: AlovaMethodCreateConfig): Method; +} +``` + +- **Parameters** + +1. url: request address +2. data: request body +3. config: configuration parameters, the parameter type is the same as [alova.Get](#alovaget) + +- **return** + +method instance + +## alova.Options() + +Create a method instance of the OPTIONS request. + +- **type** + +```ts +interface Alova { + Options(url: string, config?: AlovaMethodCreateConfig): Method; +} +``` + +- **Parameters** + +1. url: request address +2. config: configuration parameters, the parameter type is the same as [alova.Get](#alovaget) + +- **return** + +method instance diff --git a/versioned_docs/version-2.x/api/02-method.md b/versioned_docs/version-2.x/api/02-method.md new file mode 100644 index 000000000..4cda9714b --- /dev/null +++ b/versioned_docs/version-2.x/api/02-method.md @@ -0,0 +1,481 @@ +--- +title: method instance +sidebar_position: 20 +--- + +A method instance corresponds to a request information description, which has the URL, request headers, request parameters of a request, as well as request behavior parameters such as response data processing and cache data processing. Through method instances, you can feel a unified usage experience in any js environment, and it can run normally with very few changes. In addition, method instances put request parameters and request behavior parameters together, making it easier for APIs management instead of spreading it across multiple code files. + +## PromiseLike attribute + +After `[v2.16.0]`, the method instance is a PromiseLike instance, which has `then/catch/finally` functions, so you can use it as follows: + +```ts +// Call the then function of method +method.then(res => { + console.log(res); +}); + +// catch exception +method.catch(e => { + console.log(e); +}); + +//Request completion call +method.finally(() => { + console.log('Request completed'); +}); +``` + +In addition, requests can also be sent through the `await method`. + +## new Method() + +Create a custom method instance. + +- **type** + +```ts +interface MethodConstructor { + new ( + type: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'HEAD' | 'OPTIONS' | 'PATCH', + context: Alova, + url: string, + config?: AlovaMethodCreateConfig, + data?: Arg | string | FormData | Blob | ArrayBuffer | URLSearchParams | ReadableStream + ): Method; + readonly prototype: Method; +} +``` + +- **Parameters** + +1. `type`: request type +2. `context`: alova instance +3. `url`: request url +4. `config`: Configuration parameters, the type is the same as config parameter type of [alova.Get](/api/alova#alovaget) +5. `data`: request body data + +- **Example** + +```ts +import { Method } from 'alova'; +import { alovaInstance } from './api'; + +const method = new Method('GET', alovaInstance, '/api/users', { + params: { + ID: 1 + } +}); +``` + +## getMethodKey() + +Get the key value of method. This key value is used as alova internal cache key. + +- **type** + +```ts +function getMethodKey(method: Method): string; +``` + +- **Parameters** + +1. `method`: method instance + +- **return** + +The key value of the method instance passed in. + +- **Example** + +```ts +import { getMethodKey } from 'alova'; + +const method = alova.Get('/api/users'); +const methodKey = getMethodKey(method); +``` + +## matchSnapshotMethod() + +Obtain the requested method instance snapshot using the matching method of [method instance matcher](/tutorial/advanced/method-matcher) and return the matching result. + +- **type** + +```ts +type MethodFilter = + | string + | RegExp + | { + name?: string | RegExp; + filter?: MethodFilterHandler; + alova?: Alova; + }; +function matchSnapshotMethod(matcher: MethodFilter, matchAll?: boolean): Method[] | Method | undefined; +``` + +- **Parameters** + +1. `matcher`: method instance matcher +2. `matchAll`: Whether to match all, the default is true + +- **return** + +Returns an array of method instances when `matchAll` is true, otherwise returns a method instance or undefined + +- **Example** + +```ts +import { matchSnapshotMethod } from 'alova'; + +await alova.Get('/api/users'); +const snapshotMethod = matchSnapshotMethod({ + name: 'user', + filter(method, i, methodArray) { + return method.url.includes('users'); + }, + alova: alova +}); +``` + +## method.headers + +Request header. + +- **type** + +```ts +interface Method { + headers?: any; +} +``` + +## method.baseURL + +The base path of the request, inherited from [alova instance](/api/alova). + +- **type** + +```ts +interface Method { + baseURL: string; +} +``` + +## method.url + +Create the url of the method instance. + +- **type** + +```ts +interface Method { + url: string; +} +``` + +## method.type + +Request type. + +- **type**: + +```ts +interface Method { + type: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'HEAD' | 'OPTIONS' | 'PATCH'; +} +``` + +## method.data + +Request body. + +- **type** + +```ts +interface Method { + data?: any; +} +``` + +## method.context + +Create an alova instance of the current method. + +- **type** + +```ts +interface Method { + context: Alova; +} +``` + +## method.hitSource + +Hitting the source method instance, when the source method instance request succeeds, the cache of the current method instance will be invalidated. As an automatic invalidation function, you only need to set the hit source instead of manually calling `invalidateCache` to invalidate the cache. In addition, this function is more concise and effective than the `invalidateCache` method in complex invalidation relationships. The field value can be set to the name of the method instance, other method instances, name regular matching, or their array. + +- **type** + +```ts +interface Method { + hitSource?: Method | string | RegExp | (Method | string | RegExp)[]; +} +``` + +## method.meta + +The metadata of method is used to record request feature information, [View details](/tutorial/getting-started/method-metadata). + +- **type** + +```ts +interface Method { + meta?: any; +} +``` + +## method.config + +Configuration information when creating a method through `alova.Get/alova.Post` and other methods, [View details](/api/alova#alovaget). + +- **type** + +```ts +interface Method { + config: AlovaMethodCreateConfig; +} +``` + +## method.fromCache + +Is the response of current request from cache. + +- **type** + +```ts +interface Method { + fromCache: boolean; +} +``` + +## method.send() + +Use this method instance to send the request directly. If you send the request after `[v2.16.0]`, you can omit calling this method. + +- **type** + +```ts +interface Method { + send(forceRequest?: boolean): Promise; +} +``` + +- **Parameters** + +1. `forceRequest`: whether to force the request, the default is false + +- **return** + +A Promise instance with response data. + +- **Example** + +```ts +const method = alova.Get('/api/users'); +const response = await method.send(); +``` + +## method.abort() + +Abort the current request. + +- **type** + +```ts +interface Method { + abort(): void; +} +``` + +- **Example** + +```ts +const method = alova.Get('/api/users'); +method.abort(); +``` + +## method.then() + +After `[v2.16.0]`, the method instance is a PromiseLike instance. You can directly call this method or `await method` to send a request and obtain the response data. + +- **type** + +```ts +interface Method { + then(onFulfilled?: (value: Response) => any, onRejected?: (reason: any) => any): Promise; +} +``` + +- **Parameters** + +1. `onFulfilled`: callback function when the request is successful +2. `onRejected`: callback function when the request fails + +- **return** + +A Promise instance with response data. + +- **Example** + +```ts +const method = alova.Get('/api/users'); +const response = await method; +``` + +## method.catch() + +After `[v2.16.0]`, the method instance is a PromiseLike instance. This method can be called directly to send requests and catch errors. + +- **type** + +```ts +interface Method { + catch( + onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null + ): Promise; +} +``` + +- **Parameters** + +1. `onrejected`: callback function when request error + +- **return** + +Promise instance. + +- **Example** + +```ts +const method = alova.Get('/api/users'); +const response = await method.catch(error => { + console.error('Request error'); +}); +``` + +## method.finally() + +After `[v2.16.0]`, the method instance is a PromiseLike instance. This method can be called directly to send the request and handle the response completion. + +- **type** + +```ts +interface Method { + finally(onfinally?: (() => void) | undefined | null): Promise; +} +``` + +- **return** + +Promise instance. + +- **Example** + +```ts +const method = alova.Get('/api/users'); +const response = await method.finally(() => { + console.log('Request completed'); +}); +``` + +## method.onDownload() + +Bind the download event to obtain download progress information. + +- **type** + +```ts +interface Method { + onDownload(handler: ProgressHandler): () => void; +} +``` + +- **Parameters** + +1. `handler` download event callback function + +- **return** + +unbind function + +- **Example** + +```ts +const method = alova.Get('/api/download_file'); +const offEvent = method.onDownload(event => { + console.log('File size:', event.total); + console.log('Downloaded:', event.loaded); +}); + +offEvent(); +``` + +## method.onUpload() + +Bind the upload event to obtain upload progress information. + +- **type** + +```ts +interface Method { + onUpload(handler: ProgressHandler): () => void; +} +``` + +- **Parameters** + +1. `handler` upload event callback function + +- **return** + +unbind function + +- **Example** + +```ts +const method = alova.Get('/api/upload_file', formData); +const offEvent = method.onUpload(event => { + console.log('File size:', event.total); + console.log('Uploaded:', event.loaded); +}); + +offEvent(); +``` + +## method.setName() + +Set the name of the method instance. + +- **type** + +```ts +interface Method { + setName(name: string | number): void; +} +``` + +- **Parameters** + +1. `name`: the name of the method instance + +- **return** + +none + +- **Example** + +```ts +const method = alova.Get('/api/users', { + name: 'user' +}); +method.setName('newUser'); +``` diff --git a/versioned_docs/version-2.x/api/03-core-hooks.md b/versioned_docs/version-2.x/api/03-core-hooks.md new file mode 100644 index 000000000..99327dd7a --- /dev/null +++ b/versioned_docs/version-2.x/api/03-core-hooks.md @@ -0,0 +1,268 @@ +--- +title: Core useHooks +sidebar_position: 30 +--- + +## useRequest + +Represents the sending of a request. When executing useRequest, a request will be sent by default, and stateful request-related data will be created and maintained, such as `loading/data/error`, etc. It is the most commonly used method when obtaining initial data on the page. It also supports turning off its default request sending, which is very useful in request scenarios triggered by click events such as submitting data. + +> Go to [Send Request](/tutorial/combine-framework/use-request) for details. + +### type + +```ts +function useRequest( + methodHandler: Method | (...args: any[]) => Method, + config?: RequestHookConfig +): UseHookReturnType; +``` + +### Parameters + +1. `methodHandler`: can be passed in two forms: method instance and function. When specified as a function, it can receive parameters passed in by `send` and require a method instance to be returned. +2. `config`: hook configuration parameters. + +| Name | Description | Type | Default | Version | +| ------------- | -------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ------- | ------- | +| immediate | Whether to initiate the request immediately | boolean | true | - | +| initialData | The initial data value. The data value is the initial value before the first response. If it is not set, it is `undefined` | any | - | - | +| force | Whether to force the request, it can be set to the function to dynamically return a boolean value | boolean \| (...args: any[]) => boolean \| false | - | - | +| managedStates | Additional managed states, which can be updated via updateState | Record\ | - | - | +| middleware | Middleware function, [Understanding alova middleware](/tutorial/advanced/middleware) | (context: [AlovaFrontMiddlewareContext](#alovafrontmiddlewarecontext), next: [AlovaGuardNext](#alovaguardnext)) => Promise\ | - | - | + +#### AlovaFrontMiddlewareContext + +| Name | Description | Type | Version | +| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | +| method | The method object of the current request | Method | - | +| cachedResponse | Hit cached data | any | - | +| config | Current use hook configuration | Record\ | - | +| sendArgs | Parameters of the response processing callback, which are passed in by send of use hooks | any[] | - | +| frontStates | use hook front-end state collection, such as data, loading, error, etc. | [FrontRequestState](#frontrequeststate) | - | +| send | Send request function | (...args: any[]) => Promise | - | +| abort | abort function | () => void | - | +| decorateSuccess | Decorate success callback function | (decorator: (
handler: (event: [AlovaSuccessEvent](#alovasuccessevent)) => void,
event: [AlovaSuccessEvent](#alovasuccessevent),
index: number,
length: number
) => void) => void | - | +| decorateError | Decoration failure callback function | (decorator: (
handler: (event: [AlovaErrorEvent](#alovaerrorevent)) => void,
event: [AlovaErrorEvent](#alovaerrorevent),
index: number,
length: number
) => void) => void | - | +| decorateComplete | Decoration completion callback function | (decorator: (
handler: (event: [AlovaCompleteEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovacompleteevent),
index: number,
length: number
) => void) => void | - | +| update | Function to update the current use hook front-end state, more useful in react | (newFrontStates: [FrontRequestState](#frontrequeststate)) => void; | - | +| controlLoading | Customize the loading state of the control. The call will no longer trigger changes in the loading state. When the incoming control is false, the control will be cancelled. | (control?: boolean) => void | - | + +#### AlovaGuardNext + +```typescript +type AlovaGuardNext = (guardNextConfig?: { + force?: boolean | (...args: any[]) => boolean; + method?: Method; +}): Promise; +``` + +#### FrontRequestState + +The following attribute values will automatically infer the responsive data type of the corresponding UI framework based on `statesHook`, which is the `Ref` type in vue3, the normal value in react, and the `Writable` type in svelte + +| Name | Description | Type | Version | +| ----------- | ----------------------------- | ------------------ | ------- | +| loading | request loading status | boolean | - | +| data | response data | any | - | +| error | Request error information | Error \| undefined | - | +| downloading | Download progress information | Object | - | +| uploading | Upload progress information | Object | - | + +#### AlovaSuccessEvent + +| Name | Description | Type | Version | +| --------- | ---------------------------------------------------------------------------------------- | ------- | ------- | +| method | The method object of the current request | Method | - | +| sendArgs | Parameters of the response processing callback, which are passed in by send of use hooks | any[] | - | +| data | response data | any | - | +| fromCache | Whether the response data comes from cache | boolean | - | + +#### AlovaErrorEvent + +| Name | Description | Type | Version | +| -------- | ---------------------------------------------------------------------------------------- | ------ | ------- | +| method | The method object of the current request | Method | - | +| sendArgs | Parameters of the response processing callback, which are passed in by send of use hooks | any[] | - | +| error | Response error instance | Error | - | + +#### AlovaCompleteEvent + +| Name | Description | Type | Version | +| --------- | ---------------------------------------------------------------------------------------- | -------------------- | ------- | +| method | The method object of the current request | Method | - | +| sendArgs | Parameters of the response processing callback, which are passed in by send of use hooks | any[] | - | +| status | Response status, success when successful, error when failure | 'success' \| 'error' | - | +| data | response data, with value when successful | any | - | +| fromCache | Whether the response data comes from the cache, a value if successful | boolean | - | +| error | Response error instance, with value in case of failure | Error | - | + +### return value + +`UseHookReturnType` contains response data and request-related states, operation functions and event binding functions. They will automatically infer the responsive data type of the corresponding UI framework based on `statesHook`, which is the `Ref` type in vue3 and the `Ref` type in react. It is a normal value and is of `Writable` type in svelte. + +#### Responsive data + +| Name | Description | Type | Version | +| ----------- | ----------------------------- | ------------------ | ------- | +| loading | request loading status | boolean | - | +| data | response data | any | - | +| error | Request error information | Error \| undefined | - | +| downloading | Download progress information | Object | - | +| uploading | Upload progress information | Object | - | + +#### Operation function + +| name | description | function parameters | return value | version | +| ------ | ----------------------------------------------------------------------------- | ------------------------------------------------------- | ------------ | ------- | +| send | Send request function | ...args: any[] | - | - | +| abort | abort function | - | Promise | - | +| update | Function to update the current use hook front-end state, more useful in react | newFrontStates: [FrontRequestState](#frontrequeststate) | - | + +#### event + +| name | description | callback parameters | version | +| ---------- | -------------------------------- | ------------------------------------------------ | ------- | +| onSuccess | Request success event binding | event: [AlovaSuccessEvent](#alovasuccessevent) | - | +| onError | Request error event binding | event: [AlovaErrorEvent](#alovaerrorevent) | - | +| onComplete | Request completion event binding | event: [AlovaCompleteEvent](#alovacompleteevent) | - | + +## useWatcher + +Monitor the status and initiate a request after the status changes. In some scenarios that require re-requesting as the data changes, such as paging, data filtering, and fuzzy search. + +> Go to [State Change Request](/tutorial/combine-framework/use-watcher) for details. + +### type + +```typescript +function useWatcher( + handler: Method | (...args: any[]) => Method, + watchingStates: State[], + config?:WatcherHookConfig +): UseHookReturnType; +``` + +### Parameters + +1. `handler`: can be passed in two forms: method instance and function. When specified as a function, it can receive parameters passed in by `send` and require a method instance to be returned. +2. `config`: hook configuration parameters. + +| Name | Description | Type | Default | Version | +| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- | ---------- | ------- | +| immediate | Whether to initiate the request immediately | boolean | true | - | +| initialData | The initial data value. The data value is the initial value before the first response. If it is not set, it is `undefined` | any | - | - | +| force | Whether to force the request, it can be set to the function to dynamically return a boolean value | boolean \| (...args: any[]) => boolean \| false | +| managedStates | Additional managed states, which can be updated via updateState | Record\ | - | +| debounce | Request debounce time (milliseconds), when passing in the array, you can set the debounce time individually in the order of watchingStates | number \| number[] | - | +| middleware | Middleware function, [Understanding alova middleware](/tutorial/advanced/middleware) | (context: [AlovaFrontMiddlewareContext](#alovafrontmiddlewarecontext), next: [AlovaGuardNext](#alovaguardnext)) => Promise\ | - | - | +| sendable | Whether to send a request when the monitored state changes | (methodInstance: AlovaEvent) => boolean | () => true | - | +| abortLast | Whether to abort the last unresponsive request | boolean | true | - | + +### return value + +`UseHookReturnType` contains response data and request-related states, operation functions and event binding functions. They will automatically infer the responsive data type of the corresponding UI framework based on statesHook. It is Ref type in vue3 and ordinary value in react. In svelte it is Writable type. + +#### Responsive data + +| Name | Description | Type | Version | +| ----------- | ----------------------------- | ------------------ | ------- | +| loading | request loading status | boolean | - | +| data | response data | any | - | +| error | Request error information | Error \| undefined | - | +| downloading | Download progress information | Object | - | +| uploading | Upload progress information | Object | - | + +#### Operation function + +| name | description | function parameters | return value | version | +| ------ | ----------------------------------------------------------------------------- | ------------------------------------------------------- | ------------ | ------- | +| send | Send request function | ...args: any[] | Promise | - | +| abort | abort function | - | - | - | +| update | Function to update the current use hook front-end state, more useful in react | newFrontStates: [FrontRequestState](#frontrequeststate) | - | + +#### event + +| name | description | callback parameters | version | +| ---------- | -------------------------------- | ------------------------------------------------ | ------- | +| onSuccess | Request success event binding | event: [AlovaSuccessEvent](#alovasuccessevent) | - | +| onError | Request error event binding | event: [AlovaErrorEvent](#alovaerrorevent) | - | +| onComplete | Request completion event binding | event: [AlovaCompleteEvent](#alovacompleteevent) | - | + +## useFetcher + +It is used to pull data through `useFetcher`, which is useful when preloading data and updating status across modules. + +> Go to [Data Fetching](/tutorial/advanced/use-fetcher) to view details. + +### type + +```typescript +function useFetcher(config?: FetcherHookConfig): UseFetchHookReturnType; +``` + +### Parameters + +1. `config`: hook configuration parameters. + +| Name | Description | Type | Default | Version | +| ---------- | ------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ | ------- | +| force | Whether to force the request, it can be set to the function to dynamically return a boolean value | boolean | (...args: any[]) => boolean \| false | - | +| middleware | Middleware function, [Understand alova middleware](/tutorial/advanced/middleware) | (context: [AlovaFetcherMiddlewareContext](#alovafetchermiddlewarecontext), next: [AlovaGuardNext](#alovaguardnext)) => Promise\ | - | - | + +#### AlovaFetcherMiddlewareContext + +| Name | Description | Type | Version | +| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | +| method | The method object of the current request | Method | - | +| cachedResponse | Hit cached data | any | - | +| config | Current use hook configuration | Record\ | - | +| fetchArgs | Parameters of the response processing callback, which are passed in by fetch of useFetcher | any[] | - | +| fetchStates | use hook preload state collection, such as fetching, error, etc. | [FetchRequestState](#fetchrequeststate) | - | +| fetch | Data preloading function | (method: Method, ...args: any[]) => Promise | - | +| abort | abort function | () => void | - | +| decorateSuccess | Decorate success callback function | (decorator: (
handler: (event: [AlovaSuccessEvent](#alovasuccessevent)) => void,
event: [AlovaSuccessEvent](#alovasuccessevent),
index: number,
length: number
) => void) => void | - | +| decorateError | Decoration failure callback function | (decorator: (
handler: (event: [AlovaErrorEvent](#alovaerrorevent)) => void,
event: [AlovaErrorEvent](#alovaerrorevent),
index: number,
length: number
) => void) => void | - | +| decorateComplete | Decoration completion callback function | (decorator: (
handler: (event: [AlovaCompleteEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovacompleteevent),
index: number,
length: number
) => void) => void | - | +| update | Function to update the current use hook preloading state, more useful in react | (newFrontStates: [FetchRequestState](#fetchrequeststate)) => void; | - | +| controlFetching | After calling, the state of fetching will be customized and the change of fetching state will no longer be triggered internally. When the incoming control is false, the control will be canceled | (control?: boolean) => void | - | + +#### FetchRequestState + +The following attribute values will automatically infer the responsive data type of the corresponding UI framework based on `statesHook`, which is the `Ref` type in vue3, the normal value in react, and the `Writable` type in svelte + +| Name | Description | Type | Version | +| ----------- | ----------------------------- | ------------------ | ------- | +| fetching | preloading request status | boolean | - | +| error | Request error information | Error \| undefined | - | +| downloading | Download progress information | Object | - | +| uploading | Upload progress information | Object | - | + +### return value + +`UseFetchHookReturnType` contains request-related states, operation functions and event binding functions. They will automatically infer the responsive data type of the corresponding UI framework based on statesHook. It is Ref type in vue3, ordinary value in react, and ordinary value in svelte. For Writable type. + +#### Responsive data + +| Name | Description | Type | Version | +| ----------- | ----------------------------- | ------------------ | ------- | +| fetching | preloading request status | boolean | - | +| error | Request error information | Error \| undefined | - | +| downloading | Download progress information | Object | - | +| uploading | Upload progress information | Object | - | + +#### Operation function + +| name | description | function parameters | return value | version | +| ------ | ----------------------------------------------------------------------------- | ---------------------------------------------------------- | ------------ | ------- | +| fetch | Data preloading function | 1. method: preloaded Method instance
2. ...args: any[] | Promise | - | +| abort | abort function | - | - | - | +| update | Function to update the current use hook front-end state, more useful in react | newFrontStates: [FrontRequestState](#frontrequeststate) | - | + +#### event + +| name | description | callback parameters | version | +| ---------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------- | ------- | +| onSuccess | Request success event binding | event: [AlovaSuccessEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovasuccessevent) | - | +| onError | Request error event binding | event: [AlovaErrorEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovaerrorevent) | - | +| onComplete | Request completion event binding | event: [AlovaCompleteEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovacompleteevent) | - | diff --git a/versioned_docs/version-2.x/api/04-cache.md b/versioned_docs/version-2.x/api/04-cache.md new file mode 100644 index 000000000..86414f8e0 --- /dev/null +++ b/versioned_docs/version-2.x/api/04-cache.md @@ -0,0 +1,133 @@ +--- +title: Cache operation +sidebar_position: 40 +--- + +## invalidateCache() + +Active cache invalidation. + +> Go to [Manually invalidate cache](/tutorial/cache/manually-invalidate) for details. + +- **type** + +```ts +type MethodFilter = + | string + | RegExp + | { + name?: string | RegExp; + filter?: MethodFilterHandler; + alova?: Alova; + }; +function invalidateCache(matcher?: Method | Method[] | MethodFilter): void; +``` + +- **Parameters** + +1. `matcher`: Cache invalid matcher, the value is a method instance or array, or it can be set to [method instance matcher](/tutorial/advanced/method-matcher). + +- **return** + +none + +- **Example** + +```ts +import { invalidateCache } from 'alova'; + +invalidateCache(method); +invalidateCache([method1, method2]); +invalidateCache({ + name: 'userMethod', + filter: method => method.name === 'method1' +}); +``` + +## setCache() + +Set up response caching. + +> Go to [Cache Update and Query](/tutorial/cache/set-and-query) for details. + +- **type** + +```ts +type MethodFilter = + | string + | RegExp + | { + name?: string | RegExp; + filter?: MethodFilterHandler; + alova?: Alova; + }; +function setCache( + matcher: Method | Method[] | MethodFilter, + dataOrUpdater: R | ((oldCache: R) => R | undefined | void) +): void; +``` + +- **Parameters** + +1. `matcher`: The value is method instance, method name string, method name regular expression. It can also be set to [method instance matcher](/tutorial/advanced/method-matcher), which will match all matching The method instance of the condition sets the cached data. +2. `dataOrUpdater`: Cache data or update function. If it is a function, it needs to return new cached data. If it returns `undefined` or does not return, the update will be cancelled. + +- **return** + +none + +- **Example** + +```ts +import { setCache } from 'alova'; + +setCache(method, {}); +setCache([method1, method2], {}); +setCache( + { + name: 'userMethod', + filter: method => method.name === 'method1' + }, + {} +); +``` + +## queryCache() + +Query cache. + +> Go to [Cache Update and Query](/tutorial/cache/set-and-query) for details. + +- **type** + +```ts +type MethodFilter = + | string + | RegExp + | { + name?: string | RegExp; + filter?: MethodFilterHandler; + alova?: Alova; + }; +function queryCache(matcher?: Method | MethodFilter): R | undefined; +``` + +- **Parameters** + +1. `matcher`: The value is method instance, method name string, method name regular expression. It can also be set to [method instance matcher](/tutorial/advanced/method-matcher), which will meet the conditions. The first method instance queries cached data. + +- **return** + +Cache data, or return `undefined` if not cached. + +- **Example** + +```ts +import { queryCache } from 'alova'; + +const responseCache = queryCache(method); +const responseCache = queryCache({ + name: 'userMethod', + filter: method => method.name === 'method1' +}); +``` diff --git a/versioned_docs/version-2.x/api/05-states.md b/versioned_docs/version-2.x/api/05-states.md new file mode 100644 index 000000000..a32f22de5 --- /dev/null +++ b/versioned_docs/version-2.x/api/05-states.md @@ -0,0 +1,120 @@ +--- +title: Response states operation +sidebar_position: 50 +--- + +## updateState + +Manually update existing response data or additional status under any module/page. + +**⚠️ Please make sure the component is not destroyed** + +By default, `updateState` will look for the response state created by alova's useHooks when sending a request, but to prevent memory overflow, the destruction of a component will also recycle all the states created internally, so please make sure you use `updateState` It is hoped that the container component corresponding to the updated response status has not been destroyed, otherwise the corresponding response status will not be found and the update will fail. + +This problem often occurs when updating status across pages, because what we tend to overlook when the page jumps is that the previous page has been destroyed by default. Therefore, if you want to update status across pages, here are two suggestions : + +1. Persist the page components to ensure that the updated status can still be found; +2. Use [setCache](/tutorial/cache/set-and-query) instead of `updateState`. The principle is that when the request for the previous page exists in the cache, update its cache to ensure that when the page is created again, the The request can hit the updated cache to achieve the same effect. + +> Go to [Cross-page/module update response states](/tutorial/advanced/update-across-components) for details. + +> To use updateState to manage extra states, please refer to [Extra State Management](/tutorial/advanced/manage-extra-states). + +- **type** + +```ts +type MethodFilter = + | string + | RegExp + | { + name?: string | RegExp; + filter?: MethodFilterHandler; + alova?: Alova; + }; +function updateState( + matcher: Method | MethodFilter, + handleUpdate: UpdateStateCollection['data'] | UpdateStateCollection, + options?: UpdateOptions +): boolean; +``` + +- **Parameters** + +- `matcher`: The value is method instance, method name string, method name regular expression, it can also be set to [method instance matcher](/tutorial/advanced/method-matcher), if it matches the qualified method, `handleUpdate` will be called. +- `handleUpdate`: update function or update function collection. If it is a function collection, the corresponding update function on the collection will be called and the return value will be used as the update result. +- `options`: optional options. + +| Parameter name | Type | Description | +| -------------- | -------- | -------------------------------------------------- | +| onMatch | Function | After matching method, the function will be called | + +- **return** + +Whether the update is successful. + +- **Example** + +Use `useRequest` in page or component A to send a request and get the response data. + +```ts +const { data } = useRequest( + alova.Get('/api/user', { + name: 'user' + }) +); +``` + +Use `updateState` in page or component B to update the response state. + +```ts +import { updateState } from 'alova'; + +// Match by method instance +updateState(alova.Get('/api/user'), oldData => { + return [ + ...oldData, + { + id: 10000, + name: 'Alova', + }, + ] +}); + +// Match by method name +updateState('user', oldData => { + return [ + ...oldData, + { + id: 10000, + name: 'Alova', + }, + ]; +} + +// Regular matching through method name +updateState(/^us/, oldData => { + return [ + ...oldData, + { + id: 10000, + name: 'Alova', + }, + ]; +}) + +// Match through method instance matcher +updateState({ + name: 'user', + filter(method, i, methods) { + return methods.length === i + 1; + } +}, oldData => { + return [ + ...oldData, + { + id: 10000, + name: 'Alova', + }, + ]; +}); +``` diff --git a/versioned_docs/version-2.x/api/06-global-config.md b/versioned_docs/version-2.x/api/06-global-config.md new file mode 100644 index 000000000..d359d10c6 --- /dev/null +++ b/versioned_docs/version-2.x/api/06-global-config.md @@ -0,0 +1,36 @@ +--- +title: Global configuration +sidebar_position: 60 +--- + +## globalConfig() + +Global configuration. + +- **type** + +```ts +function globalConfig(config: AlovaGlobalConfig): void; +``` + +- **Parameters** + +1. config: configuration + +| Parameter name | Type | Description | +| -------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------- | +| limitSnapshots | number | method snapshot number limit, set to 0 to disable saving snapshots. After closing, the method snapshot matcher will be unavailable | + +- **return** + +none + +- **Example** + +```ts +import { globalConfig } from 'alova'; + +globalConfig({ + limitSnapshots: 10 +}); +``` diff --git a/versioned_docs/version-2.x/api/_category_.json b/versioned_docs/version-2.x/api/_category_.json new file mode 100644 index 000000000..4b4861e39 --- /dev/null +++ b/versioned_docs/version-2.x/api/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "API", + "link": { + "type": "generated-index" + } +} diff --git a/versioned_docs/version-2.x/contributing/01-overview.md b/versioned_docs/version-2.x/contributing/01-overview.md new file mode 100644 index 000000000..4a74bfb3d --- /dev/null +++ b/versioned_docs/version-2.x/contributing/01-overview.md @@ -0,0 +1,175 @@ +--- +title: Contribution Guidelines +sidebar_position: 10 +--- + +# alova Contribution Guidelines + +Hello, I'm glad to meet you here. This is a detailed alova contribution guidelines, which includes detailed guidance on contributing to all aspects of alova. Please continue reading. + +## Preface + +In the past period of time, we have received active participation information from developers from all over the world in Github issues and Github Discussion, which means that alova is being loved by more and more developers. Even so, alova is still a rookie, and it still has a long way to go. + +**We expect to make alova a common project for everyone who is willing to participate, and we encourage everyone to become a contributor to the alova community with an open and inclusive attitude. Moreover, we believe that contributing to alova is not limited to code contributions, but participating in any activities that are beneficial to the development of alova is considered to contribute to alova.** Participating in contributions now can win you more effective contribution opportunities, and it allows you to contribute to the world Even if you are a junior developer, as long as the idea is in line with [alova's mission and design principles](#alova-mission-and-design-principles), please generously participate! + +> There is a [Code of conduct](./code-of-conduct) here, please refer to it. + +## Contribution directory + +Here are 13 places where you can contribute, but not limited to these, you can choose the part you want to participate in, and link to the corresponding location for detailed reading: + +- [use alova in your project](#use-alova-in-your-project) +- [star alova](#star-alova) +- [report bug](#report-bug) +- [Propose new feature ideas](#propose-new-feature-ideas) +- [Pull Request](#pull-request) +- [Create an adapter or strategy library based on alova](#create-an-adapter-or-strategy-library-based-on-alova) +- [Participate in community/PR review](#participate-in-community-pr-review) +- [Publish and disseminate information about alova](#publish-and-disseminate-information-about-alova) +- [Share experience](#share-experience) +- [Collaboration](#collaboration) +- [Donation](#donation) +- [Correct or add docs](#correct-or-add-docs) +- [Translate docs](#Translate-docs) + +## alova mission and design principles + +### alova Mission + +alova's mission gives it a clear development direction, which clearly defines what alova should do. + +alova is a lightweight request strategy library, **Its mission is to enable developers to achieve more efficient Client-Server data interaction by less codes**. + +For developers, alova provides them with a simple API and out-of-the-box advanced request functions, as well as various simple and high-performance request strategy modules. For application users, they can enjoy the benefits of alova The smooth experience brought by high-performance data interaction, therefore, alova has the following features: + +1. The api design is similar to axios, allowing users to learn at a lower cost; +2. Deep binding of the UI framework, greatly improving the benefits of developers; +3. Out-of-the-box advanced functions to avoid repeated packaging, such as request sharing, request caching, etc., to reduce developers' repeated packaging; +4. The platform-independent coding way, and it can perfectly migrate in different platform; +5. High scalability design, which can encapsulate high-reusability and high-performance business-related request strategies; +6. Highly aggregated and low-coupling method design improves API code maintainability; + +### alova design principles + +The design principles points out how it should be designed, the following is the core design concept of alova. + +1. `Method` proxy design, high aggregation, platform-independent design, throughout the request, you should be able to access it in any request function, from another perspective, the information related to the request should also be placed in the method instance ; +2. Lightweight, try to keep the source code concise in coding, such as avoiding repeated code, merging variable declarations, prototype chain function encapsulation, no similar API, tree shaking, but long variable names are allowed, because it will be used when compiling will be replaced by a single letter; +3. Highly scalable design. First, the design of alova uses a large number of adapter patterns and hook functions. For example, adapters include `requestAdapter`, `storageAdapter`, etc., and hook functions include `beforeRequest`, `responded`, `transformData`, `localCache`, etc., and most of them have default behaviors, which are designed to be easy to use while retaining high scalability; second, global request parameters can be overwritten, such as `timeout`, `shareRequest`, etc., for These parameters can be set individually for special requests. +4. The api design is universal. First, it means that the function of this api has a high level of abstraction, rather than a specific business. Second, the api design is scalable to adapt to the needs of the api iteration + +> The api universal design is only applicable to the alova library. If you are conceiving a request strategy, you can design it according to the specific business. + +## Select the contribution point you are interested in + +### Use alova in your project + +We believe that your use of alova in the project is also a contributor to alova, which is also telling people that alova is a trustworthy open source project, please open [this issue](https://github.com/alovajs/alova/issues/165), which may give you the opportunity to display your project on the alova official website. + +### Star alova + +Although this may be considered insignificant, it represents your recognition of alova, and every star is very important to alova. star alova in the right top of [alova's Github repository](https://github.com/alovajs/alova), which is important to us. + +### Report bug + +Please move to [Github new issue](https://github.com/alovajs/alova/issues/new/choose) and select the corresponding template to submit. Detailed instructions will be displayed in the submitted issue. + +**PLEASE NOTE:** If you want to ask questions about alova, please go to [Github Discussion](https://github.com/alovajs/alova/discussions) to create a question. That would be closed immediately when ask a question in issues. + +### Propose new feature ideas + +In order for alova to achieve its value and goals, before submitting a new feature idea, please carefully read [alova mission and design principles](#alova-mission-and-design-principles), and ensure that your new idea is in line with alova's mission and design philosophy design concept. + +Then, please submit it in [🚀 New Feature Proposal](https://github.com/alovajs/alova/issues/new?assignees=&labels=feature-request&projects=&template=FEATURE_REQUEST_en.yml), the detailed description will be Displayed when submitting an issue. + +### Pull Request + +You can contribute the following 3 aspects of code through pull request. If you are a new partner who is interested in participating, all `good first issue` issues are listed in [Github contribution list](https://github.com/alovajs/alova/contribute), it is used to Tell new partners who are interested in contributing that this is a good start. + +#### Bug fix + +Issues marked as [`bug:confirmed`](https://github.com/alovajs/alova/labels/bug%3Aconfirmed) in Github issues are all confirmed bugs, you can choose freely. + +If you encounter a bug yourself, please also [report a bug](#report-bug) first to ensure that the bug is confirmed to avoid invalid pull requests. + +#### New feature development + +Issues marked as [`feature-request:confirmed`](https://github.com/alovajs/alova/labels/feature-request%3Aconfirmed) in Github issues are new features that have been confirmed, you You can choose freely. + +If you have an idea for adding a new feature, please also [submit an issue of a new feature idea](#propose-new-feature-ideas) to ensure that the idea is confirmed to avoid invalid pull requests. + +#### Project configuration + +If you are very good at project configuration and find deficiencies in the alova project, such as incomplete configuration, too old configuration version, insufficient automation (including project development automation and Github warehouse management automation), you can also press [New Feature development](#new-feature-development) process to contribute. + +:::warning IMPORTANT + +1. Please read the [Developing Guidelines](./developing-guidelines) carefully before developing, it can guide you step by step on how to contribute code. +2. When you identify an issue that needs to be resolved, please make sure that it has not been flagged by someone else's pull request, which means that it has been pre-occupied. + +::: + +### Create an adapter or strategy library based on alova + +alova provides high-extensibility features, and you can write your own js library based on it. + +#### Custom Adapter + +Customize various adapters to meet the operating requirements in different environments. The following directions are available for reference: + +1. Customize statesHook, which can be executed under different UI frameworks, such as `solid/qwik`, currently supports `react/vue/svelte`, please read [Custom statesHook](/tutorial/custom/custom-stateshook); +2. Customize the request adapter, so that alova can cooperate with more request schemes, such as `GraphQL/SSE`, etc.; +3. Customize the storage adapter to meet the storage requirements of different environments, such as `react-native`; +4. Any combination of the above, such as the official [uniapp adapter](https://github.com/alovajs/adapter-uniapp), which includes request adapters and storage adapters. + +#### Custom request strategy + +Request strategies can help developers write high-performance functions more efficiently. Although the official [@alova/scene](/tutorial/strategy) provides some common request strategies, it is not enough It is a good choice to customize your own reusable request strategy based on alova to meet the business scenarios related to various requests of developers, and you can also publish them on npm for everyone to use. + +:::tip Submit your project + +If you have written an alova-based js library, please submit your project in [this issue](https://github.com/alovajs/alova/issues/165), which will allow your project to be displayed on the alova official website Opportunity. + +::: + +### Participate in community/PR review + +If you are interested in technical communication, then it may be more suitable for you to participate in more community communication. You can participate in the discussion of bugs and new features in Github issues, or in [Github Discussion](https://github.com/alovajs/alova/discussions), [Discord](https://discord.gg/S47QGJgkVb) or [QQ channel](https://pd.qq.com/s/1cdjx0nnw) to answer questions for others, which allows you to communicate with people from all over the world, which is a very interesting thing. + +At the same time, you can also participate in PR review in [pull request](https://github.com/alovajs/alova/pulls), which is also a topic of communication. + +### Publish and disseminate information about alova + +You can publish or repost any information that is beneficial to the development of alova on any social platform, short video platform, or technology sharing platform, which will help increase the influence of alova. We will filter out relevant articles or videos and display them on the alova official website. Here are some good articles: + +- [It’s time to replace your axios](https://medium.com/@huzhen555/its-time-to-replace-your-axios-12c014833b04) +- [Alova.js 筆記-試用相較 axios 更輕量、更高集成的請求庫](https://uu9924079.medium.com/alova-js-%E7%AD%86%E8%A8%98-%E8%A9%A6%E7%94%A8%E7%9B%B8%E8%BC%83-axios-%E6%9B%B4%E8%BC%95%E9%87%8F-%E6%9B%B4%E9%AB%98%E9%9B%86%E6%88%90%E7%9A%84%E8%AB%8B%E6%B1%82%E5%BA%AB-546ec5424df9) + +### Share experience + +If you have alova experience worth sharing, or better practice cases, you can share it in [Github Discussion's Practices channel](https://github.com/alovajs/alova/discussions/categories/practices), better The sharing of will also be displayed in the official documentation. + +### Collaboration + +We welcome project collaboration with any organization or individual, which can help us expand alova's influence and accelerate the development of the project. If you have any suggestions or intentions for cooperation, please contact us via email **hujou555@gmail.com**. + +### Donation + +You can donate to the project through the following 3 ways. Please go to the donation page to view the donation privileges. + +1. [Github sponsors](https://github.com/sponsors/alovajs) +2. [OpenCollective](https://opencollective.com/alova) +3. [afdian](https://afdian.net/a/huzhen555) + +### Correct or add docs + +If you need to add new documentation content, or find errors in alova's documentation, such as wrong examples, wrong words, incorrect descriptions, or unmentioned content, you can [create a new document repository issue](https://github.com/alovajs/alovajs.github.io/issues/new), or [new document warehouse pull request](https://github.com/alovajs/alovajs.github.io/fork) directly modify the error, which should be the better option, and we welcome any suggestions or contributions to improve the documentation. + +### Translate Documentation + +If you are proficient in different languages, you are welcome to translate the alova documentation, which will help expand the use and audience of alova. + +## Become a core team member + +Check [HERE](./become-core-member) for details diff --git a/versioned_docs/version-2.x/contributing/02-become-core-member.md b/versioned_docs/version-2.x/contributing/02-become-core-member.md new file mode 100644 index 000000000..501677561 --- /dev/null +++ b/versioned_docs/version-2.x/contributing/02-become-core-member.md @@ -0,0 +1,32 @@ +--- +title: Become a core team member +sidebar_position: 20 +--- + +🤝🤝🤝 If you also agree with alovajs’ philosophy, then let us create the next generation request library together! + +## Responsibilities + +1. Responsible for the development and maintenance of vscode plug-in projects, request scenario modules and adapter projects, as well as the Hongmeng version of alovajs. +2. Write and optimize relevant development documents. +3. In-depth participation in the entire life cycle of project design and implementation, promotion and maintenance. +4. Participate in project development decision-making and commercialization exploration. + +## Revenue + +1. With the title of core member, you have the opportunity to enhance your reputation in the front-end field, thereby broadening your career development path. +2. More opportunities for public sharing and opportunities to contact more front-end personnel. +3. Broaden project thinking and technical experience through in-depth participation in project design, implementation and operation. +4. Decision-making power on projects. +5. Profit sharing. + +## Require + +1. Agree with alova’s design philosophy and development direction, and be willing to work hard to move forward together. +2. Passion for technology, sense of responsibility, and teamwork. +3. Technical capabilities only need to cover existing needs. +4. Have some spare time. + +## How to join + +If you are interested, please send an email to [hujou555@gmail.com](mailto:hujou555@gmail.com), and be sure to add `alova core member application` at the beginning of email. diff --git a/versioned_docs/version-2.x/contributing/03-developing-guidelines.md b/versioned_docs/version-2.x/contributing/03-developing-guidelines.md new file mode 100644 index 000000000..357669268 --- /dev/null +++ b/versioned_docs/version-2.x/contributing/03-developing-guidelines.md @@ -0,0 +1,138 @@ +--- +title: Developing Guidelines +sidebar_position: 30 +--- + +:::info version required + +Node.js 16+, npm 8+ + +::: + +## 1. Fork repository + +[Open alova fork page](https://github.com/alovajs/alova/fork), click "Create fork" and clone the forked repository to local. + +## 2. Clone to local + +Use the `git clone` command line, or the `Github Desktop` application to clone forked project. + +## 3. New pull request + +You can [create pull request through a forked repo](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork) after writing code. You can also commit code in arbitrary batches, without commiting a complete code. + +## 4. Code something in your computer + +### Install dependencies + +Install dependencies using `npm install`. + +### Install the recommended plugin(vscode) + +If you are using vscode, it is recommended that you install the following plugins: + +- eslint: check code quality. +- prettier: formatting code. +- jest: Automatically execute unit test cases, and execute individual collection or unit test cases. +- EditorConfig: Make sure the file format is consistent. + +### Project Structure + +``` +|-.github +| |-ISSUE_TEMPLATE -> github issues template +| |-workflows -> github action +|-.husky -> husky configuration +|-.vscode -> vscode configuration +|-config -> rollup package files +|-src -> source code +|-test -> unit test suits +| |-browser -> browser environment unit test suits +| |-server -> SSR unit test suits +| |-components -> Unit test components +| |-mockServer.ts -> mock apis (msw) +|-typings -> ts declaration +|- Other configuration files + +``` + +### Coding specifications + +#### Code format + +If you install the `prettier` plugin, it will automatically format codes every time you save files, so you don't have to worry about the format. + +#### Minimize code + +Lightweight is one of the alova's features, so it is necessary to minimize the amount of coding when coding. Here are a few coding specifications that need to be followed: + +1. Avoid the same code block, which can reduce the amount of code in the library, but two lines of code may not be worth encapsulating; +2. Use a variable declarator to aggregate variable declarations, for example: + +```javascript +// ❌ +const a = 1; +const b = 2; + +// ✅ +const a = 1, + b = 2; +``` + +3. Use constants to save js built-in values and prototype methods to reduce the amount of code in the compilation phase of `uglify`. built-in values and prototype methods that often used are defined in `src/utils/variables.ts`. + +```javascript +// ❌ +if (a === false) { + //... +} +arr.forEach(item => { + //... +}); + +// ✅ +import { falseValue, forEach } from '@/utils/variables'; +if (a === falseValue) { + //... +} +forEach(arr, item => { + //... +}); +``` + +## 5. Unit Testing Guidelines + +After finish code, it is necessary to add corresponding unit tests. + +The alova project uses **jest** as the unit test framework, and msw as the mock server. It is recommended to use the TDD mode. After modifying code every time, please pass the corresponding unit test. + +:::warning IMPORTANT + +When you're ready to commit your code, make sure all your unit tests are passed. When you're working on a pull request, you can have multiple small commits, and GitHub can automatically squash them before merging them. + +::: + +1. To add browser-related unit test cases, please add them to the corresponding test collection in `test/browser`, if there is no suitable test suits, you can create one by yourself; +2. Add SSR-related unit test cases, please add them to the corresponding test collection in `test/server`, if there is no suitable test suits, you can create one by yourself; + +### Run and debug a single unit test or suits + +It is recommended to use the **jest** plugin (one of the plugins recommended above) to test a single use case or a suit. You can right-click the specified unit test, select `Run Test` to run it, and select `Debug Test` to debug it with breakpoint. + +![image](https://github.com/alovajs/alova/assets/29848971/a94ba9db-c100-472f-b870-6bcecb031bea) + +### Run all unit tests + +1. Use the **jest** plugin to run: + +![image](https://github.com/alovajs/alova/assets/29848971/5af3ff15-16b7-4b28-9ae6-d0b5a236b181) + +2. Run the browser unit tests with command line `npm run test:browser`, run the SSR unit tests with `npm run test:node`, and run both at the same time with `npm run test`. + +## 6. Commit codes + +alova uses [semantic-release](https://semantic-release.gitbook.io) as an automatic release tool, which can automatically release new version packages after merging code into `main` , and generate `CHANGELOG`, but you need to ensure that the committed message format follows [commit information convention](https://www.conventionalcommits.org/en/v1.0.0/), it is recommended that use `npm run commit` to automatically generate a git message that conforms to the specification. + +## 7. Writing docs + +If you are adding a new feature, you can try to add the relevant documentation of the new feature. For details, please read [Correcting or add docs](/contributing/overview#correct-or-add-docs), otherwise please explain it in the pull request. diff --git a/versioned_docs/version-2.x/contributing/04-code-of-conduct.md b/versioned_docs/version-2.x/contributing/04-code-of-conduct.md new file mode 100644 index 000000000..580d03abe --- /dev/null +++ b/versioned_docs/version-2.x/contributing/04-code-of-conduct.md @@ -0,0 +1,137 @@ +--- +title: Code of Conduct +sidebar_position: 40 +--- + +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +- Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +- The use of sexualized language or imagery, and sexual attention or advances of + any kind +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email address, + without their explicit permission +- Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +[INSERT CONTACT METHOD]. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][mozilla coc]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][faq]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[mozilla coc]: https://github.com/mozilla/diversity +[faq]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations diff --git a/versioned_docs/version-2.x/contributing/_category_.json b/versioned_docs/version-2.x/contributing/_category_.json new file mode 100644 index 000000000..d3bf2a858 --- /dev/null +++ b/versioned_docs/version-2.x/contributing/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Contributing", + "link": { + "type": "generated-index" + } +} diff --git a/versioned_docs/version-2.x/tutorial/01-example/01-init-page.md b/versioned_docs/version-2.x/tutorial/01-example/01-init-page.md new file mode 100644 index 000000000..d38ee50b1 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/01-example/01-init-page.md @@ -0,0 +1,16 @@ +--- +title: (vue)page initialization request +sidebar_position: 10 +--- + +import InitPage from '@site/example-links/InitPage'; + +> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started); + + + +:::info example description + +This example mainly shows the simplicity of the request, it will return stateful request status, data, etc. which can be used directly in the view and managed directly by alova + +::: diff --git a/versioned_docs/version-2.x/tutorial/01-example/02-submit-form.md b/versioned_docs/version-2.x/tutorial/01-example/02-submit-form.md new file mode 100644 index 000000000..b5f3bcce0 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/01-example/02-submit-form.md @@ -0,0 +1,16 @@ +--- +title: (vue)form submission +sidebar_position: 20 +--- + +import SubmitForm from '@site/example-links/SubmitForm'; + +> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started); + + + +:::info example description + +This example mainly shows the basic data submission encoding method + +::: diff --git a/versioned_docs/version-2.x/tutorial/01-example/03-condition-search.md b/versioned_docs/version-2.x/tutorial/01-example/03-condition-search.md new file mode 100644 index 000000000..e7a58e069 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/01-example/03-condition-search.md @@ -0,0 +1,16 @@ +--- +title: (vue)Conditional Search +sidebar_position: 30 +--- + +import ConditionSearch from '@site/example-links/ConditionSearch'; + +> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started); + + + +:::info example description + +This example mainly shows the simplicity of data search. There is no need for the developer to maintain the state of the request, data, etc., and there is no need to manually trigger the request to send. Just bind the state of the search conditions. + +::: diff --git a/versioned_docs/version-2.x/tutorial/01-example/04-memory-cache.md b/versioned_docs/version-2.x/tutorial/01-example/04-memory-cache.md new file mode 100644 index 000000000..6768af625 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/01-example/04-memory-cache.md @@ -0,0 +1,21 @@ +--- +title: (vue)Response Cache - Memory Mode +sidebar_position: 40 +--- + +import MemoryCache from '@site/example-links/MemoryCache'; + +> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started); + + + +:::info example description + +The memory cache mode stores the response data in memory, and the cache is invalid when the page is refreshed. + +_Operation guide:_ + +1. Click on the following student list item, the requesting student details will be sent, and the modal box will display the Loading status; +2. Click the mask to close the pop-up box and reopen it. At this time, the cache will be hit and the student details will be displayed immediately, and the request record will no longer be printed in Request Records; + +::: diff --git a/versioned_docs/version-2.x/tutorial/01-example/05-storage-placeholder.md b/versioned_docs/version-2.x/tutorial/01-example/05-storage-placeholder.md new file mode 100644 index 000000000..9fd88b854 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/01-example/05-storage-placeholder.md @@ -0,0 +1,21 @@ +--- +title: (vue)Response Cache - Cache Placeholder Mode +sidebar_position: 50 +--- + +import StoragePlaceholder from '@site/example-links/StoragePlaceholder'; + +> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started); + + + +:::info example description + +The cache placeholder mode is to persist the response data. It will be updated to the data state as placeholder data immediately after refreshing the page. At the same time, a request is sent. Developers can use placeholder data to replace the Loading state before responding. + +_Operation guide:_ + +1. Click `Reload page` to refresh the page, you no longer see the Loading status, but the old data is rendered and replaced with new data when the request is responded; +2. Click `Invalidate the data of placeholder` to invalidate the cached data, then you will see the Loading status again; + +::: diff --git a/versioned_docs/version-2.x/tutorial/01-example/06-storage-restore.md b/versioned_docs/version-2.x/tutorial/01-example/06-storage-restore.md new file mode 100644 index 000000000..bfa105451 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/01-example/06-storage-restore.md @@ -0,0 +1,21 @@ +--- +title: (vue)Response Caching - Restore Mode +sidebar_position: 60 +--- + +import StorageRestore from '@site/example-links/StorageRestore'; + +> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started); + + + +:::info example description + +The cache restore mode is to persist the response data. When the request hits the cache, the persistent cached data will be returned, and no more requests will be issued. It is generally used for some data that needs server management but does not change for a certain period of time. The following is an example of the restore mode of holiday information. + +_Operation guide:_ + +1. Click `Reload page` to refresh the page, you will no longer see the Loading status, but will use the cached data and render it to the page immediately, and no longer send requests; +2. Click `Invalidate the data of placeholder` to invalidate the cached data, then you will see the Loading status again; + +::: diff --git a/versioned_docs/version-2.x/tutorial/01-example/07-update-state.md b/versioned_docs/version-2.x/tutorial/01-example/07-update-state.md new file mode 100644 index 000000000..00f6ed794 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/01-example/07-update-state.md @@ -0,0 +1,20 @@ +--- +title: (vue)Update state across components/pages +sidebar_position: 70 +--- + +import UpdateState from '@site/example-links/UpdateState'; + +> The example uses vue3 as an example, but you can also use alova in react and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details; + + + +:::info example description + +When you need to update state across components or pages, you are often limited by the component hierarchy. Here is a way to break this limitation. + +_Operation guidance:_ + +In the edit box or edit page, after adding or editing list data, the list data is updated across component levels. + +::: diff --git a/versioned_docs/version-2.x/tutorial/01-example/08-prefetch.md b/versioned_docs/version-2.x/tutorial/01-example/08-prefetch.md new file mode 100644 index 000000000..ab2690dc1 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/01-example/08-prefetch.md @@ -0,0 +1,21 @@ +--- +title: (vue)Data Prefetch +sidebar_position: 80 +--- + +import Prefetch from '@site/example-links/Prefetch'; + +> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started); + + + +:::info example description + +Data pre-fetching is a way to predict user operation behavior to achieve a strategy of displaying content to users faster. + +_Operation guide:_ + +1. Move the mouse to any list item and stay there for 0.2 seconds, a request for detailed data will be sent on the bottom panel; +2. Click to open this list item, you can see the detailed data immediately; + +::: diff --git a/versioned_docs/version-2.x/tutorial/01-example/09-load-more.md b/versioned_docs/version-2.x/tutorial/01-example/09-load-more.md new file mode 100644 index 000000000..ebe27569f --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/01-example/09-load-more.md @@ -0,0 +1,23 @@ +--- +title: (vue)Load more +sidebar_position: 90 +--- + +import LoadMore from '@site/example-links/LoadMore'; + +> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started); + + + +:::info example description + +Using pagination strategy, achieve a more high-performance and easy-to-use pagination function, automatic management of paging-related status, preloading of previous and previous pages, and automatic maintenance of data addition/editing/replacement/ Removed, and request-level stabilization. + +_Operation guide:_ + +1. After the initialization is completed, the next page of data will be preloaded, and there is no need to wait for scrolling down the page; +2. Adding, deleting, and modifying list items does not need to reset the list, it will be automatically processed into the same effect as the re-request; + +[usePagination documentation](/tutorial/strategy/usePagination) + +::: diff --git a/versioned_docs/version-2.x/tutorial/01-example/10-paginated-list.md b/versioned_docs/version-2.x/tutorial/01-example/10-paginated-list.md new file mode 100644 index 000000000..a1e55d20d --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/01-example/10-paginated-list.md @@ -0,0 +1,23 @@ +--- +title: (vue)Paginated list +sidebar_position: 100 +--- + +import Pagination from '@site/example-links/Pagination'; + +> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started); + + + +:::info example description + +Using pagination strategy, achieve a more high-performance and easy-to-use pagination function, automatic management of paging-related status, preloading of previous and previous pages, and automatic maintenance of data addition/editing/replacement/ Removed, and request-level stabilization. + +_Operation guide:_ + +1. After the initialization is completed, the next page of data will be preloaded, and there is no need to wait when turning to the next page; +2. Adding, deleting, and modifying list items does not need to reset the list, it will be automatically processed into the same effect as the re-request; + +[usePagination documentation](/tutorial/strategy/usePagination) + +::: diff --git a/versioned_docs/version-2.x/tutorial/01-example/11-controlled-cache-by-indexeddb.md b/versioned_docs/version-2.x/tutorial/01-example/11-controlled-cache-by-indexeddb.md new file mode 100644 index 000000000..b5b5eaf4f --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/01-example/11-controlled-cache-by-indexeddb.md @@ -0,0 +1,23 @@ +--- +title: (vue)Manage Cache with IndexedDB +sidebar_position: 110 +--- + +import ControlledCacheByIndexedDB from '@site/example-links/ControlledCacheByIndexedDB'; + +> The example uses vue3 as an example, but you can also use alova in react and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details; + + + +:::info example description + +Using controlled cache allows developers to customize and manage the cache. Under the large file cache, it can cooperate with IndexedDB to manage the local cache. + +_Operation guidance:_ + +1. Select one of the pictures, the picture will request the network to load first, and the picture data will be saved in the local IndexedDB; +2. Refresh the page and select the same picture again, the picture will get data from IndexedDB instead of initiating a network request; + +[Controlled cache document](/tutorial/cache/controlled-cache) + +::: diff --git a/versioned_docs/version-2.x/tutorial/01-example/12-silent-submit-setting.md b/versioned_docs/version-2.x/tutorial/01-example/12-silent-submit-setting.md new file mode 100644 index 000000000..aa49e9dc2 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/01-example/12-silent-submit-setting.md @@ -0,0 +1,23 @@ +--- +title: (svelte)Silent Submit - Settings Page +sidebar_position: 120 +--- + +import SettingSilentSvelte from '@site/example-links/SettingSilentSvelte'; + +> The example uses svelte as an example, but you can also use alova in react and vue, please read the [Getting Started Guide](/tutorial/getting-started) for details; + + + +:::info example description + +Use the silent submission strategy, submit and respond immediately, greatly reducing the impact of network fluctuations, so that your application is still very smooth even when the network is unstable or even disconnected. + +_Operation guidance:_ + +1. Operate the setting item, it will generate feedback immediately without waiting for the server to respond; +2. Switch the request mode and network status to experience the difference between them; + +[Silent submit strategy document](/tutorial/strategy/sensorless-data-interaction) + +::: diff --git a/versioned_docs/version-2.x/tutorial/01-example/13-silent-submit-simple-list.md b/versioned_docs/version-2.x/tutorial/01-example/13-silent-submit-simple-list.md new file mode 100644 index 000000000..2b4ba3046 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/01-example/13-silent-submit-simple-list.md @@ -0,0 +1,23 @@ +--- +title: (vue)Silent submit - Simple List +sidebar_position: 130 +--- + +import SimpleListSilentVue from '@site/example-links/SimpleListSilentVue'; + +> The example uses vue3 as an example, but you can also use alova in react and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details; + + + +:::info example description + +A simple list implemented using the silent submission strategy, which responds immediately after submission, greatly reduces the impact of network fluctuations, allowing your application to remain very smooth even when the network is unstable or even disconnected. + +_Operation guidance:_ + +1. Add, edit, and delete list items, it will generate feedback immediately without waiting for the server to respond; +2. Switch the request mode and network status to experience the difference between them; + +[Silent submit strategy document](/tutorial/strategy/sensorless-data-interaction) + +::: diff --git a/versioned_docs/version-2.x/tutorial/01-example/14-silent-submit-notes.md b/versioned_docs/version-2.x/tutorial/01-example/14-silent-submit-notes.md new file mode 100644 index 000000000..d4ae8b3b8 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/01-example/14-silent-submit-notes.md @@ -0,0 +1,23 @@ +--- +title: (react)Silent submit - Notebook +sidebar_position: 140 +--- + +import NoteSilentReact from '@site/example-links/NoteSilentReact'; + +> The example uses react as an example, but you can also use alova in vue3 and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details; + + + +:::info example description + +A simple list implemented using the silent submission strategy, which responds immediately after submission, greatly reduces the impact of network fluctuations, allowing your application to remain very smooth even when the network is unstable or even disconnected. + +_Operation guidance:_ + +1. Add, edit, and delete notes, it will generate feedback immediately without waiting for the server to respond; +2. Switch the request mode and network status to experience the difference between them; + +[Silent submit strategy document](/tutorial/strategy/sensorless-data-interaction) + +::: diff --git a/versioned_docs/version-2.x/tutorial/01-example/15-form-hook.md b/versioned_docs/version-2.x/tutorial/01-example/15-form-hook.md new file mode 100644 index 000000000..8dbccd923 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/01-example/15-form-hook.md @@ -0,0 +1,18 @@ +--- +title: (react)Form submit strategy +sidebar_position: 150 +--- + +import FormHook from '@site/example-links/FormHook'; + +> The example uses react as an example, but you can also use alova in vue3 and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details; + + + +:::info example description + +This example provides four parts: form data persistence, form editing, multi-module form, and form filtering data, which can be tried separately. + +[Form submit strategy document](/tutorial/strategy/useForm) + +::: diff --git a/versioned_docs/version-2.x/tutorial/01-example/16-captcha-send.md b/versioned_docs/version-2.x/tutorial/01-example/16-captcha-send.md new file mode 100644 index 000000000..1eca34a3d --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/01-example/16-captcha-send.md @@ -0,0 +1,18 @@ +--- +title: (react)Send captcha +sidebar_position: 160 +--- + +import CaptchaSend from '@site/example-links/CaptchaSend'; + +> The example uses react as an example, but you can also use alova in vue3 and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details; + + + +:::info example description + +This example demonstrates the convenient implementation of the captcha sending function. + +[Captcha sending strategy document](/tutorial/strategy/useCaptcha) + +::: diff --git a/versioned_docs/version-2.x/tutorial/01-example/17-retriable-hook.md b/versioned_docs/version-2.x/tutorial/01-example/17-retriable-hook.md new file mode 100644 index 000000000..8febc2043 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/01-example/17-retriable-hook.md @@ -0,0 +1,18 @@ +--- +title: (react)Request Retry/Poll Request +sidebar_position: 170 +--- + +import RetriableHook from '@site/example-links/RetriableHook'; + +> The example uses react as an example, but you can also use alova in vue3 and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details; + + + +:::info example description + +This example provides four parts: form data persistence, form editing, multi-module form, and form filtering data, which can be tried separately. + +[Request Retry Strategy Documentation](/tutorial/strategy/useRetriableRequest) + +::: diff --git a/versioned_docs/version-2.x/tutorial/01-example/18.action-delegation-middleware.md b/versioned_docs/version-2.x/tutorial/01-example/18.action-delegation-middleware.md new file mode 100644 index 000000000..2590d7c38 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/01-example/18.action-delegation-middleware.md @@ -0,0 +1,18 @@ +--- +title: (react)Trigger requests across components +sidebar_position: 180 +--- + +import ActionDelegationMiddleware from '@site/example-links/ActionDelegationMiddleware'; + +> The example uses react as an example, but you can also use alova in vue3 and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details; + + + +:::info example description + +This example mainly uses the `actionDelegationMiddleware` middleware to demonstrate the refresh of server-side data across any level of components without using global state management. + +[Actions delegation middleware document](/tutorial/strategy/actionDelegationMiddleware) + +::: diff --git a/versioned_docs/version-2.x/tutorial/01-example/19-serial-request.md b/versioned_docs/version-2.x/tutorial/01-example/19-serial-request.md new file mode 100644 index 000000000..34fb885df --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/01-example/19-serial-request.md @@ -0,0 +1,19 @@ +--- +title: (vue)serial request hook +sidebar_position: 190 +--- + +import SerialRequest from '@site/example-links/SerialRequest'; + +> The example uses vue3 as an example, but you can also use alova in react and svelte, please read [Getting Started Guide](/tutorial/getting-started) for details; + + + +:::info example description + +This example mainly demonstrates a more elegant serial request through `useSerialRequest`. + +[useSerialRequest](/tutorial/strategy/useSerialRequest) +[useSerialWatcher](/tutorial/strategy/useSerialWatcher) + +::: diff --git a/versioned_docs/version-2.x/tutorial/01-example/_category_.json b/versioned_docs/version-2.x/tutorial/01-example/_category_.json new file mode 100644 index 000000000..52fa495e9 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/01-example/_category_.json @@ -0,0 +1,7 @@ +{ + "label": "Examples", + "link": { + "type": "generated-index", + "description": "Here are rich examples to show how alova behaves in different request scenarios.The dependencies will be installed before the example runs, so wait for a while!" + } +} diff --git a/versioned_docs/version-2.x/tutorial/02-getting-started/02-quick-start.md b/versioned_docs/version-2.x/tutorial/02-getting-started/02-quick-start.md new file mode 100644 index 000000000..6cdfadc12 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/02-getting-started/02-quick-start.md @@ -0,0 +1,115 @@ +--- +title: Quick Start +sidebar_position: 20 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import EmbedSandpack from "@site/src/components/EmbedSandpack"; + +import quickStartGET from '!!raw-loader!@site/codesandbox/01-getting-started/02-first-request/get.js'; +import quickStartPOST from '!!raw-loader!@site/codesandbox/01-getting-started/02-first-request/post.js'; + +:::tip Example tip + +If you haven’t learned about alova yet, it is recommended that you read [alova overview](/tutorial/getting-started) first. + +::: + +## Install + + + + +```bash +npm install alova --save +``` + + + + +```bash +yarn add alova +``` + + + + +```bash +pnpm add alova +``` + + + + +```bash +bun add alova +``` + + + + +> You can also [use alova through CDN](/tutorial/others/use-in-static) + +## Create alova instance + +In alova, a request needs to be made through an alova instance. Let's create one first. When creating an alova instance, you need to specify a request adapter. It is recommended to use the `GlobalFetch` request adapter here, which is a package based on the `fetch API`. + + + + +```javascript +import { createAlova } from 'alova'; +import GlobalFetch from 'alova/GlobalFetch'; + +const alovaInstance = createAlova({ + requestAdapter: GlobalFetch() +}); +``` + + + + +```javascript +const { createAlova } = require('alova'); +const GlobalFetch = require('alova/GlobalFetch'); + +const alova = createAlova({ + requestAdapter: GlobalFetch(); +}); +``` + +> When using GlobalFetch in nodejs, the nodejs version requires `v17.5`, or you can use [axios request adapter](/tutorial/request-adapter/alova-adapter-axios/). + + + + +```javascript +import { createAlova } from 'npm:alova'; +import GlobalFetch from 'npm:alova/GlobalFetch'; + +const alova = createAlova({ + requestAdapter: GlobalFetch(); +}); +``` + + + + +## GET request + +Sending a request via `alovaInstance.Get` will receive a `Response` instance thanks to the `GlobalFetch` request adapter, which is simple. + + + +In an asynchronous function, you can also use `await alovaInstance.Get` to wait for a response. + +## POST request + +Submitting data via `alovaInstance.Post` is also easy. + + + +## What’s next? + +In fact, this is just the simplest request example. We will learn more about the features in the next chapters, so let's start learning. diff --git a/versioned_docs/version-2.x/tutorial/02-getting-started/03-method.md b/versioned_docs/version-2.x/tutorial/02-getting-started/03-method.md new file mode 100644 index 000000000..a188cbd1d --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/02-getting-started/03-method.md @@ -0,0 +1,306 @@ +--- +title: Method Instance +sidebar_position: 30 +--- + +In the previous chapter we tried to send a request and obtain the response data. In fact, `alovaInstance.Get(...)` is not a function that initiates a request, but creates a method instance, which is a PromiseLike instance. You can use `then, catch, finally` methods or `await` Send the request just like a Promise object. + +```javascript +const userMethodInstance = alovaInstance.Get('/api/user'); + +userMethodInstance.then(response => { + // ... +}); + +userMethodInstance.catch(error => { + // ... +}); + +userMethodInstance.finally(() => { + // ... +}); + +try { + await userMethodInstance; +} catch (error) { + // ... +} finally { + // ... +} +``` + +Simple way to write: + +```javascript +const response = await alovaInstance.Get('/api/user'); +``` + +Each method instance describes the type of each request, request url, request headers, request parameters, etc. In addition, you can define request behavior on the method instance to control how the method handles the request. + +## Request type + +alova provides a total of 7 request types: GET, POST, PUT, DELETE, HEAD, OPTIONS, and PATCH. + +| Instance creation function | Parameters | +| -------------------------- | --------------------------------------------- | +| GET | `alovaInstance.Get(url[, config])` | +| POST | `alovaInstance.Post(url[, data[, config]])` | +| PUT | `alovaInstance.Put(url[, data[, config]])` | +| DELETE | `alovaInstance.Delete(url[, data[, config]])` | +| HEAD | `alovaInstance.Head(url[, config])` | +| OPTIONS | `alovaInstance.Options(url[, config])` | +| PATCH | `alovaInstance.Patch(url[, data[, config]])` | + +Parameter Description: + +- `url` is the request path; +- `data` is the request body data; +- `config` is the request configuration object, which includes configurations such as request headers, params parameters, request behavior parameters, etc.; + +you can also create a method instance customly. This is useful when you need to dynamically specify the request type. + +```javascript +import { Method } from 'alova'; + +const method = new Method('GET', alovaInstance, '/api/users', { + params: { + ID: 1 + } +}); +``` + +Next, let’s take a look at how to define request parameters, which should seem familiar to you. + +## Request parameters + +### URL parameters + +Pass in the URL parameters through params, and the params parameters will be spliced in the form of ? after the url. + +```javascript +alovaInstance.Get('/todo/list', { + params: { + userId: 1 + } +}); +``` + +Of course, you can also directly splice it behind the url, and the effect will be the same. + +```javascript +alovaInstance.Get('/todo/list?userId=1'); +``` + +### Request body + +When sending **POST, PUT, DELETE, PATCH request**, you can send data through the request body. At this time, the second parameter is passed in the request body. It is worth noting that the POST request can also pass in the params parameter. + +```javascript +alovaInstance.Post( + '/todo', + //The second parameter is the request body + { + title: 'test todo', + time: '12:00' + }, + // The third parameter is configuration + { + params: { + userId: 1 + } + } +); +``` + +### Request header + +Specify request headers via headers. + +```javascript +alovaInstance.Get('/user', { + headers: { + 'Content-Type': 'application/json;charset=UTF-8' + } +}); +``` + +### Other parameters supported by the request adapter + +In addition to request headers, params parameters, etc., it also supports configuring parameters supported by the corresponding request adapter. When using `GlobalFetch` as the request adapter of alova, you can configure any `fetch API` supported parameters on the `method` instance. These Parameters will be passed to the `fetch` function during request. + +```javascript +alovaInstance.Get('/todo/list', { + // ... + // highlight-start + credentials: 'same-origin', + referrerPolicy: 'no-referrer', + mode: 'cors' + // highlight-end +}); +``` + +When the above `method` instance sends a request through `fetch`, it will be requested with the following parameters. + +```javascript +fetch('/todo/list', { + // ... + // highlight-start + credentials: 'same-origin', + referrerPolicy: 'no-referrer', + mode: 'cors' + // highlight-end +}); +``` + +> In addition to passing Object, the request body can also pass request body parameters supported by the request adapter. For example, GlobalFetch supports passing `string | FormData | Blob | ArrayBuffer | URLSearchParams | ReadableStream` parameters. + +If you use other request adapters, you can also pass the parameters they support. + +## Request behavior + +In [RSM](/tutorial/others/RSM), request behavior is used to describe how the request will be handled. + +### overtime time + +Set request timeout. + +```javascript +// Request timeout at request level +alovaInstance.Get('/todo/list', { + // ... + // highlight-start + timeout: 10000 + // highlight-end +}); +``` + +### Request sharing + +We will always encounter this situation. When a request is issued but has not yet been responded to, the same request is issued again, resulting in a waste of requests or repeated submission of problems, such as the following three scenarios: + +1. A component will obtain initialization data when it is created. When a page renders multiple components at the same time, multiple identical requests will be issued at the same time; +2. The submit button is not disabled and the user clicks the submit button multiple times; +3. When the preloading page is entered before preloading is completed, multiple identical requests will be initiated; +4. Prevent repeated requests from being sent under react’s StrictMode; + +Shared requests are used to solve these problems. It can not only improve application fluency, but also reduce server pressure. + +```mermaid +flowchart LR + classDef response fill:#a8bcff + R1[Request 1] --> S1[Send request] --> W1[Waiting for response]:::response --> RE1[Receive data 1] + R2[Same request as request 1] --> W1[Waiting for response]:::response --> RE2[Receive data 1] +``` + +Request sharing is enabled by default. If you wish to turn off request sharing on a specific request, you can do this: + +```javascript +alovaInst.Get('/todo', { + // ... + // highlight-start + shareRequest: false + // highlight-end +}); +``` + +:::warning How to identify identical requests + +The request method, request URL, request header, URL parameters, and request body of the method instance are used as a unique identifier. If the identifier is the same, it means the same request, instead of comparing the reference address of the method instance. + +::: + +### Convert response data + +Sometimes we need to uniformly transform the response data. We can set the `transformData` function for the method instance to convert the response data into the required structure. + +```javascript +alovaInstance.Get('/todo/list', { + // The function accepts response data and response header data, and requires the converted data to be returned. + transformData(rawData, headers) { + return rawData.list.map(item => { + return { + ...item, + statusText: item.done ? 'Completed' : 'In progress' + }; + }); + } +}); +``` + +### Response cache + +Response caching allows you to better utilize server-side data multiple times without having to send a request to obtain the data every time it is requested. GET requests will set a memory cache time of 5 minutes by default, which we will explain in detail in the [Response Caching](/tutorial/cache/mode) section later. + +## Abort request + +`[2.6.2+]` calls the `abort` of the method instance to abort the request. + +```javascript +const userMethod = alovaInstance.Get('/api/user'); +userMethod.then(res => { + // ... +}); + +const handleCancel = () => { + // highlight-start + userMethod.abort(); + // highlight-end +}; +``` + +## Monitor upload and download progress + +**[v2.17.0+]** Bind the upload progress event through `onUpload` of the method instance, and bind the download progress event through `onDownload`, which will return the unbinding function. + +```javascript +const uploadMethod = alovaInstance.Post('/todo/uploadfile', formData); +const offUploadEvent = uploadMethod.onUpload(event => { + console.log('File size:', event.total); + console.log('Uploaded:', event.loaded); +}); + +uploadMethod.then(res => { + // ... +}); + +// Unbind upload callback +const handleOffEvent = () => { + offUploadEvent(); +}; +``` + +```javascript +const downloadMethod = alovaInstance.Get('/todo/downloadfile'); +const offDownloadEvent = downloadMethod.onDownload(event => { + console.log('File size:', event.total); + console.log('Downloaded:', event.loaded); +}); + +downloadMethod.then(res => { + // ... +}); + +// Unbind download callback +const handleOffEvent = () => { + offDownloadEvent(); +}; +``` + +:::warning Things to note when using the `GlobalFetch` adapter + +Due to fetch api limitations, the **GlobalFetch** adapter provided by alova does not support upload progress. If you need to upload progress, please use [XMLHttpRequest Adapter](/tutorial/request-adapter/alova-adapter-xhr) or [axios Adapter](/tutorial/request-adapter/alova-adapter-axios). + +You can also write your own request adapter, see [Writing Request Adapter](/tutorial/custom/custom-http-adapter) for details. + +::: + +**Upload/Download Status Type** + +```typescript +type Progress = { + /** Total amount of data uploaded or downloaded */ + total: number; + /** Completed data */ + loaded: number; +}; +``` diff --git a/versioned_docs/version-2.x/tutorial/02-getting-started/04-alova.md b/versioned_docs/version-2.x/tutorial/02-getting-started/04-alova.md new file mode 100644 index 000000000..8a6456f6d --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/02-getting-started/04-alova.md @@ -0,0 +1,67 @@ +--- +title: Alova Instance +sidebar_position: 40 +--- + +Alova instances can not only create method instances of different types, but also set global parameters. The created method instances will inherit the parameters of this alova instance. When the parameters of the alova instance are set to the same parameters as the method instance, such as `timeout`, `shareRequest`, etc., the parameters of the method instance will be used first. + +Next we look at the global parameters of alova. + +## baseURL + +After setting the baseURL, you no longer need to add the same url prefix for every request. + +```javascript +const alovaInstance = createAlova({ + baseURL: 'https://api.alovajs.dev' + // ... +}); +``` + +At this point, you only need to specify pathname when creating a method instance. + +```javascript +alovaInstance.Get('/todo/list'); +``` + +## Global timeout + +The following is to set the global request timeout. + +```javascript +// Set request timeout globally +const alovaInstance = createAlova({ + // ... + // highlight-start + // Request timeout, unit is milliseconds, default is 0, which means it will never timeout + timeout: 50000 + // highlight-end +}); +``` + +## Global sharing request + +Set sharing requests globally. + +```javascript +const alovaInstance = createAlova({ + // ... + // highlight-start + shareRequest: false + // highlight-end +}); +``` + +## Request adapter + +In the previous chapter we have configured the `GlobalFetch` request adapter, which will be used to send requests initiated by this alova instance. In fact, we also provide various request adapters for different JS environments. + +- [Mock request adapter](/tutorial/request-adapter/alova-mock) +- [XMLHttpRequest Adapter](/tutorial/request-adapter/alova-adapter-xhr) +- [axios adapter](/tutorial/request-adapter/alova-adapter-axios) +- [uniapp adapter](/tutorial/request-adapter/alova-adapter-uniapp) +- [taro adapter](/tutorial/request-adapter/alova-adapter-taro) + +## Global response cache + +You can also set the response cache globally, which we will explain in detail in the [Response Cache](/tutorial/cache/mode) chapter later. diff --git a/versioned_docs/version-2.x/tutorial/02-getting-started/05-global-interceptor.md b/versioned_docs/version-2.x/tutorial/02-getting-started/05-global-interceptor.md new file mode 100644 index 000000000..fbb8d9e6b --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/02-getting-started/05-global-interceptor.md @@ -0,0 +1,137 @@ +--- +title: Global Interceptor +sidebar_position: 50 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## Global request interceptor + +Usually, we need to use the same configuration for all requests, such as adding token and timestamp to the request header. At this time, we can set a global request interceptor, which will be triggered before all requests. We can set this interceptor Set request parameters uniformly. + +```mermaid +flowchart LR + R1[Request 1] --> beforeRequest + R2[Request 2] --> beforeRequest + R3[Request 3] --> beforeRequest + RN[Request N] --> beforeRequest + beforeRequest --> S1[Send request] +``` + +```javascript +const alovaInstance = createAlova({ + // ... + //The function parameter is a method instance, including request data such as url, params, data, headers, etc. + // You can modify these data freely + // highlight-start + beforeRequest(method) { + // Suppose we need to add token to the request header + method.config.headers.token = 'token'; + } + // highlight-end +}); +``` + +You can also set beforeRequest as an asynchronous function. + +```javascript +const alovaInstance = createAlova({ + // ... + // highlight-start + async beforeRequest(method) { + //Perform some asynchronous tasks + // ... + } + // highlight-end +}); +``` + +## Global response interceptor + +When we want to uniformly parse response data, uniformly handle errors, and uniformly handle request completion, we can specify a global response interceptor when creating an alova instance. The response interceptor includes an interceptor for successful requests and an interceptor for failed requests. and request completion interceptors. + +```mermaid +flowchart LR + classDef error fill:#f96,stroke:#f00,stroke-width:2px; + + R1[Request 1 successful] --> responded.onSuccess + R2[Request 2 successful] --> responded.onSuccess + RN[Request N successful] --> responded.onSuccess + R4[Request 4 failed]:::error --> responded.onError:::error + R5[Request M failed]:::error --> responded.onError:::error + responded.onSuccess --> responded.onComplete + responded.onError --> responded.onComplete +``` + +```javascript +const alovaInstance = createAlova({ + // ... + //Use two items of the array to specify the interceptor for successful request and the interceptor for failed request respectively. + responded: { + // highlight-start + // Interceptor for successful request + // When using the GlobalFetch request adapter, the first parameter receives the Response object + // The second parameter is the method instance of the current request. You can use it to synchronize the configuration information before and after the request. + onSuccess: async (response, method) => { + if (response.status >= 400) { + throw new Error(response.statusText); + } + const json = await response.json(); + if (json.code !== 200) { + // This request will throw an error when an error is thrown or a Promise instance with reject status is returned. + throw new Error(json.message); + } + + //The parsed response data will be passed to the transformData hook function of the method instance. These functions will be explained later. + return json.data; + }, + // highlight-end + + // highlight-start + // Interceptor for request failure + // This interceptor will be entered when a request error occurs. + // The second parameter is the method instance of the current request. You can use it to synchronize the configuration information before and after the request. + onError: (err, method) => { + alert(error.message); + }, + // highlight-end + + // highlight-start + //Interceptor for request completion + // When you need to execute logic whether the request succeeds, fails, or hits the cache, you can specify a global `onComplete` interceptor when creating an alova instance, such as turning off the request loading state. + // Receive the method instance of the current request + onComplete: async method => { + // Process request completion logic + } + // highlight-end + } +}); +``` + +If you do not need to set an interceptor for request failure or completion, you can directly pass in the interceptor function for successful request, instead of setting a callback through an object. + +```javascript +const alovaInstance = createAlova({ + // ... + // highlight-start + async responded(response, method) { + // Interceptor for successful request + } + // highlight-end +}); +``` + +:::info Interceptor triggering instructions + +When you use `GlobalFetch` to request the adapter, due to the characteristics of `window.fetch`, the `onError` interceptor will only be triggered when the connection times out or the connection is aborted. In other cases, the `onSuccess` interceptor will be triggered. [For details, please Check here](https://developer.mozilla.org/docs/Web/API/fetch) + +::: + +:::warning Special attention + +1. `onSuccess`, `onError` and `onComplete` can be set as synchronous functions and asynchronous functions. +2. The `onError` callback is a capture function for request errors. An error thrown in `onSuccess` will not trigger `onError`. When an error is caught but no error is thrown or a Promise instance that returns reject status is used, the request will be considered successful and no response data will be obtained. +3. In 2.0.x and previous versions, `responded` was incorrectly spelled as `responsed`. In 2.1.0, the two have been made compatible. It is recommended to use `responded` instead of `responsed` in subsequent versions. . + +::: diff --git a/versioned_docs/version-2.x/tutorial/02-getting-started/06-method-metadata.md b/versioned_docs/version-2.x/tutorial/02-getting-started/06-method-metadata.md new file mode 100644 index 000000000..995782ccb --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/02-getting-started/06-method-metadata.md @@ -0,0 +1,157 @@ +--- +title: Method Metadata +sidebar_position: 60 +--- + +:::info version requirements + +v2.7.0+ + +::: + +Method instances run through the entire request life cycle of alova, and there will be a large number of different method instances in the project. Sometimes we need to add additional information to specific method instances to facilitate their identification or additional information transfer. Wait, at this point, we need to use method metadata. + +## Use metadata to identify identities + +### Use identity before request + +For example, most of the interfaces in your project need to be accompanied by `token` for each request, but there are still some interfaces that do not require verification. You may handle them uniformly in the global `beforeRequest` function. + +```javascript +const nonvalidateRequiredApi = [ + '/api/url1', + '/api/url2', + '/api/url3' + // ... +]; + +createAlova({ + beforeRequest(method) { + if (!nonvalidateRequiredApi.includes(method.url)) { + method.config.headers.token = '...'; + } + } +}); +``` + +This will cause the following two problems: + +1. Information is not aggregated with method instances, making maintainability even worse; +2. Coding is more troublesome; + +To solve these two problems, we will use metadata to identify a specific method instance when it is created. + +**Step 1: Define metadata when creating method instance** + +```javascript +const loginAPI = (username, password) => { + const methodInstance = alovaInst.Post('/login', { + username, + password + }); + methodInstance.meta = { + ignoreToken: true + }; + return methodInstance; +}; +``` + +**[2.18.0+]** And you can also directly define metadata in config + +```javascript +const loginAPI = (username, password) => { + return alovaInst.Post( + '/login', + { + username, + password + }, + { + meta: { + ignoreToken: true + } + } + ); +}; +``` + +**Step 2: Use metadata as the basis for judgment in `beforeRequest`** + +```javascript +createAlova({ + // ... + beforeRequest(method) { + if (!method.meta?.ignoreToken) { + method.config.headers.token = '...'; + } + } +}); +``` + +### Use the identity after the response + +This method can also be used in global `responded`. For example, in most cases, the request api will return json data, but there may be a file download interface, which will return a binary data stream. In this case Below, you can use different metadata in `responded` to handle different responses separately. + +**Step one: When creating a method instance, you also need to assign a metadata** + +```javascript +const downloadAPI = filePath => { + const methodInstance = alovaInst.Post('/download_file', { + filePath + }); + methodInstance.meta = { + isDownload: true + }; + return methodInstance; +}; +``` + +**Step 2: Use metadata in `responded` as a basis for judgment** + +```javascript +createAlova({ + // ... + responded: + onSuccess: (response, method) => method.meta?.isDownload ? response.blob() : response.json() + onError: (error, method) => { + //Metadata of method instances can also be accessed when responding to errors + } + } +}); +``` + +## Use metadata to pass information + +In some cases, if you want to add additional information to different method instances for use elsewhere, you can also use metadata to save it. Take uniformly generating different method instance IDs as an example. + +```javascript +createAlova({ + beforeRequest(method) { + if (!method.meta.generateId) { + method.meta.uid = generateUUID(); + } + }, + + responded: { + onSuccess(response, method) { + // Access the meta data generated by the current method when the request is successful. + const currentMethodUID = method.meta.uid; + }, + onError(error, method) { + //Access the meta data generated by the current method when the request fails. + const currentMethodUID = method.meta.uid; + } + } +}); +``` + +## Tips for non-typescript projects + +In a non-typescript environment, you can use any attribute as an information carrier, not limited to the `meta` attribute. + +```javascript +methodInstance.showResponseMsg = true; +methodInstance.others = 'abc'; +``` + +Only in the typescript environment, any attribute name will report that the attribute "$0" does not exist. ts(2339)`, so in the type we specify the `meta` attribute as the information carrier. diff --git a/versioned_docs/version-2.x/tutorial/02-getting-started/README.md b/versioned_docs/version-2.x/tutorial/02-getting-started/README.md new file mode 100644 index 000000000..6d845e06c --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/02-getting-started/README.md @@ -0,0 +1,136 @@ +--- +title: What is alova +--- + +import Link from '@docusaurus/Link'; +import NavCard from '@site/src/components/NavCard'; +import SupportList from '@site/src/components/SupportList'; + +alova is a lightweight request strategy library. It provides a complete set of solutions to deal with complex request scenarios. We call it **Request Strategy**. It only takes one line of code to quickly implement various complex request logics. , not only can help you improve development efficiency, but also help you improve the operating efficiency of the App and reduce the pressure on the server. + +Here is the simplest request example: + +```javascript +const response = await alova.Get('/api/user'); +``` + +Ordinary? Let's look at another example of automatically managing request status. **loading, error, and data are responsive data**. In UI frameworks such as react, vue, svelte, etc., they can be bound directly in the view and will be processed according to the request. State automatically maintains this responsive data. + +```javascript +const { loading, error, data } = useRequest(alova.Get('/api/user')); +``` + +The following is an example of a paging request strategy that automatically triggers requests with different parameters when page, pageSize, etc. change. + +```javascript +const { loading, error, data, page, pageSize, total } = usePagination((page, size) => + alova.Get('/api/user/list', { + params: { page, size } + }) +); +``` + +alova provides 10+ request strategy modules based on the [RSM](/tutorial/others/RSM) specification, which are implemented in the form of useHook. + +## Core useHook + +useRequest +useWatcher +useFetcher + +## Scenario-based request strategy + +usePagination +useSQRequest +useForm +TokenAuthentication +useAutoRequest +useCaptcha +actionDelegationMiddleware +useSerialRequest +useSerialWatcher +useRetriableRequest +useUploader +useSSE + +## High flexibility + +Thanks to the high flexibility of alova, you can use it with different request libraries in the following different JS environments (the gray part will be gradually supported in the future). + + + +## Is there any difference? + +Unlike other request libraries, alova's goal is to make requests simpler and maintain more efficient data interaction. + +We consider both developers and App users. For developers, alova provides them with a simple request API and an out-of-the-box high-performance request strategy module. For application users, they can enjoy Alova's high-performance data interaction brings a smooth experience. + +In addition, let’s take a look at the specific features: + +- API design similar to axios, allowing users to learn at a lower cost; +- 10+ out-of-the-box high-performance request strategies to make applications smoother; +- alova is lightweight, only 4kb+, which is 30%+ of axios; +- High flexibility, alova's adapter allows alova to be used in any js environment and with any UI framework (the built-in supported UI framework is `vue/react/svelte`), and provides a unified experience and perfect code migrate; +- 3 caching modes and request sharing mechanism to improve request performance and reduce server pressure; +- Highly aggregated organization of API code. The request parameters, cache behavior, response data conversion, etc. of each API will be gathered in the same code block, which has great advantages for managing a large number of APIs; + +In [alova's future](/tutorial/others/future), further request simplification will be implemented. + +:::info compared to other request libraries + +You can also check out [Comparison with other request libraries](/tutorial/others/comparison) to learn more about how alova is different. + +::: + +## Online trial + +You can run the project directly in the browser via Codesandbox [online editable examples try alovajs](/category/examples), so it's almost indistinguishable from local development without having to install anything on your machine. + +## Join alova community + +import ImgDiscord from '@site/static/img/discord.svg'; +import ImgX from '@site/static/img/x.svg'; +import ImgWechat from '@site/static/img/wechat.svg'; +import wechatQrcode from '@site/static/img/wechat_qrcode.jpg'; + +, +title: 'Discord', +desc: 'The community\'s GPT robot will answer your questions', +link: 'https://discord.gg/S47QGJgkVb', +target: '__blank' +}, +{ +Image: , +title: 'WeChat', +desc: 'Communicate in group chat and get responses faster', +link: wechatQrcode, +target: '__blank' +}, +{ +Image: , +title: 'X', +desc: 'Follow us and continue to receive the latest news', +link: 'https://x.com/alovajs', +target: '__blank' +} +]}> + +## Welcome to contribute + +Before contributing, please be sure to read the [Contribution Guide](/contributing/overview) in detail to ensure your effective contribution. + +## Start + +Next, we will start with the simplest request, then explain the request strategy, understand how alova simplifies your work, and then go into the advanced guide and the best practices summarized in actual projects. + +Let’s start learning to send our first request! + + diff --git a/versioned_docs/version-2.x/tutorial/02-getting-started/_category_.json b/versioned_docs/version-2.x/tutorial/02-getting-started/_category_.json new file mode 100644 index 000000000..6a7cbc87e --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/02-getting-started/_category_.json @@ -0,0 +1,3 @@ +{ + "label": "Getting Started" +} diff --git a/docs/tutorial/03-combine-framework/02-use-request.md b/versioned_docs/version-2.x/tutorial/03-combine-framework/02-use-request.md similarity index 100% rename from docs/tutorial/03-combine-framework/02-use-request.md rename to versioned_docs/version-2.x/tutorial/03-combine-framework/02-use-request.md diff --git a/docs/tutorial/03-combine-framework/03-use-watcher.md b/versioned_docs/version-2.x/tutorial/03-combine-framework/03-use-watcher.md similarity index 100% rename from docs/tutorial/03-combine-framework/03-use-watcher.md rename to versioned_docs/version-2.x/tutorial/03-combine-framework/03-use-watcher.md diff --git a/docs/tutorial/03-combine-framework/04-initial-data.md b/versioned_docs/version-2.x/tutorial/03-combine-framework/04-initial-data.md similarity index 100% rename from docs/tutorial/03-combine-framework/04-initial-data.md rename to versioned_docs/version-2.x/tutorial/03-combine-framework/04-initial-data.md diff --git a/docs/tutorial/03-combine-framework/05-response.md b/versioned_docs/version-2.x/tutorial/03-combine-framework/05-response.md similarity index 100% rename from docs/tutorial/03-combine-framework/05-response.md rename to versioned_docs/version-2.x/tutorial/03-combine-framework/05-response.md diff --git a/docs/tutorial/03-combine-framework/06-abort-request.md b/versioned_docs/version-2.x/tutorial/03-combine-framework/06-abort-request.md similarity index 100% rename from docs/tutorial/03-combine-framework/06-abort-request.md rename to versioned_docs/version-2.x/tutorial/03-combine-framework/06-abort-request.md diff --git a/docs/tutorial/03-combine-framework/07-download-upload-progress.md b/versioned_docs/version-2.x/tutorial/03-combine-framework/07-download-upload-progress.md similarity index 100% rename from docs/tutorial/03-combine-framework/07-download-upload-progress.md rename to versioned_docs/version-2.x/tutorial/03-combine-framework/07-download-upload-progress.md diff --git a/docs/tutorial/03-combine-framework/08-receive-params.md b/versioned_docs/version-2.x/tutorial/03-combine-framework/08-receive-params.md similarity index 100% rename from docs/tutorial/03-combine-framework/08-receive-params.md rename to versioned_docs/version-2.x/tutorial/03-combine-framework/08-receive-params.md diff --git a/versioned_docs/version-2.x/tutorial/03-combine-framework/10-typescript.md b/versioned_docs/version-2.x/tutorial/03-combine-framework/10-typescript.md new file mode 100644 index 000000000..de279e29c --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/03-combine-framework/10-typescript.md @@ -0,0 +1,159 @@ +--- +title: Typescript +sidebar_position: 100 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +In terms of Typescript, we have indeed spent a lot of effort on optimization in order to provide a better user experience. We try our best to use automatic type inference to reduce the trouble of defining types for you. + +## Automatically infer alova useHooks states type + +When createAlova creates an alova instance, the state types created by `useRequest`, `useWatcher`, and `useFetcher` will be automatically inferred based on the passed `statesHook`. Currently only Vue, React, and Svelte are supported. + +> `useFetcher` is a useHook used for data fetching. For details, please read [Advanced-Data fetching chapter](/tutorial/advanced/use-fetcher). + +The following are the status types returned by useHooks by default. + + + + +```typescript +const vueAlova = createAlova({ + statesHook: VueHook + // ... +}); +const { + loading, // Ref + data, // Ref<{ data: any }> + error // Ref +} = useRequest(vueAlova.Get<{ data: any }>('/todo/list')); +``` + + + + +```typescript +const reactAlova = createAlova({ + statesHook: ReactHook + // ... +}); +const { + loading, // boolean + data, // { data: any } + error // Error +} = useRequest(reactAlova.Get<{ data: any }>('/todo/list')); +``` + + + + +```typescript +const svelteAlova = createAlova({ + statesHook: SvelteHook + // ... +}); +const { + loading, // Writable + data, // Writable<{ data: any }> + error // Writable +} = useRequest(svelteAlova.Get<{ data: any }>('/todo/list')); +``` + + + + +The type of data will be different depending on the response data type specified in different Method instances, let's continue to look below. + +## Type of response data + +When you specify a type for a data interface, you need to divide it into two situations. + +### Case 1 + +When the response data does not need to be converted by calling `transformData`, the type can be specified directly through generics. + +```typescript +interface Todo { + title: string; + time: string; + done: boolean; +} +const Get = alovaInstance.Get('/todo/list'); +const { data } = useRequest(Get); +// vue: The type of data is Ref +// react: The type of data is Todo[] +// svelte: The type of data is Writable +``` + +### Case 2 + +When the response data needs to be converted by calling `transformData`, the type needs to be specified in the conversion function parameter, and then its return value type will be used as the response data type. + +```typescript +interface Todo { + title: string; + time: string; + done: boolean; +} +const Get = alovaInstance.Get('/todo/list', { + //Write the type into the data parameter, and the headers will be automatically inferred, so you don’t need to specify the type. + transformData(data: Todo[], headers) { + return data.map(item => ({ + ...item, + status: item.done ? 'Completed' : 'Not completed' + })); + } +}); + +const { data } = useRequest(Get); +// vue: The type of data is Ref<(Todo & { status: string })[]> +// react: The type of data is (Todo & { status: string })[] +// svelte: The type of data is Writable<(Todo & { status: string })[]> +``` + +:::warning note + +The response data is converted by the global response interceptor, so when setting the type, it should also be set to the converted type. + +::: + +## Type inferred from request adapter + +Because alova supports custom request adapters, and the request configuration objects, response objects, and response headers of different adapters may be different, so the global `beforeRequest`, `responded` interceptors, and the configuration object when the `Method` instance is created The types will be automatically inferred based on the types provided by the request adapter. Let's look at these types first. + +If you are using [**GlobalFetch**](https://github.com/alovajs/alova/blob/main/src/predefine/GlobalFetch.ts), alova will automatically infer the type using `fetch api`, The types of fetch api are as follows. + +```typescript +declare function fetch(input: RequestInfo | URL, init?: RequestInit): Promise; +``` + +### Method configuration type of instance + +The method configuration type will be automatically inferred as: + +```typescript +// AlovaMethodCommonConfig is a unified request parameter and behavior parameter +// highlight-start +const methodConfig: AlovaMethodCommonConfig & RequestInit = { + // highlight-end + // ... +}; +alovaInstance.Get('/api/user', methodConfig); +``` + +### Global response interceptor parameter type + +The type of responded interceptor will be automatically inferred as: + +```typescript +createAlova({ + // ... + // highlight-start + responded: (response: Response, method: Method) => { + // highlight-end + // ... + } +}); +``` diff --git a/docs/tutorial/03-combine-framework/README.md b/versioned_docs/version-2.x/tutorial/03-combine-framework/README.md similarity index 100% rename from docs/tutorial/03-combine-framework/README.md rename to versioned_docs/version-2.x/tutorial/03-combine-framework/README.md diff --git a/docs/tutorial/03-combine-framework/_category_.json b/versioned_docs/version-2.x/tutorial/03-combine-framework/_category_.json similarity index 100% rename from docs/tutorial/03-combine-framework/_category_.json rename to versioned_docs/version-2.x/tutorial/03-combine-framework/_category_.json diff --git a/versioned_docs/version-2.x/tutorial/04-cache/01-mode.md b/versioned_docs/version-2.x/tutorial/04-cache/01-mode.md new file mode 100644 index 000000000..175ffc3fe --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/04-cache/01-mode.md @@ -0,0 +1,215 @@ +--- +title: Cache Mode +sidebar_position: 10 +--- + +import MemoryCache from '@site/example-links/MemoryCache'; +import StoragePlaceholder from '@site/example-links/StoragePlaceholder'; +import StorageRestore from '@site/example-links/StorageRestore'; + +The cache mode can be set at different granularities such as global or request level. When set globally, all Method instances created from the same alova instance will inherit the setting. + +:::info note + +Whether to use the cache mode and which cache mode to use depends on the scenario. The usage scenarios of different cache modes will be mentioned below when introducing different cache modes separately. + +::: + +## Memory mode (default) + +The memory mode puts the cache in the memory, which means that the page cache is invalidated when it is refreshed, and is the most commonly used cache mode. + +Memory mode is generally used to solve the performance consumption caused by frequent requests for the same data in a short period of time (minutes or seconds). For example, when you are writing a todo details page, you may think that users will frequently click on the todo list Check the details, if the user does not repeatedly request the interface when repeatedly viewing a certain detail, and can return the data immediately, the colleague who improves the response speed also reduces the pressure on the server. At this point we can set the response data cache for a todo detail `Method` instance. + +```javascript +alovaInstance.GET('/todo/list', { + //... + // highlight-start + localCache: { + // Set cache mode to memory mode + mode: 'memory', + + // unit is milliseconds + // When set to `Infinity`, it means that the data will never expire, and when it is set to 0 or a negative number, it means no caching + expire: 60 * 10 * 1000 + } + // highlight-end +}); +``` + +Memory mode is the default mode, you can abbreviate like this + +```javascript +alovaInstance.GET('/todo/list', { + //... + // highlight-start + localCache: 60 * 10 * 1000 + // highlight-end +}); +``` + +> GET requests will set the memory cache time of 300000ms (5 minutes) by default, and developers can also customize the settings. + +> If you need to set the caching mode globally, see [Global setting cache mode] at the bottom of this section (#Global setting cache mode) + +### Memory mode example + + + +## Cache placeholder mode + +This cache mode is used when you don't want to display the Loading icon every time the application is entered, but you want to use the old data instead, you can use the cache occupancy mode, which has a better experience than Loading. + +In the cache occupancy mode, `data` will be immediately assigned the old data of the last cache. You can judge that if there is old data, use it to replace the Loading display. At the same time, it will send a request to obtain the latest data and update the cache, so as to achieve In order to quickly display the actual data, and obtain the latest data. + +Set on `Method` instances: + +```javascript +const todoListGetter = alovaInstance.Get('/todo/list', { + //... + // highlight-start + localCache: { + // Set the cache mode to persistent placeholder mode + mode: 'placeholder', + // cache time + expire: 60 * 10 * 1000 + } + // highlight-end +}); +``` + +> If you need to set the caching mode globally, see [Global setting cache mode] at the bottom of this section (#Global setting cache mode) + +### Cache placeholder mode example + + + +## Restore mode + +In this mode, the server-side cached data will be persistent. If the expiration time is not reached, even if the page cache is refreshed, it will not be invalidated. It is generally used for some data that requires server-side management but is basically unchanged, such as the specific dates of annual holidays. It is different, but it will not change again. In this scenario, we only need to set the cache expiration time to the last moment of this year. + +Set on `Method` instances: + +```javascript +const todoListGetter = alovaInstance.Get('/todo/list', { + //... + // highlight-start + localCache: { + // Set the cache mode to persistent mode + mode: 'restore', + // cache time + expire: 60 * 10 * 1000 + } + // highlight-end +}); +``` + +:::warning Caution + +When request body is special data such as **FormData**, **Blob**, **ArrayBuffer**, **URLSearchParams**, **ReadableStream**, it will be considered that you intend to communicate with server. In this case would not cache data. + +::: + +> If you need to set the caching mode globally, see [Global setting cache mode] at the bottom of this section (#Global setting cache mode) + +### Restore mode example + + + +### What should I do if the data changes in restore mode? + +When the `Method` instance in restore mode is set, it may be due to the change of the interface data or the logic change of the front-end processing response data. At this time, it is necessary to let the user re-cache the changed data after publishing the application. At this time, you can use `tag` The attribute sets the cache tag. Each piece of persistent data contains a `tag` identifier. When the `tag` changes, the original persistent data will become invalid, and new data will be obtained again, and the new `tag` will be used for identification . + +```javascript +const todoListGetter = alovaInstance.Get('/todo/list', { + //... + localCache: { + mode: 'restore', + expire: 60 * 10 * 1000, + + // highlight-start + // Add or modify the tag parameter, the cached data will be invalid + // It is recommended to use version number management + tag: 'v1' + // highlight-end + } +}); +``` + +## Global setting cache mode + +:::info version required +v1.3.0+ +::: +The above settings are all set separately on `Method`. If you need to set the cache mode globally, you can do it as follows: + +```javascript +const alovaInstance = createAlova({ + //... + // highlight-start + localCache: { + // Uniformly set the cache mode of POST + POST: { + mode: 'placeholder', + expire: 60 * 10 * 1000 + }, + // Uniformly set the cache mode of the HEAD request + HEAD: 60 * 10 * 1000 + } + // highlight-end +}); +``` + +Henceforth, the `Method` instance created by `alovaInstance` instance will use this cache setting by default, and it can also be overridden in the `Method` instance. + +> Note: When the cache mode is set globally, the original 5-minute GET cache mode will be overwritten. + +## Disable caching mode globally + +If you don't want to use any request cache in your project, you can turn it off globally. If you want to use it only in a few specific requests, you can also turn it off globally and set it in the specified `Method` instance . + +```javascript +const alovaInstance = createAlova({ + //... + // highlight-start + // Set to null to disable all request caching globally + localCache: null + // highlight-end +}); +``` + +## Expiration time type + +There are two types of expiration time to choose from, namely **relative time** and **absolute time** + +### Relative Time + +That is, the time to expire when the cached data is saved, in **milliseconds**, the above examples are all of this type. + +```javascript +localCache: 60 * 10 * 1000; +``` + +```javascript +localCache: { +expire: 60 * 10 * 1000, +} +``` + +### absolute time + +With a specific time point as the expiration time, the cache will expire at the set time point + +```javascript +localCache: new Date('2030-01-01'); +``` + +```javascript +localCache: { + expire: new Date('2030-01-01'); +} +``` + +## Instruction for response automatic maintenance + +The key of the response data cache is uniquely identified by the combination of the request method (method), request address (url), request header parameters (headers), url parameters (params), and request body parameters (requestBody) of the method instance. Any information or Different positions will be treated as different keys. diff --git a/versioned_docs/version-2.x/tutorial/04-cache/02-auto-invalidate.md b/versioned_docs/version-2.x/tutorial/04-cache/02-auto-invalidate.md new file mode 100644 index 000000000..9bbdfe62c --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/04-cache/02-auto-invalidate.md @@ -0,0 +1,91 @@ +--- +title: Auto Invalidate +sidebar_position: 20 +--- + +There is a scenario where when the user clicks on an item in the todo list, enters the todo details page and edits it, at this time we hope that the todo list data on the previous page will also be updated with the edited content. Usually The approach is to trigger content updates on the previous page through events, which increases maintenance costs. And `alova` provides 3 ways to achieve this goal very elegantly: + +1. Use `useFetcher` to immediately re-request the latest data, which will be explained in the [Data Fetching](/tutorial/advanced/use-fetcher) chapter; +2. Update the cache. This method will be explained in detail in the [Cache set and query](/tutorial/cache/set-and-query) chapter later; +3. Invalidate the response cache. When requested again, the data will be requested again due to cache invalidation. This is also what this chapter will explain. + +Automatic cache invalidation is to set invalidation source rules in the target cache. As long as the rules match, the target cache can be automatically invalidated. This saves the trouble of manually clearing the cache in many cases. + +## scenes to be used + +Setting up automatic invalidation rules is convenient when the target cache is one-to-one or one-to-many with the invalidation source. + +```mermaid +flowchart + M1[method1 invalidation source points to] --> T1[target cache] + M11[method1 invalidation source points to] --> T2[target cache] + M2[method2 invalidation source points to] --> T2[target cache] + MN[methodN invalidation source points to] --> T2[target cache] +``` + +## Set automatic invalidation rules + +Setting this rule is very simple, you can set the `hitSource` parameter for it when creating a Method instance with caching. + +### Invalidation source is set to method instance + +With a fixed method instance as the invalidation source, as long as the method instance or its clone instance succeeds, the target cache will be cleared automatically. + +```javascript +alova.Get('/todo/1', { + //... + hitSource: alova.Post('/todo', {}) +}); +``` + +### Match invalidating source by method name + +Like the method instance matcher, you can specify the name of the method in hitSource to match the failure source. Multiple failure sources can be set to the same name. When the method instance request with this name succeeds, the target cache will be automatically cleared. + +```javascript +const methodSubmitTodo = data => + alova.Post('/todo', data, { + name: 'submitTodo' + }); + +alova.Get('/todo/1', { + //... + // Match the failure source whose method instance name is submitTodo + hitSource: 'submitTodo' +}); +``` + +### Match invalidating source by regexp of method name + +If the method instance name is not fixed, you can specify a regular expression in hitSource to match the method name, and the target cache will be automatically cleared when the matched method instance succeeds in the request. + +```javascript +const methodSubmitTodo = data => + alova.Post('/todo', data, { + name: 'prefix-submitTodo' + }); + +alova.Get('/todo/1', { + //... + // Match all instances whose method instance name starts with prefix + hitSource: /^prefix/ +}); +``` + +### Combination setting validating source + +If you want to use the above multiple rules to match failure sources, you can specify hitSource as an array, and the array item is any one of the above three rules, and the method instance that meets any one of the rules in the array will be matched. + +```javascript +alova.Get('/todo/1', { + //... + // When the method instance request that satisfies any matching rule in the array succeeds, the cache will be invalidated + hitSource: [alova.Post('/todo', {}), 'submitTodo', /^prefix/] +}); +``` + +## hitSource data type + +```typescript +type hitSource = string | RegExp | Method | (string | RegExp | Method)[]; +``` diff --git a/versioned_docs/version-2.x/tutorial/04-cache/03-manually-invalidate.md b/versioned_docs/version-2.x/tutorial/04-cache/03-manually-invalidate.md new file mode 100644 index 000000000..bd8dd546b --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/04-cache/03-manually-invalidate.md @@ -0,0 +1,102 @@ +--- +title: Manually Invalidate +sidebar_position: 30 +--- + +Generally, automatic invalidate cache is more concise, and it is recommended to use it first to invalidate the cache. When the automatic invalidate cache does not meet the needs, you can also invalidate the cache by calling `invalidateCache`. + +## Use method instance invalidate cache + +Pass in a method instance in the `invalidateCache` function, it will look for the cache under this instance to invalidate it. + +In the following example, when the submission is successful, the todo details data cache will be invalidated. + +```javascript +// Get the todo details data with id 1 +const getTodoDetail = id => + alovaInstance.Get(`/todo/${id}`, { + localCache: 1000000 + }); +const { loading, data } = useRequest(getTodoDetail(1)); +``` + +```javascript +// Submit the data and invalidate the todo details with id 1. +const { + // ... + send, + onSuccess +} = useRequest(createTodoPoster, { immediate: false }); + +// highlight-start +//Invalid cache after successful submission +onSuccess(() => { + invalidateCache(getTodoDetail(1)); +}); +// highlight-end + +const handleSubmit = () => { + send({ + title: 'new todo', + content: 'new todo content' + }); +}; +``` + +## Batch invalidate cache + +In the following example, we invalidate caches in batches by specifying the name of the cache or a regular expression of the name. + +```javascript +//The cache of the method named todoList will be invalidated +invalidateCache('todoList'); + +// The cache of methods whose names match the following regular expression will be invalidated +invalidateCache(/^todoList/); +``` + +## Dynamic invalidate cache + +Maybe sometimes you are not sure which cached data needs to be invalidated. We can use [Method instance matcher](/tutorial/advanced/method-matcher) to dynamically find the corresponding method instance. The following example shows how to invalidate the cache of the first 5 method instances named todoList. + +```javascript +const getTodoList = currentPage => { + return alovaInstance.Get('/todo/list', { + // highlight-start + // First set the name for the method instance, which is used to filter out the required Method instance when the Method instance cannot be specified directly. + name: 'todoList', + // highlight-end + params: { + currentPage, + pageSize: 10 + } + }); +}; + +const { + // ... + send, + onSuccess +} = useRequest(createTodoPoster, { immediate: false }); +// After successful submission, the todo data cache of the first page will be invalidated. +onSuccess(() => { + // highlight-start + //The cache of the first 5 Method instances whose invalid name is todoList + invalidateCache({ + name: 'todoList', + filter: (method, index, ary) => { + return index < 5; + } + }); + // highlight-end +}); +``` + +> For more methods of using method instance matcher, see [Method instance matcher](/tutorial/advanced/method-matcher) + +## Invalidate all caches + +```javascript +// When no parameters are passed, all response caches are invalidated +invalidateCache(); +``` diff --git a/versioned_docs/version-2.x/tutorial/04-cache/04-force-request.md b/versioned_docs/version-2.x/tutorial/04-cache/04-force-request.md new file mode 100644 index 000000000..3337a47a8 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/04-cache/04-force-request.md @@ -0,0 +1,75 @@ +--- +title: Force Request +sidebar_position: 40 +--- + +Forced request refers to a mechanism that bypasses the cache check and triggers a request. It is useful when the latest data needs to be obtained under certain conditions. + +## Force request with method + +Force request by calling the send function of the method instance, and passing `true`. + +```javascript +const response = await alovaInstance.Get('/api/user').send(true); +``` + +## Force request in useHook + +Among the three core hooks of `useRequest/useWatcher/useFetcher`, force request parameters are supported. + +```javascript +// useRequest +useRequest(todoListGetter, { + // highlight-start + force: true + // highlight-end +}); + +// useWatcher +useWatcher(todoListGetter, [page], { + // highlight-start + force: true + // highlight-end +}); + +// useFetcher +useFetcher({ + // highlight-start + force: true + // highlight-end +}); +``` + +### Dynamically set force value + +In actual situations, we often need to set whether to force the request to be sent based on different situations. In this case, force can be set to a function, which will also receive parameters passed in from the `send` function. Please read the [receive params](/tutorial/combine-framework/receive-params) for details + +```javascript +useRequest(todoListGetter, { + // highlight-start + force: isForce => { + return isForce; + } + // highlight-end +}); + +// useWatcher +useWatcher(todoListGetter, [page], { + // highlight-start + force: isForce => { + return isForce; + } + // highlight-end +}); + +// useFetcher +useFetcher({ + // highlight-start + force: isForce => { + return isForce; + } + // highlight-end +}); +``` + +`useFetcher` is a useHook for data fetching, which will be discussed in the [Data Fetching](/tutorial/advanced/use-fetcher) chapter later. diff --git a/versioned_docs/version-2.x/tutorial/04-cache/05-set-and-query.md b/versioned_docs/version-2.x/tutorial/04-cache/05-set-and-query.md new file mode 100644 index 000000000..c11798832 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/04-cache/05-set-and-query.md @@ -0,0 +1,284 @@ +--- +title: Set & Query Cache +sidebar_position: 50 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +The cache also supports updating and querying, As we mentioned in [cache mode](/tutorial/cache/mode), each cached data is saved with the method instance that sends the request as the key, so the method instance will also be used when updating the cache manually to find the corresponding cached data. + +## Update static cache data + + + + +```html + + +``` + + + + +```jsx +import { setCache } from 'alova'; +import { useState } from 'react'; + +const getTodoListByDate = dateList => + alovaInstance.Get('/todo/list/dates', { + params: { dateList } + }); + +const App = () => { + // Get 5 days of data in batches during initialization + const [dates, setDates] = useState(['2022-05-01', '2022-05-02', '2022-05-03', '2022-05-04', '2022-05-05']); + const { + //... + onSuccess + } = useWatcher(() => getTodoListByDate(dates.join()), [dates], { + immediate: true + }); + onSuccess(todoListDates => { + if (todoListDates.length <= 1) { + return; + } + + // highlight-start + // By default, the data of these 5 days will be cached together in a key + // In order to make subsequent requests for data of a certain day also hit the cache, we can disassemble the data of 5 days into days, and manually set the response cache one by one through setCache + // The first parameter of setCache is the method instance object, which is used to specify the key of the cache + // The second parameter is the cached data + todoListDates.forEach(todoDate => { + setCache(getTodoListByDate(todoDate.date), [todoDate]); + }); + // highlight-end + }); + + // highlight-start + const handleTodolistToggle = () => { + // At this time, when the switching date is May 1, it will hit the response cache we manually set. + // The dates value is being monitored by useWatcher, so changing it can automatically trigger the request + setDates(['2022-05-01']); + }; + // highlight-end + + return ; +}; +``` + + + + +```html + + +``` + + + + +```html + + +``` + + + + +## Dynamically set cache data + +You can also pass in a callback function in `setCache` to dynamically calculate the cache data and return the cache data that needs to be updated. + +```javascript +setCache(getTodoListByDate('2022-10-01'), oldCache => { + // Return the data that needs to be cached + return { + ...oldCache, + expire: isAfter('2022-10-01', new Date()) + }; +}); +``` + +Similarly, you can also dynamically find method instances through [method instance matcher](/tutorial/advanced/method-matcher). + +```javascript +setCache( + { + name: 'todoList', + filter: (method, index, ary) => { + return index < 5; + } + }, + 'newCache' +); +``` + +## Abort to set cache + +Sometimes you need to dynamically determine whether to update the cache. If no data is returned in the callback function of `setCache`, or `undefined` is returned, the original cache data will not be updated at this time + +```javascript +setCache(getTodoListByDate('2022-10-01'), oldCache => { + const isExpired = isAfter('2022-10-01', new Date()); + if (!isExpired) { + return; // abort cache updating when return the undefined + } + return null; // update the cache to null +}); +``` + +## Cache query + +At the same time, we also provide a cache query method. + +```javascript +import { queryCache } from 'alova'; + +const cacheData = queryCache(getTodoListByDate('2022-10-01')); +``` + +You can also dynamically find method instances via [method instance matcher](/tutorial/advanced/method-matcher). + +```javascript +const cacheData = queryCache({ + name: 'todoList', + filter: (method, index, ary) => { + return index < 5; + } +}); +``` diff --git a/versioned_docs/version-2.x/tutorial/04-cache/06-controlled-cache.md b/versioned_docs/version-2.x/tutorial/04-cache/06-controlled-cache.md new file mode 100644 index 000000000..89dec2a39 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/04-cache/06-controlled-cache.md @@ -0,0 +1,83 @@ +--- +title: Controlled Cache +sidebar_position: 60 +--- + +:::info version requirements + +v2.1.0+ + +::: + +When sending a request, by default it will first check whether there is matching cache data. If it matches, it will use it as the response data to return. If in some scenarios, the user needs to use a custom cache, they must first use `setCache` to synchronize Setting up cached data can only work, which undoubtedly increases the burden on users. This is an uncontrolled cache. + +If you want to use **IndexedDB** to custom manage cached data under uncontrolled caching, you may first pre-set the hit cache for the upcoming request, like this: + +```javascript +const getFile = fileName => { + const fileGetter = alovaInstance.GET(`/file/${fileName}`); + const tx = db.transaction(['files']); + const getRequest = tx.objectStore('files').get(fileName); + getRequest.onsuccess = ({ result }) => { + setCache(fileGetter, result); + }; + return fileGetter; +}; +``` + +**❌ However, the above writing method is not recommended** for the following reasons: + +1. Each time `getFile` is called, a cache will be set, but fileGetter is not necessarily used to send requests; +2. IndexedDB is an asynchronous interface. If the cache matching step occurs before IndexedDB triggers onsuccess, then the cached data will not be matched, and their order is unpredictable; +3. Custom cache management tasks and methods are separated, but in fact they should be brought together; + +In this case, you can use controlled caching to solve the above problem. Using controlled caching is also very simple. You can set the localCache in the method to an asynchronous or synchronous function, and return custom data as a hit response. + +```javascript +const getFile = fileName => + alovaInstance.GET(`/file/${fileName}`, { + // Controlled cache functions support asynchronous and synchronous functions + localCache() { + return new Promise((resolve, reject) => { + const tx = db.transaction(['files']); + const getRequest = tx.objectStore('files').get(fileName); + getRequest.onsuccess = resolve; + getRequest.onerror = reject; + }); + } + }); +``` + +## Do not use caching + +If you wish to continue sending the request, you can return `undefined` or `void` in `localCache`, which is useful in the case of cache misses when customizing the cache. + +## Use transformData to set up cache + +Because the `transformData` function has the following two properties: + +- It is only triggered when responding, but will not be triggered when the response cache is hit; +- Support asynchronous functions; + +Therefore, you can also use it to save customized caches. For example, in a cache scenario where files are used as response data, you can use IndexedDB to cache file data. + +```javascript +const fileGetter = alovaInstance.Get('/file/file_name', { + // Use IndexedDB to cache files + async transformData(fileBlob) { + await new Promise((resolve, reject) => { + const tx = db.transaction(['files'], 'readwrite'); + const putRequest = tx.objectStore('files').put({ + file: fileBlob + }); + putRequest.onsuccess = resolve; + putRequest.onerror = reject; + }); + return fileBlob; + } +}); +``` + +## Notes + +When used in usehooks, throwing an error in the `localCache` function will trigger `onError`. When a request is made directly using a method instance, a promise instance with a reject status will be returned. diff --git a/versioned_docs/version-2.x/tutorial/04-cache/README.md b/versioned_docs/version-2.x/tutorial/04-cache/README.md new file mode 100644 index 000000000..be3949357 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/04-cache/README.md @@ -0,0 +1,9 @@ +--- +title: Response Cache +--- + +Response cache is a technology that caches the data returned by server to the client. It avoids sending repeated requests when the server data can be reused. It can not only respond to user requests immdediately, but also save server resources. According to different caching scenarios, alova provides 3 modes named **memory mode, cache placeholder mode, and restore mode**. Just choose the one that suits you. + +In addition, using the cache operation API, you can freely add, modify, and delete caches, as well as customize cache matching rules. + +Next, let us start to understand from the cache mode! diff --git a/versioned_docs/version-2.x/tutorial/04-cache/_category_.json b/versioned_docs/version-2.x/tutorial/04-cache/_category_.json new file mode 100644 index 000000000..2360c70d8 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/04-cache/_category_.json @@ -0,0 +1,3 @@ +{ + "label": "Response Cache" +} diff --git a/docs/tutorial/05-strategy/01-sensorless-data-interaction/02-virtual-data.md b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/02-virtual-data.md similarity index 100% rename from docs/tutorial/05-strategy/01-sensorless-data-interaction/02-virtual-data.md rename to versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/02-virtual-data.md diff --git a/docs/tutorial/05-strategy/01-sensorless-data-interaction/03-start-silent-factory.md b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/03-start-silent-factory.md similarity index 100% rename from docs/tutorial/05-strategy/01-sensorless-data-interaction/03-start-silent-factory.md rename to versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/03-start-silent-factory.md diff --git a/docs/tutorial/05-strategy/01-sensorless-data-interaction/04-conservative-request.md b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/04-conservative-request.md similarity index 100% rename from docs/tutorial/05-strategy/01-sensorless-data-interaction/04-conservative-request.md rename to versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/04-conservative-request.md diff --git a/docs/tutorial/05-strategy/01-sensorless-data-interaction/05-modify-response.md b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/05-modify-response.md similarity index 100% rename from docs/tutorial/05-strategy/01-sensorless-data-interaction/05-modify-response.md rename to versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/05-modify-response.md diff --git a/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/06-request-retry.md b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/06-request-retry.md new file mode 100644 index 000000000..1b8ef082b --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/06-request-retry.md @@ -0,0 +1,144 @@ +--- +title: Step 3 - Set Request Retry +sidebar_position: 60 +--- + +When a request enters the silent queue, you can set request retry parameters for it to ensure its request success rate. This is valid when the behavior mode is set to **queue** and **silent**. The difference is, The request under the behavior of **silent** is persistent by default, and the request will continue to be sent even if it is refreshed before the request succeeds, while the request under the behavior of **queue** will not be persisted and will be cleared after refreshing. + +## Maximum number of retries + +Set the maximum number of retries, no retries by default. + +```javascript +useSQRequest(createOrEditTodo, { + //... + // highlight-start + // The number of retries is 3 times + maxRetryTimes: 3 + // highlight-end +}); +``` + +## Request delay time + +By default, each retry interval is 1000ms, and we can customize the delay time of each retry in the avoidance strategy. + +```javascript +useSQRequest(createOrEditTodo, { + //... + maxRetryTimes: 3, + // highlight-start + // Requests are delayed by 2000ms each time + backoff: { + delay: 2000 + } + // highlight-end +}); +``` + +If you need to increase the delay time according to the rules, you can set a growth factor for it. + +```javascript +useSQRequest(createOrEditTodo, { + //... + maxRetryTimes: 3, + backoff: { + delay: 2000, + // highlight-start + // When multiplier is set to 2, the first retry delay is 2 seconds, the second is 4 seconds, and the third is 6 seconds + multiplier: 2 + // highlight-end + } +}); +``` + +not enough? You can even add a random jitter value to each delay to make it look less regular + +```javascript +useSQRequest(createOrEditTodo, { + //... + maxRetryTimes: 3, + backoff: { + delay: 2000, + multiplier: 2, + // highlight-start + /** + * The initial jitter percentage value of the delay request, the range is 0-1 + * When only startQuiver is set, endQuiver defaults to 1 + * For example set to 0.5, it will add 50% to 100% random time on the current delay time + * If endQuiver has a value, the delay time will be increased by a random value in the range of startQuiver and endQuiver + */ + startQuiver: 0.5, + + /** + * The jitter end percentage value of the delayed request, the range is 0-1 + * When only endQuiver is set, startQuiver defaults to 0 + * For example set to 0.8, it will add a random time from 0% to 80% on the current delay time + * If startQuiver has a value, the delay time will increase the random value in the range of startQuiver and endQuiver + */ + endQuiver: 0.8; + // highlight-end + } +}); +``` + +## set retry rules + +By default, as long as the request fails, it will be retried. The request failure is divided into the following situations: + +1. The request is wrong, and the error is not caught by the global `onError` hook; +2. The request was successful, but an error was thrown in the global `onSuccess` hook; + +But in reality, not all requests need to be retried. For example, when a server error occurs or the network is disconnected, it should not be retried. In this case, it is necessary to set a retry judgment rule. When a request fails, an instance of `Error` is usually obtained. We can set a regular expression to match `error.message` or `error.name`, and if the match passes, no retry will be made. + +```javascript +useSQRequest(createOrEditTodo, { + //... + // highlight-start + // When the thrown error name is 500, or the wrong message matches network error, do not retry + retryError: { + name: /^500$/, + message: /network error/i + } + // highlight-end +}); +``` + +You can also set one of the matching rules. When only setting the matching rules for `message`, it can be directly abbreviated as a regular expression. + +```javascript +// Only set the name that matches the error +useSQRequest(createOrEditTodo, { + //... + retryError: { + name: /^500$/ + } +}); + +// Only set the message that matches the error +useSQRequest(createOrEditTodo, { + //... + retryError: /network error/i +}); +``` + +In order not to pollute the error message, usually we will put the error code returned by the server in `error.name`, of course, you can also splice it into `error.message`, the error handling example of Response is as follows: + +```javascript +const alovaInst = createAlova({ + //... + responded: { + onSuccess(response) { + // Error thrown on 500 error + if (response.status === 500) { + const error = new Error(response.statusText); + error.name = response.status; + throw error; + } + return response.json(); + } + } +}); +``` + +In the next step, the saved operation records will be used to perform data compensation on the list data to achieve the latest state. diff --git a/docs/tutorial/05-strategy/01-sensorless-data-interaction/07-data-compensation.md b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/07-data-compensation.md similarity index 100% rename from docs/tutorial/05-strategy/01-sensorless-data-interaction/07-data-compensation.md rename to versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/07-data-compensation.md diff --git a/docs/tutorial/05-strategy/01-sensorless-data-interaction/08-edit-item.md b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/08-edit-item.md similarity index 100% rename from docs/tutorial/05-strategy/01-sensorless-data-interaction/08-edit-item.md rename to versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/08-edit-item.md diff --git a/docs/tutorial/05-strategy/01-sensorless-data-interaction/09-what-more.md b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/09-what-more.md similarity index 100% rename from docs/tutorial/05-strategy/01-sensorless-data-interaction/09-what-more.md rename to versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/09-what-more.md diff --git a/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/README.md b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/README.md new file mode 100644 index 000000000..5b703c10d --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/README.md @@ -0,0 +1,146 @@ +--- +title: Sensorless data interaction - Overview +--- + +Non-inductive data interaction means that when users interact with the application, relevant content can be displayed immediately without waiting, or the operation result can be displayed without waiting when submitting information, just like interacting with local data, thereby greatly improving the smoothness of the application It allows users to not feel the lag caused by data transmission. + +This is not new. The concept of optimistic update existed before 2015. It refers to displaying the submission results to the interface before the server responds. It is based on the assumption that most submissions are successful. The opposite is a conservative update, that is, the server will display a wait state before responding until the request is completed. In terms of handling failures, the current optimistic update solution is usually handled through fallback, such as the following sample code: + +```javascript +const list = []; +const data = {}; +addTodo(data).catch(() => { + list = list.filter(item => item !== data); +}); +list.push(data); +``` + +This can cause the following problems: + +1. Rollback will increase the user's understanding and operation costs; +2. Request timing issues; +3. If subsequent requests depend on this submission, this failure will make subsequent requests meaningless; +4. Possible lost requests; + +After several months of program design and continuous iteration, alova has taken a big step in this area. In our program, the above problems have been solved, which can ensure the success of the request more stably. Although there are still technical limitations, But it has been applied in many scenarios. In our technical solution, the problems caused by network fluctuations can be reduced to a higher extent. Your application is still available in high-latency networks or even disconnected, and the latest data can still be maintained after refreshing the page. + +## Application scenarios + +Although non-inductive data interaction cannot be used on a large scale, it is very suitable in certain scenarios. During the exploration, we found at least including but not limited to the following scenarios for your reference. + +### Editor application + +Note-taking applications such as Evernote and canvas editing applications such as MasterGO need to meet the following requirements: + +1. When entering the note or drawing list, the list data will be pulled in full, and the local cache data will be used next time; +2. Real-time synchronization to the server during the editing process, and the synchronization process occurs in the background, which will not affect the normal use of users; +3. You can continue to use it even when the network is poor or disconnected; + +:::info example + +We provide a [note application example](/tutorial/example/silent-submit-notes), you can enter the experience. + +::: + +### Setup module + +The setting module composed of commonly used switches and selectors needs to realize the requirement that the user operation is synchronized to the server in real time, and the submission status is no longer displayed, but the latest status after the operation is directly displayed. + +:::info example + +We provide a [setting page example](/tutorial/example/silent-submit-setting), you can enter the experience. + +::: + +### Simple list management + +The data we fill in when creating a list item is enough for the display of the list page, which is called a simple list. For example, a student list page displays the three data of the student's name, gender, and grade. These three data are required when creating a student fill in. In a simple list the following requirements will be fulfilled: + +1. Immediately display the latest status on the list page when adding, editing and deleting list items, no need to display it after the submission is completed, and it is not limited by network fluctuations; +2. When the page is refreshed, the list page is always kept up to date; + +:::info example + +We provide a [simple list page example](/tutorial/example/silent-submit-simple-list), you can enter the experience. + +::: + +### Complex list management + +A complex list means that the data filled in when creating a list item is not enough for display on the list page, and additional data needs to be generated according to the calculation of the server. For example, a Todo list page needs to list specific executions in addition to displaying basic information. date, but only the execution date range and related rules are specified on the creation page, so the execution date is calculated and generated by the server based on the date range and rules. + +The following requirements will be fulfilled in a complex list: + +1. Immediately display the latest status on the list page when adding, editing and deleting list items, and update the data calculated by the server to this list item after the server responds; +2. When the page is refreshed, the list page is always kept up to date; + +:::info example + +Stay tuned for complex list examples... + +::: + +### Free Mode + +In the above scenarios, you may want to judge whether to use the non-inductive interaction strategy or the most common conservative request strategy based on a condition. The requirements are as follows: + +1. When the network status is good, or paying users will use the non-sensing interaction strategy, but when the network fluctuates greatly, or free users cannot enjoy the non-sensing interaction strategy; +2. Strategies can be switched freely; + +:::info example + +In the above examples, you can experience the free switching strategy + +::: + +## Not recommended application scenarios + +### Information sharing class + +The submitted information needs to be synchronized to others, such as order information. This type of information has high real-time requirements, and we should ensure that the submission is successful. + +### Complex data interaction class + +Complex data interaction refers to the mixed editing and filtering of data, such as adding, editing, deleting and filtering a certain list. In this case, Alova cannot currently support it well. In subsequent versions Will try to solve this puzzle too. + +## Technical solutions + +In the technical solution of non-inductive data interaction, alova has implemented data pre-fetching and silent submission respectively. Next, let's understand these two technical solutions. + +:::info + +Please make sure you have mastered the following chapters before reading + +- [Basic Learning](/tutorial/getting-started) + +::: + +### Data pre-fetching + +In html, you may have seen such a tag ``, which tells the browser to preload the style file when it is idle, and put it in the cache In , when you really need to use it, you can take it out of the cache. Alova also uses a similar scheme to pre-fetch the required data through [useFetcher](/tutorial/advanced/use-fetcher), and it will be stored locally. in cache. You can predict the content that the user needs to read under any circumstances, and then pre-fetch the corresponding content. For example, the content of the next page can be pre-loaded in the process page, or the user stays on a button 200ms, we can pre-fetch the data needed for the next interface, which is similar to **Next.js** page preloading. + +We provide a [preloaded example](/tutorial/example/prefetch), you can enter the experience. + +### Silent submit + +Silent submission is a mechanism of submitting and responding. In the scheme, the completion of submission will be guaranteed, so it can be regarded as a safer optimistic update scheme. Silent submission mainly uses **Silent Queue** to persist request information and ensure request timing issues. **Virtual data** is used as a placeholder for server response data, which is replaced with actual response data when the request is completed. , through these two technologies, localized data creation is realized, and operations such as editing and deleting of newly created data are realized, even if the created data has not yet been submitted successfully on the server side. In order to keep development costs to a minimum, this is done automatically in alova. + +### Quiet Queue + +Silent queues are used to ensure the timing of requests. We can create queues arbitrarily, and all requests entering the queue will be stored in the queue in the form of **SilentMethod** instances. Each **SilentMethod** not only contains request information, but also Contains related configurations for silent submission, such as _unique id_, _error retry parameters_, etc. The requests in the queue will only initiate the next request after the previous response, thus ensuring the timing of the requests in the queue. You can put dependent requests in the same queue, which also ensures data consistency. + +![Silent queue](https://user-images.githubusercontent.com/29848971/220057005-dd467392-4a43-45a7-90dc-999dd1d95531.png) + +In the scheme, three behavior modes of `queue`, `silent`, and `static` are provided respectively, which are used to distinguish what kind of behavior a request needs to perform. + +- queue: The request will enter the silent queue, but it will not be persisted. It will wait for the previous request to complete before sending the request. The response callback will be triggered after the response. It is generally used for data acquisition that depends on the previous request; +- silent: The request will enter the silent queue and be persisted, and then trigger the response callback immediately. In this behavior mode, onSuccess will receive virtual data, and onError will never be triggered. Use this pattern; +- static: the request will not enter the silent queue, nor will it be persisted, it will issue the request immediately, and this mode can be used when silent submission is disabled; + +### virtual data + +In the submit-to-response mechanism, virtual data plays an important role. It means that before the server actually responds, it is used as a placeholder for the response data, and through the tracing mechanism, even if the virtual data is distributed in various locations of the application, Can be automatically replaced with the actual response data after the response. At the same time, it also plays an important role in the silent queue. It can identify the dependencies of requests in the queue, and replace the dependent data with actual data after the dependencies respond. For example, when creating a piece of data, it will return the id of this data. When the service When the terminal has not responded, the user performs a delete operation, and the id needs to be used as the delete identifier. At this time, the delete request will depend on the creation request. Before creating a request response, the virtual data will be used as an id placeholder as a parameter for deletion, and the virtual data id will be replaced after creating a request response, so that the deletion request can be completed. + +![virtual data](https://user-images.githubusercontent.com/29848971/220005505-d30b7ae2-ddd0-4080-81a4-65c9be2cb0bd.png) + +Next, we will learn more about the characteristics of virtual data. diff --git a/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/_category_.json b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/_category_.json new file mode 100644 index 000000000..89d03fd9e --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/_category_.json @@ -0,0 +1,3 @@ +{ + "label": "Sensorless data interaction" +} diff --git a/docs/tutorial/05-strategy/02-usePagination.md b/versioned_docs/version-2.x/tutorial/05-strategy/02-usePagination.md similarity index 100% rename from docs/tutorial/05-strategy/02-usePagination.md rename to versioned_docs/version-2.x/tutorial/05-strategy/02-usePagination.md diff --git a/docs/tutorial/05-strategy/03-useForm.md b/versioned_docs/version-2.x/tutorial/05-strategy/03-useForm.md similarity index 100% rename from docs/tutorial/05-strategy/03-useForm.md rename to versioned_docs/version-2.x/tutorial/05-strategy/03-useForm.md diff --git a/docs/tutorial/05-strategy/04-tokenAuthentication.md b/versioned_docs/version-2.x/tutorial/05-strategy/04-tokenAuthentication.md similarity index 100% rename from docs/tutorial/05-strategy/04-tokenAuthentication.md rename to versioned_docs/version-2.x/tutorial/05-strategy/04-tokenAuthentication.md diff --git a/docs/tutorial/05-strategy/05-useUploader.md b/versioned_docs/version-2.x/tutorial/05-strategy/05-useUploader.md similarity index 100% rename from docs/tutorial/05-strategy/05-useUploader.md rename to versioned_docs/version-2.x/tutorial/05-strategy/05-useUploader.md diff --git a/docs/tutorial/05-strategy/06-useAutoRequest.md b/versioned_docs/version-2.x/tutorial/05-strategy/06-useAutoRequest.md similarity index 100% rename from docs/tutorial/05-strategy/06-useAutoRequest.md rename to versioned_docs/version-2.x/tutorial/05-strategy/06-useAutoRequest.md diff --git a/docs/tutorial/05-strategy/07-useBreakpointUploader.md b/versioned_docs/version-2.x/tutorial/05-strategy/07-useBreakpointUploader.md similarity index 100% rename from docs/tutorial/05-strategy/07-useBreakpointUploader.md rename to versioned_docs/version-2.x/tutorial/05-strategy/07-useBreakpointUploader.md diff --git a/docs/tutorial/05-strategy/08-useCaptcha.md b/versioned_docs/version-2.x/tutorial/05-strategy/08-useCaptcha.md similarity index 100% rename from docs/tutorial/05-strategy/08-useCaptcha.md rename to versioned_docs/version-2.x/tutorial/05-strategy/08-useCaptcha.md diff --git a/docs/tutorial/05-strategy/09-actionDelegationMiddleware.md b/versioned_docs/version-2.x/tutorial/05-strategy/09-actionDelegationMiddleware.md similarity index 100% rename from docs/tutorial/05-strategy/09-actionDelegationMiddleware.md rename to versioned_docs/version-2.x/tutorial/05-strategy/09-actionDelegationMiddleware.md diff --git a/docs/tutorial/05-strategy/10-useSerialRequest.md b/versioned_docs/version-2.x/tutorial/05-strategy/10-useSerialRequest.md similarity index 100% rename from docs/tutorial/05-strategy/10-useSerialRequest.md rename to versioned_docs/version-2.x/tutorial/05-strategy/10-useSerialRequest.md diff --git a/docs/tutorial/05-strategy/11-useSerialWatcher.md b/versioned_docs/version-2.x/tutorial/05-strategy/11-useSerialWatcher.md similarity index 100% rename from docs/tutorial/05-strategy/11-useSerialWatcher.md rename to versioned_docs/version-2.x/tutorial/05-strategy/11-useSerialWatcher.md diff --git a/docs/tutorial/05-strategy/12-useRetriableRequest.md b/versioned_docs/version-2.x/tutorial/05-strategy/12-useRetriableRequest.md similarity index 100% rename from docs/tutorial/05-strategy/12-useRetriableRequest.md rename to versioned_docs/version-2.x/tutorial/05-strategy/12-useRetriableRequest.md diff --git a/docs/tutorial/05-strategy/13-useSSE.md b/versioned_docs/version-2.x/tutorial/05-strategy/13-useSSE.md similarity index 100% rename from docs/tutorial/05-strategy/13-useSSE.md rename to versioned_docs/version-2.x/tutorial/05-strategy/13-useSSE.md diff --git a/docs/tutorial/05-strategy/14-rateLimitMiddleware.md b/versioned_docs/version-2.x/tutorial/05-strategy/14-rateLimitMiddleware.md similarity index 100% rename from docs/tutorial/05-strategy/14-rateLimitMiddleware.md rename to versioned_docs/version-2.x/tutorial/05-strategy/14-rateLimitMiddleware.md diff --git a/docs/tutorial/05-strategy/README.md b/versioned_docs/version-2.x/tutorial/05-strategy/README.md similarity index 100% rename from docs/tutorial/05-strategy/README.md rename to versioned_docs/version-2.x/tutorial/05-strategy/README.md diff --git a/docs/tutorial/05-strategy/_category_.json b/versioned_docs/version-2.x/tutorial/05-strategy/_category_.json similarity index 100% rename from docs/tutorial/05-strategy/_category_.json rename to versioned_docs/version-2.x/tutorial/05-strategy/_category_.json diff --git a/versioned_docs/version-2.x/tutorial/06-advanced/01-use-fetcher.md b/versioned_docs/version-2.x/tutorial/06-advanced/01-use-fetcher.md new file mode 100644 index 000000000..f51eab080 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/06-advanced/01-use-fetcher.md @@ -0,0 +1,307 @@ +--- +title: Fetch Data +sidebar_position: 10 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +When you have the following needs: + +1. Preload the data that will be used in subsequent processes and store it in the cache, so that users no longer have to wait for the data loading process; +2. Conveniently implement cross-page data update (similar to global state), for example, modify an item in the todo list and then re-fetch the latest data, and the interface will be refreshed after the response. + +`useFetcher` is the hook used to implement the above scenario. The response data obtained through it cannot be received directly, but the data fetched through it will not only update the cache, but also update the corresponding state, thereby re-rendering the view. + +## Preload data + +Let's implement a paging list to automatically preload the next page of data. Before preloading data, please make sure that the Method instance used has enabled caching. + + + + +```html + + + +``` + + + + +```jsx +import { useState } from 'react'; + +//method instance creation function +const getTodoList = currentPage => { + return alovaInstance.Get('/todo/list', { + localCache: 60000, + params: { + currentPage, + pageSize: 10 + } + }); +}; + +const App = () => { + const { + // The fetching attribute is the same as loading. It is true when sending the fetch request and false after the request is completed. + fetching, + error, + onSuccess, + onError, + onComplete, + + // Only after calling fetch will a request be sent to fetch data. You can call fetch repeatedly to fetch data from different interfaces. + fetch + } = useFetcher({ + updateState: false + }); + const [currentPage, setCurrentPage] = useState(1); + const { data, onSuccess } = useWatcher(() => getTodoList(currentPage), [currentPage], { + immediate: true + }); + + // After the current page is loaded successfully, pass in the method instance of the next page to pre-fetch the data of the next page. + onSuccess(() => { + fetch(getTodoList(currentPage + 1)); + }); + + return ( + <> + {fetching ?
Fetching...
: null} + {/* list view */} + + ); +}; +``` + +
+ + +```html + + +{#if fetching} +
Fetching...
+{/if} + +``` + +
+ + +```html + + + +``` + + +
+ +:::warning + +The above example is set `updateState` to false when calling `useFetcher`. This is because the data fetching will automatically trigger a states cross-component updating by default, causing the view to be re-rendered. When preload data is the same as the currently requested data. You can set it to false to avoid affecting view errors. + +::: + +## Update views across modules/components + +Next, we will modify a todo data and re-fetch the latest todo list data to update the view. We may not know which page the todo list is currently on. In this case, when using the `fetch` function, we can use [Method instance matcher](/tutorial/advanced/method-matcher) to dynamically fetch the data of the current page. + +> The Method instance matcher is used to find method instances that meet the conditions among the requested method instances. + +First, set a name for the method instance in the todo list, which is used to filter out the required Method instance when the Method instance cannot be specified directly. + +```javascript title="api/todoList.js" +const getTodoList = currentPage => { + return alovaInstance.Get('/todo/list', { + // highlight-start + name: 'todoList', + // highlight-end + params: { + currentPage, + pageSize: 10 + } + }); +}; +``` + +Then in the `EditTodo` component, use the `fetch` function to dynamically find the last name of `todoList` in the requested Method instance to fetch data. + +```javascript title="EditTodo Component" +const { fetch } = useFetcher(); + +// Trigger data fetch in event +const handleSubmit = () => { + // submit data... + await fetch({ + name: 'todoList', + filter: (method, index, ary) => { + // Return true to specify the Method instance that needs to be fetched + return index === ary.length - 1; + } + }); +}; +``` + +:::warning Notes + +useFetcher only updates the cache after the request is completed, and if this Method is foundIf the instance has been requested using useHook before, the `data` state created by this useHook will also be updated to ensure that the page data is consistent. This is the guarantee that `useFetcher` is used to update views across modules/components. + +::: + +> For more methods of using `Method` instance matcher, see [Method instance matcher](/tutorial/advanced/method-matcher). + +## Force sending request + +Same as `useRequest` and `useWatcher`, please read [Force Request](/tutorial/cache/force-request) for more information. + +## Bind response callback + +useFetcher also supports binding `onSuccess/onError/onComplete` callback functions. + +```javascript +const { onSuccess, onError, onComplete } = useFetcher(); +``` + +Please read [Response Processing](/tutorial/combine-framework/response) for details. + +## send function parameter passing rules + +Different from `useRequest` and `useWatcher`, the custom parameters of the fetch function start from the second parameter, and they will also be received by the event callback and `force` function respectively. + +```javascript +const { onSuccess, fetch } = useFetcher(); +onSuccess(({ sendArgs }) => { + //The value of sendArgs is ['test arg'] +}); + +fetch(getTodoList(), 'test arg'); +``` + +For details, please read [send function parameter passing rules](/tutorial/combine-framework/receive-params). + +## Comparison between useRequest and useFetcher + +1. useFetcher does not return the `data` field, the pre-fetched data will be saved in the cache, and the status data of the corresponding location will be updated; +2. Renamed `loading` to `fetching`; +3. There is no `send` function, but there is a `fetch` function. You can reuse the fetch function to fetch data from different interfaces. At this time, you can use the `fetching` and `error` states to uniformly render the view to achieve unified processing. the goal of; diff --git a/versioned_docs/version-2.x/tutorial/06-advanced/02-update-across-components.md b/versioned_docs/version-2.x/tutorial/06-advanced/02-update-across-components.md new file mode 100644 index 000000000..a8a814256 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/06-advanced/02-update-across-components.md @@ -0,0 +1,102 @@ +--- +title: Update states across components +sidebar_position: 20 +--- + +There is a scenario where when the user clicks on an item in the todo list, enters the todo details page and edits it, at this time we hope that the todo list data on the previous page will also be updated without resetting the situation. For edited content, `useFetcher` is no longer applicable. + +At this time, you can use `updateState` to update the existing responsive state under any module/page. It can find and modify the responsive state in other modules. + +[Here is an example of `updateState`](/tutorial/example/update-state) + +## Use method instance to find response states + +When determining the method instance corresponding to the updated response state, you can pass in this method instance in `updateState`. It will find whether there is a corresponding response state under this instance and provide it to you for modification in the callback function. Finally Just return the modified data. + +```javascript +import { updateState } from 'alova'; + +//Todo item being edited +const editingTodo = { + id: 1, + title: 'todo1', + time: '09:00' +}; + +const { send, onSuccess } = useRequest(createTodoPoster, { immediate: false }); +onSuccess(() => { + // highlight-start + // Fixed modification of todo data on the first page + // updateState will return whether the update is successful + const updated = updateState(getTodoList(1), todoList => { + return todoList.map(item => { + if (item.id === editingTodo.id) { + return { + ...item, + ...editingTodo + }; + } + return item; + }); + }); + // highlight-end +}); +``` + +:::warning note + +1. When updating the state through `updateState`, if the cache (memory cache and persistent cache) is detected, the new data update cache will also be updated. +2. Only when a request has been initiated using useRequest or useWatcher, alova will manage the states returned by the hook. The reason is that the response states is generated and saved through a Method instance, but when no request is initiated, the url and URL in the Method instance are Parameters such as params, query, and headers are still uncertain. + +::: + +## Dynamically update response states + +Maybe sometimes you are not sure that you need to update the response states under the method, but you know how to find the cached data that needs to be invalidated. We can use [Method instance matcher](/tutorial/advanced/method-matcher) to dynamically Find the corresponding method instance. The following example shows adding a piece of data to the list corresponding to the method instance named todoList. + +```javascript +updateState('todoList', todoListRaw => { + todoListRaw.push({ + title: 'new todo', + time: '10:00' + }); + return todoListRaw; +}); +``` + +The [Method instance matcher](/tutorial/advanced/method-matcher) will be introduced in detail in subsequent chapters. + +## Listen for matching events + +When dynamically updating the response state, sometimes you may want to do some processing when a method instance is matched, or you may want to obtain the matching method instance. `updateState` can also pass in a third parameter to set a matching event to achieve these purposes. . + +```javascript +updateState( + 'todoList', + todoListRaw => { + // ... + }, + { + // Called when a method instance is matched, and the parameter is the matched method instance. + onMatch: method => { + // ... + } + } +); +``` + +:::warning ⚠️ Please make sure the component is not destroyed + +By default, `updateState` will look for the response state created by alova's useHooks when sending a request. However, to prevent memory overflow, the destruction of a component will also recycle all the states created internally, so please make sure you use `updateState` It is hoped that the container component corresponding to the updated response states has not been destroyed, otherwise the corresponding response states will not be found and the update will fail. + +This problem often occurs when updating states across pages. What we tend to overlook is that by default, the previous page has been destroyed when the page jumps. Therefore, if you want to update states across pages, here are two suggestions: + +1. Persist the page components to ensure that the updated states can still be found; +2. Use [setCache](/tutorial/cache/set-and-query) instead of `updateState`. The principle is that when the request for the previous page exists in the cache, update its cache to ensure that when the page is created again, the The request can hit the updated cache to achieve the same effect. + +::: + +## Notes + +1. In actual use, whether you use `useRequest` or `useWatcher` to send a request, you can call the `send` function to specify different parameters to send the request repeatedly. The response states returned by these use hooks will be used by multiple method instances. Reference, so you can choose any method instance to match the same response states value; +2. When dynamically searching and updating the response states, the method instance matcher finds multiple method instances, and the first instance will prevail; diff --git a/versioned_docs/version-2.x/tutorial/06-advanced/03-method-matcher.md b/versioned_docs/version-2.x/tutorial/06-advanced/03-method-matcher.md new file mode 100644 index 000000000..1666b3b22 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/06-advanced/03-method-matcher.md @@ -0,0 +1,141 @@ +--- +title: Method Matcher +sidebar_position: 30 +--- + +A method matcher is a method that dynamically finds a method instance in a list of requested method snapshots. It is generally used. When developers are not sure which method to use, they can use the method matcher to search according to certain rules. + +## Matching rules + +When a request is made using a method instance, it will be saved as a snapshot. The method matcher looks in these method snapshots based on the `name` attribute set by the method instance. Multiple matchers are allowed to set the same `name`. + +Method instance matching types are as follows: + +```typescript +type MethodFilter = + | string + | RegExp + | { + name: string | RegExp; + filter: (method: Method, index: number, methods: Method[]) => boolean; + + // Optional parameter, if the alova object is passed in, it will only match the Method instance created by this alova, otherwise it will match the Method instances of all alova instances. + alova?: Alova; + }; +``` + +The method instance matcher can be used in the following functions. + +- [setCache](/tutorial/cache/set-and-query) +- [queryCache](/tutorial/cache/set-and-query) +- [invalidateCache](/tutorial/cache/manually-invalidate) +- [updateState](/tutorial/advanced/update-across-components) +- [useFetcher.fetch](/tutorial/advanced/use-fetcher) + +## Match by name attribute + +Matching is performed by passing in the complete instance name, and its matching result is an array. + +```javascript +// Each time getTodoList is called, a new method instance will be generated, and their names are the same. +const getTodoList = currentPage => + alova.Get('/todo/list', { + // highlight-start + name: 'todoList' + // highlight-end + // ... + }); + +//The following means invalidating the cache of all Method instances with name 'todoList' +invalidateCache('todoList'); +``` + +## Match by regular expression + +By passing in a regular expression for matching, any method instance whose name matches the regular expression will be matched, and its result is also an array. + +```javascript +// The following means invalidating the cache of all Method instances whose names start with 'todo' +invalidateCache(/^todo/); +``` + +## Filter matching results + +Further filter method instances that do not meet the conditions by specifying `filter`. The filter function is used in the same way as Array.prototype.filter. Returning true indicates successful matching, and returning false indicates failure. See the type declaration above for details. + +Let's look at a few examples. + +**Invalidate the cache of the last method instance with a specific name** + +```javascript +invalidateCache({ + name: 'todoList', + filter: (method, index, methods) => index === methods.length - 1 +}); +``` + +**Set the cache of the last method instance of a specific name created by `alovaInst`** + +```javascript +setCache( + { + name: /^todo/, + filter: (method, index, methods) => index === methods.length - 1, + + // If the alova parameter is passed, only the Method instances created by this alova instance will be matched, otherwise it will be matched in all Method instances. + alova: alovaInst + }, + newCache +); +``` + +**Repulse the last requested data from the todo list** + +```javascript +const { fetch } = useFetcher(); +fetch({ + name: 'todoList', + filter: (method, index, methods) => index === methods.length - 1 +}); +``` + +> The alova parameter can further narrow the matching scope. + +## Differences in use in different functions + +### invalidateCache + +Apply the set of all matching Method instances, that is, invalidate the cache corresponding to all matching Method instances. + +### setCache + +All matching Method instance collections are applied. When static data is passed in, all Method instance caches are set to the same value. When the callback function is passed in, this function will be called cyclically and the return value will be used as cache data. + +### updateState + +The first matching Method instance is applied. + +### fetch + +The first matching Method instance is applied, i.e. the data will only be pulled once. + +## Limit instance snapshots + +`[v2.20.0+]` By default, 1000 method instance snapshots will be saved, otherwise memory overflow may occur in frequent request scenarios. You can also adjust the limit as needed. + +```js +import { globalConfig } from 'alova'; + +globalConfig({ + // Limit saving to 500 instance snapshots + limitSnapshots: 500 +}); +``` + +When set to 0, instance snapshots are no longer saved and the method matcher cannot be used. + +```js +globalConfig({ + limitSnapshots: 0 +}); +``` diff --git a/versioned_docs/version-2.x/tutorial/06-advanced/04-middleware.md b/versioned_docs/version-2.x/tutorial/06-advanced/04-middleware.md new file mode 100644 index 000000000..2ffa7b652 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/06-advanced/04-middleware.md @@ -0,0 +1,405 @@ +--- +title: Middleware +sidebar_position: 40 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +Request middleware is an asynchronous function. it provides a powerful ability to control almost all behaviors of a request. If you just use alova, then you probably don't need to use request middleware, because it is mainly used to complete custom request strategies, no matter simple or complex request strategies, you may use it, let's look at it next What magical powers does it have. + +## Middleware function + +Request middleware is an async function, You can define request middleware in `useRequest`, `useWatcher`, `useFetcher`. The following is a simple request middleware, which prints some information before and after the request without changing any request behavior. + +```javascript +useRequest(todoList, { + async middleware(_, next) { + console.log('before request'); + await next(); + console.log('after request'); + } +}); +``` + +Here are a few things you need to know about the `next` function call. This function is also an asynchronous function. Calling it can continue to send requests. At this time, the _loading_ state will be set to true, and then the request will be sent. The return value of next is a Promise instance with the response data, you can manipulate the return value in the middleware function. + +## Control response data + +The return value of the middleware function will be used as the response data of this request to participate in subsequent processing. If the middleware does not return any data but calls `next`, the response data of this request will be used for subsequent processing. + +```javascript +// The modified result will be used as the response data +useRequest(todoList, { + async middleware(_, next) { + const result = await next(); + result.code = 500; + return result; + } +}); + +// Will participate in subsequent processing with the response data of this request +useRequest(todoList, { + async middleware(_, next) { + await next(); + } +}); + +// will respond with the string abc +useRequest(todoList, { + async middleware(_, next) { + await next(); + return 'abc'; + } +}); +``` + +There is also a special case here. When `next` is not called and there is no return value, subsequent processing will not be performed, which means that _onSuccess_, _onError_, _onComplete_ response events will not be triggered. + +```javascript +useRequest(todoList, { + async middleware() {} +}); +``` + +## change request + +Sometimes you want to change the request. At this time, you can specify another method instance in `next`, and the information in this method will be requested when sending the request. At the same time, you can also set whether to force the request through `next` Penetrating the cache is also very simple. + +```javascript +useRequest(todoList, { + async middleware(_, next) { + await next({ + // Change the requested method instance + method: newMethodInstance, + + // Whether to force the request this time + force: true + }); + } +}); +``` + +## Control errors + +### Catch errors + +In the middleware, you can capture the request error generated in `next`, after capturing, the global `onError` hook will no longer be triggered. + +```javascript +useRequest(todoList, { + async middleware(_, next) { + try { + await next(); + } catch (e) { + console.error('Error caught', e); + } + } +}); +``` + +### Throw an error + +Of course, you can also throw a custom error in the middleware, even if the request is normal, it will enter the request error process. + +```javascript +// No request is sent, and global and request-level onError will be triggered at the same time. If the request is sent through `method.send`, the promise instance of rejection will be returned +useRequest(todoList, { + async middleware(_, next) { + throw new Error('error on before request'); + await next(); + } +}); + +// After request is success, global and request-level onError will be triggered at the same time. If the request is sent through `method.send`, the promise instance of rejection will be returned +useRequest(todoList, { + async middleware(_, next) { + await next(); + throw new Error('error on after request'); + } +}); +``` + +## Control response delay + +In the middleware, we can delay the response or respond in advance. In the case of advance, although the response data cannot be obtained, some other data can be returned as the response data to participate in subsequent processing. + +```javascript +// Delay response for 1 second +useRequest(todoList, { + async middleware(_, next) { + await new Promise(resolve => { + setTimeout(resolve, 1000); + }); + return next(); + } +}); + +// Respond immediately and use the string abc as the response data +useRequest(todoList, { + async middleware(_, next) { + return 'abc'; + } +}); +``` + +## More than that + +**So far, all we have mentioned is the use of the second parameter `next` of the middleware, so what is the first parameter for? ** + +The first parameter of the middleware contains some information about this request, as well as the control functions for the status and events returned in useHook such as `loading`, `data` and `onSuccess`. Let's move on! + +## Included request information + + + + +The following is the request information contained in the middleware of useRequest and useWatcher + +```javascript +async function alovaFrontMiddleware(context, next) { + // The method instance of this request + context.method; + + // The parameter array sent by the send function, the default is [] + context.sendArgs; + + // The cache data hit by this request + context.cachedResponse; + + // configuration collection of useHook + context.config; + + // The various states returned by useHook, including the following attributes + // loading, data, error, downloading, uploading, and additional states managed by managedStates + context.frontStates; + //... +} +``` + + + + +The following is the request information contained in the middleware of useFetcher + +```javascript +async function alovaFetcherMiddleware(context, next) { + // The method instance of this request + context.method; + + // The parameter group passed in by the fetch of useFetcher, the default is [] + context.fetchArgs; + + // The cache data hit by this request + context.cachedResponse; + + // configuration collection of useHook + context.config; + + // The various states returned by useHook, including the following attributes + // fetching, error, downloading, uploading + context.fetchStates; + //... +} +``` + + + + +Next, let's take a look at what controls are available. + +## Modify responsive data + +Use `context.update` to modify reactive data. + + + + +```javascript +async function alovaFrontMiddleware(context, next) { + context.update({ + // Modify the loading status to true in advance + loading: true, + + // Modify the data value, such as setting custom initialization data + data: { + /* ... */ + } + }); + //... +} +``` + + + + +```javascript +async function alovaFetcherMiddleware(context, next) { + context.update({ + // Modify the fetching status to true in advance + fetching: true, + + // Modify the value of error + error: new Error('custom midleware error') + }); + //... +} +``` + + + + +## Decorate events + +You can also decorate _onSuccess_, _onError_, _onComplete_ callback functions in middleware to make them richer, such as changing the parameters of the callback function, or receiving the return value of the callback function to achieve more functions. + +You can use `decorateSuccess`, `decorateError`, `decorateComplete` functions to decorate callback functions. The following takes the success callback as an example, which is decorated in 3 places: + +1. Added `custom` attribute to event object; +2. Added a second parameter to the success callback function, the value is `extra data`; +3. Receive the value of the second success callback function and print it; + +```javascript +const { onSuccess } = useRequest(todoList, { + //... + async middleware(context, next) { + // Decorate the successful callback function, the following function parameters are explained: + // handler: bound callback function + // event: the event object corresponding to the callback function + // index: The subscript of the callback function, indicating which callback function is currently being executed + // length: the number of callback functions bound + context.decorateSuccess((handler, event, index, length) => { + event.custom = 1; + const received = handler(event, 'extra data'); + if (index === 1) { + console.log(`received the return value of ${index + 1} callback function:`, received); + // [Print information] Received the return value of the second callback function: I'm second handler + } + }); + //... + } +}); +onSuccess((event, extra) => { + console.log(event.custom); // 1 + console.log(extra); // extra data +}); +onSuccess((event, extra) => { + return "I'm second handler"; +}); +``` + +The usage of `decorateError`, `decorateComplete` is the same as `decorateSuccess`. + +## Abort or repeat send request + +In the middleware, you can also receive `abort` and `send` functions returned by use hooks (`fetch` in useFetcher), and you can also send multiple requests when triggering a request intent. + +A typical usage example is request retry. After sending a request, if the request fails, it will automatically request again according to a certain strategy, and `onSuccess` will be triggered after the retry is successful. The following is a sample code for a simple request retry. + + + + +```javascript +async function alovaFrontMiddleware(context, next) { + return next().catch(error => { + if (needRetry) { + setTimeout(() => { + context.send(...context.sendArgs); + }, retryDelay); + } + return Promise.reject(error); + }); +} +``` + + + + +```javascript +async function alovaFetcherMiddleware(context, next) { + return next().catch(error => { + if (needRetry) { + setTimeout(() => { + context.fetch(context.method, ...context.fetchArgs); + }, retryDelay); + } + return Promise.reject(error); + }); +} +``` + + + + +If you need to abort the request inside the middleware, you can call `context.abort()`. + +## Controlled loading state + +In the above content, we know that you can customize and modify the responsive data through `context.update`, but when you modify the loading status value (`loading` or `fetching`), it will be hindered, because in normal circumstances Next, the loading status value will be automatically set to true when `next` is called, and false will be automatically set in the response process, which will overwrite the loading status value modified by `context.update`, at this time we can turn on the controlled loading status , after it is turned on, the `next` function and the response process will no longer modify the loading status value, but we have full control over it. + +Let's take request retry as an example. We hope that the loading status will remain true after the request is retried until the request ends. + + + + +In the middleware of useRequest and useWatcher, use `context.controlLoading` to enable custom control loading status. + +```javascript +async function alovaFrontMiddleware(context, next) { + context.controlLoading(); + + // Set to true when the request starts + context.update({ loading: true }); + return next() + .then(value => { + // set to false after successful request + context.update({ loading: false }); + return value; + }) + .catch(error => { + if (needRetry) { + setTimeout(() => { + context.send(...context.sendArgs); + }, retryDelay); + } else { + // Also set to false when not retrying again + context.update({ loading: false }); + } + return Promise.reject(error); + }); +} +``` + + + + +In the middleware of useFetching, use `context.controlFetching` to enable custom control loading state. + +```javascript +async function alovaFetcherMiddleware(context, next) { + context.controlFetching(); + + // Set to true when the request starts + context.update({ fetching: true }); + return next() + .then(value => { + // set to false after successful request + context.update({ fetching: false }); + return value; + }) + .catch(error => { + if (needRetry) { + setTimeout(() => { + context.fetch(context.method, ...context.fetchArgs); + }, retryDelay); + } else { + // Also set to false when not retrying again + context.update({ fetching: false }); + } + return Promise.reject(error); + }); +} +``` + + + diff --git a/versioned_docs/version-2.x/tutorial/06-advanced/05-custom-method-key.md b/versioned_docs/version-2.x/tutorial/06-advanced/05-custom-method-key.md new file mode 100644 index 000000000..031585771 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/06-advanced/05-custom-method-key.md @@ -0,0 +1,26 @@ +--- +title: custom method key +sidebar_position: 50 +--- + +:::info version required + +v2.20.0+ + +::: + +Method key is used to identify all data associated with method instances and has a great effect, for example: + +- Caching of associated response data +- Identity sharing request + -Associate the status value returned by useRequest and other useHook + +By default, the method key is generated from the relevant request parameters of the method instance, which can accurately identify a request. + +But sometimes you want to change it so that the above three situations can be recognized as the same method in different requests. + +```javascript +//The method key is generated when creating, you can customize it through __key__ +const methodInst = alovaInstance.Get('/api/user', {}); +methodInst.__key__ = 'my-custom-method-key'; +``` diff --git a/versioned_docs/version-2.x/tutorial/06-advanced/06-error-logger.md b/versioned_docs/version-2.x/tutorial/06-advanced/06-error-logger.md new file mode 100644 index 000000000..7e96a6998 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/06-advanced/06-error-logger.md @@ -0,0 +1,37 @@ +--- +title: Error logger +sidebar_position: 60 +--- + +:::info version required + +v2.6.0+ + +::: + +For the convenience of debugging, when using use hooks to request or respond to an error, the error logger will be printed on the console by default. If you do not want to print an error message or customize the control to print an error message in some cases (such as a production environment), alova also Support for them is provided. + +## Disable error logger + +Log printing can be turned off by setting `errorLogger` to `false or null` when creating an alova instance. + +```javascript +const alovaInstance = createAlova({ + //... + errorLogger: false +}); +``` + +## Custom print error logger + +The error logger is printed by `console.error` by default. If `console.error` is not supported in your project environment, or if you want to collect error information, you can specify `errorLogger` as a function to customize error logger. + +```javascript +const alovaInstance = createAlova({ + //... + // error is the error instance, methodInstance is the method instance corresponding to the error + errorLogger(error, methodInstance) { + reportError(`${methodInstance.url}: ${error.message}`); + } +}); +``` diff --git a/versioned_docs/version-2.x/tutorial/06-advanced/07-cache-logger.md b/versioned_docs/version-2.x/tutorial/06-advanced/07-cache-logger.md new file mode 100644 index 000000000..311c0d67a --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/06-advanced/07-cache-logger.md @@ -0,0 +1,60 @@ +--- +title: cache logger +sidebar_position: 70 +--- + +:::info version required + +v2.8.0+ + +::: + +In order to facilitate debugging when using the interface cache, when the request hits the cache without sending a network request, the hit cache information will be printed on the console by default, which can solve some confusion when using the cache. + +If you don't want to print cache information or custom control print cache information in some cases (such as production environment), alova also provides support for them. + +## Close cache logger printing + +Console printing can be turned off by setting `cacheLogger` to `false or null` when creating an alova instance. + +```javascript +const alovaInstance = createAlova({ + //... + cacheLogger: false +}); +``` + +You can also dynamically turn it on and off according to different environments. + +```javascript +const alovaInstance = createAlova({ + //... + // Enable cache logger in the development environment + cacheLogger: process.env.NODE_ENV === 'development' +}); +``` + +## Custom print cache logger + +The cache logger is printed via `console.log` by default. If `console.log` or other purposes are not supported in your project environment, `cacheLogger` can be specified as a function to customize the logger for processing cache hits. + +```javascript +const alovaInstance = createAlova({ + //... + /** + * Custom cache logger function + * @param response hit cache data + * @param method the current method instance + * @param cacheMode cache mode memory or restore + * @param tag The tag in the restore mode has a value only when the tag is set in the corresponding cache + */ + cacheLogger(response, method, cacheMode, tag) { + saveHitCache({ + response, + method, + cacheMode, + tag + }); + } +}); +``` diff --git a/versioned_docs/version-2.x/tutorial/06-advanced/08-manage-extra-states.md b/versioned_docs/version-2.x/tutorial/06-advanced/08-manage-extra-states.md new file mode 100644 index 000000000..8de90c559 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/06-advanced/08-manage-extra-states.md @@ -0,0 +1,198 @@ +--- +title: Manage extra states +sidebar_position: 80 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +In the previous [Cross Page/Module Update Response States](/tutorial/advanced/update-across-components) chapter, we introduced how to update the response status across pages or modules, but in this chapter we It only introduces updating the `data` state returned by `useRequest` and `useWatcher` through `updateState`, the value of data is always consistent with the response data, but in many cases we will use additional states(such as state A) to display data, and After the request is successful, the data data is appended to the additional state A, such as the pagination scheme of pull-down loading. In this case, we need to manage the additional state A so that it can be updated across pages/modules. + +## Update a single state + +Additional states can be managed via `managedStates` when called by the use hook, and automatically assigned the state name to update it when `updateState` is called in other modules/pages. + + + + +```javascript title="A.vue" +const todoList = page => + alova.Get('/todo', { + name: 'todoList' + }); + +const allTodo = ref([]); +useRequest(todoList, { + //... + + // highlight-start + // manage allTodo as additional state + managedStates: { + allTodo + } + // highlight-end +}); +``` + +```javascript title="B.vue" +const handleSuccess = () => { + // highlight-start + // Pass in an object and specify the state name to look up + updateState('todoList', { + allTodo: allTodoData => { + // Add a new todo item + allTodoData.push({ + title: 'new todo', + time: '10:00' + }); + return allTodoData; + } + }); + // highlight-end +}; +``` + + + + + +```javascript title="A.jsx" +const PageA = () => { + const todoList = page => + alova.Get('/todo', { + name: 'todoList' + }); + + const [allTodo, setAllTodo] = allTodoState = useState([]); + useRequest(todoList, { + //... + + // highlight-start + // manage allTodo as additional state + managedStates: { + allTodo: allTodoState + } + // highlight-end + }); + + return ( + //... + ); +} +``` + +```javascript title="B.jsx" +const PageB = () => { + //... + const handleSuccess = () => { + // highlight-start + // Pass in an object and specify the state name to look up + updateState('todoList', { + allTodo: allTodoData => { + // Add a new todo item + allTodoData.push({ + title: 'new todo', + time: '10:00' + }); + return allTodoData; + } + }); + // highlight-end + }; + + return ( + //... + ); +} +``` + + + + + +```javascript title="A.svelte" +const todoList = page => + alova.Get('/todo', { + name: 'todoList' + }); + +const allTodo = ref([]); +useRequest(todoList, { + //... + + // highlight-start + // manage allTodo as additional state + managedStates: { + allTodo + } + // highlight-end +}); +``` + +```javascript title="B.svelte" +const handleSuccess = () => { + // highlight-start + // Pass in an object and specify the state name to look up + updateState('todoList', { + allTodo: allTodoData => { + // Add a new todo item + allTodoData.push({ + title: 'new todo', + time: '10:00' + }); + return allTodoData; + } + }); + // highlight-end +}; +``` + + + + +:::info Note + +Not support to manage additional states. + +::: + + + + +## Update multiple states + +In the above example, we implemented the update of a single `allTodo` state across pages. In fact, any number of states can be updated at the same time through the object description method of `updateState`. + +```javascript +updateState('todoList', { + state1: state1Data => { + //... + }, + state2: state2Data => { + //... + }, + state3: state3Data => { + //... + } + //... +}); +``` + +It should be noted that the above 3 additional states need to be managed through the `managedStates` property before updating. + +## shorthand for data status update + +When only updating the data state, you can directly pass in the callback function instead of specifying it as an object. + +```javascript +updateState('todoList', { + data: dataRaw => { + //... + } +}); + +// The following are shorthand +updateState('todoList', dataRaw => { + //... +}); +``` diff --git a/versioned_docs/version-2.x/tutorial/06-advanced/09-ssr.md b/versioned_docs/version-2.x/tutorial/06-advanced/09-ssr.md new file mode 100644 index 000000000..e2a09ab2e --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/06-advanced/09-ssr.md @@ -0,0 +1,228 @@ +--- +title: Server-Side Rendering(SSR) +sidebar_position: 90 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info version required + +2.8.0+ + +::: + +## Overview + +Although the positioning of alova is not to make requests in nodejs, we have also adapted it in order to combine the server-side rendering([Nuxt3.x](https://nuxt.com/) / [Nextjs](https://nextjs.org/) / [sveltekit](https://kit.svelte.dev/)) of the UI framework. Although built-in request functionality is provided in e.g. `Nuxt3.x`, `Sveltekit`, if you choose to use alova, you can use alova to manage requests in both server and client, instead of server and client separately. Use different request schemes to manage them. + +Here are some caveats for using alova in SSR, and examples of usage in SSR for different UI frameworks. + +## Call apis on server + +In SSR, it is necessary to get data on server and render it into HTML. In this case, we cannot use alova's use hooks (and do not need to use them) to obtain data. Below we will show the supported SSR frameworks respectively. + +### Nuxt3.x + +In Nuxt3.x, `useAsyncData` is provided to initialize page data on server, and `useFetch` and `$fetch` request functions are also provided. These request functions that can be used on both server and client are really convenient. However, if you want to use alova in nuxt, you can use the combination of **useAsyncData + alova.Method** to complete the server-side data fetching, which is no different from your usual `useAsyncData`. + +```html + +``` + +### Nextjs + +Nextjs provides fixed server-side initialization page data functions, such as `getStaticProps`, `getServerSideProps`, etc., you can [directly use the method instance](/tutorial/getting-started/quick-start) call apis in the function. + +```jsx +const todoListGetter = alovaInstance.Get('/todo/list', { + headers: { + 'Content-Type': 'application/json;charset=UTF-8' + } +}); + +export const getServerSideProps = async ctx => { + const list = await todoListGetter.send(); + return { + props: { + list + } + }; +}; +export default function App(props) { + return props.list.map(item => ( +
+ {item.title} + {item.time} +
+ )); +} +``` + +### Sveltekit + +Sveltekit also provides the `load` function to initialize the page data on server, and you can also [directly use the method instance](/tutorial/getting-started/quick-start) call apis in the function. For example, call apis in `+page.server.js`. + +```javascript title=+page.server.js +const todoListGetter = alovaInstance.Get('/todo/list', { + headers: { + 'Content-Type': 'application/json;charset=UTF-8' + } +}); + +/** @type {import('./$types').PageServerLoad} */ +export async function load({ params }) { + return { + list: todoListGetter.send() + }; +} +``` + +## Using usehooks in SSR + +Since each SSR framework has its own way to initialize data on server, when generating html in SSR, `useRequest` and `useWatcher` in the component will not be initiated even if `immediate` is set to `true` request, as this is more like client initialization data. + +However, if you need to initialize the data of the page as in the client, you can also set `immediate` to `true`, and when the page is running in the browser, you can use all the functions of alova as usual. + +## Precautions + +### Client and server caches are separate + +If you use alova's caching function, you may need to pay attention here that the client and server caches are not shared, which means that if you directly use **usehooks** to get data when initializing the page, you may Run into inconsistencies in client-side and server-side rendering, although few people do. + +Please see the following code snippet. + + + + +```html + + + +``` + + + + +```jsx +function App(props) { + const { loading, data } = useRequest(alovaGetter); + return ( + <> + {loading ?
loading
: null} +
{data}
+ + ); +} +``` + +
+ + +```html + + +{#if $loading} +
loading
+{/if} +
{{ data }}
+``` + +
+
+ +The following code assumes that `alovaGetter` requests are cached on server, but not on the client. + +At this time, when the html is generated on server , `loading` is `false` and does not display `
loading
` because it hits the cache, but when the client is initialized, because it misses the cache, `loading` is `true` will cause `
loading
` to be displayed, and the SSR framework will prompt that the rendering of the two ends is inconsistent. + +**Solution** + +1. Try to put the page data initialization work in the acquisition function instead of the component; +2. If you must do this, you can avoid using the same apis on the client and server, or turn off the problematic api caches; +3. If caching is also required, you can clear the cache on server in the data initialization function of server. The sample code is as follows: + + + + +```html + + + +``` + + + + +```jsx +import { invalidateCache } from 'alova'; + +function App(props) { + const { loading, data } = useRequest(alovaGetter); + return ( + <> + {loading ?
loading
: null} +
{data}
+ + ); +} + +export const getServerSideProps = async () => { + // Clear the cache on server + invalidateCache(alovaGetter); + return { + props: {} + }; +}; +``` + +
+ + +```javascript title=+page.server.js +import { invalidateCache } from 'alova'; + +/** @type {import('./$types').PageServerLoad} */ +export async function load({ params }) { + // Clear the cache on server + invalidateCache(alovaGetter); + return {}; +} +``` + + +
diff --git a/versioned_docs/version-2.x/tutorial/06-advanced/README.md b/versioned_docs/version-2.x/tutorial/06-advanced/README.md new file mode 100644 index 000000000..feaeb352d --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/06-advanced/README.md @@ -0,0 +1,9 @@ +--- +title: 进阶 +--- + +import DocCardList from '@theme/DocCardList'; + +进阶教程可以让你更深入地了解 alova 的一些特性,它们可能并不是常用功能,但可以帮你快速解决更多特殊的请求问题。 + + diff --git a/versioned_docs/version-2.x/tutorial/06-advanced/_category_.json b/versioned_docs/version-2.x/tutorial/06-advanced/_category_.json new file mode 100644 index 000000000..3462df72e --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/06-advanced/_category_.json @@ -0,0 +1,3 @@ +{ + "label": "Advanced" +} diff --git a/versioned_docs/version-2.x/tutorial/07-best-practice/01-manage-apis.md b/versioned_docs/version-2.x/tutorial/07-best-practice/01-manage-apis.md new file mode 100644 index 000000000..54127698e --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/07-best-practice/01-manage-apis.md @@ -0,0 +1,154 @@ +--- +title: Manage APIs +sidebar_position: 10 +--- + +In a project, we may need to use hundreds or thousands of request APIs, so managing these request APIs becomes particularly important. + +You may write the request code like the code snippet in [quick start](/tutorial/getting-started/quick-start). all codes in one file. + +```javascript +const { loading, data, error } = useRequest( + alovaInstance.Get('https://api.alovajs.org/profile', { + params: { + id: 1 + } + }) +); +``` + +This is just for beginners to understand, but in actual projects, we do not recommend this, because the method instance is not only used to send requests, it can also be used to operate cache and state, the above usage will make these request api become It's unmanageable, and if you think it's wrong, you might forget a little: + +> The key of the response data cache is uniquely identified by the combination of the method instance’s request method (method), request address (url), request header parameters (headers), url parameters (params), and request body parameters (requestBody). Or different positions will be treated as different keys. + +Therefore, in actual projects, method instances should be managed, and alova instances can also be managed uniformly. + +## api file structure + +First of all, your project needs a folder that uniformly stores method instances and alova instances, for example called `api`, the following is a common api management structure, and you can also use any structure suitable for the project. + +``` +|-api +| |-index.js -> contains all alova instances +| |-methods +| | |-user.js +| | |-article.js +| | |-order.js +| | |-... +|-... +``` + +In short, your project should use a suitable folder structure to organize them. + +> Next, take vue as an example to show the sample code + +## Manage alova instance + +Your project may need to communicate with different servers, or you may need to use special request schemes in specific requests, or use different response interceptors, etc. All of these require creating and maintaining multiple alova instances in the project. It is recommended to Use a separate file to manage them, for example in the above api management structure, will use `api/index.js` to manage. + +```javascript title=api/index.js +import { createAlova } from 'alova'; +import VueHook from 'alova/vue'; +import GlobalFetch from 'alova/GlobalFetch'; +import { axiosRequestAdapter } from '@alova/adapter-axios'; + +// user alova instance +export const userAlova = createAlova({ + baseURL: 'https://api-user.alovajs.org', + statesHook: VueHook, + requestAdapter: GlobalFetch(), + async responded(method) { + method.config.headers.token = 'user token'; + } +}); + +// order alova instance +export const orderAlova = createAlova({ + baseURL: 'https://api-order.alovajs.org', + statesHook: VueHook, + requestAdapter: GlobalFetch(), + async responded(method) { + method.config.headers.token = 'order token'; + } +}); + +// upload alova instance +export const uploadAlova = createAlova({ + baseURL: 'https://api-order.alovajs.org', + statesHook: VueHook, + requestAdapter: axiosRequestAdapter() +}); +``` + +## Manage method instances + +We can use different js files to classify and manage method instances. For example, in the above api management structure, `api/methods/user.js` will be used to manage method instances related to user information, and `api/methods/order.js` will be used `A method instance related to order management. + +In addition, as mentioned above, in addition to sending requests, method instances can also be used to operate caches and states. In order to ensure the number and order of request parameters, we can use a function to correspond to a request API, through The corresponding method instance is returned in the form of incoming request parameters. As long as the incoming parameters are the same, the request information and parameter order of the method instance are also the same, so as to ensure that the method instance used to operate the cache and state is correct. + +```javascript title=api/methods/user.js +import { userAlova } from '..'; + +// Get user information +export const getUserInfo = id => userAlova.Get('/user/' + id); + +// Edit user information +export const editUserInfo = (name, age, mobile) => + userAlova.Post('/user', { + name, + age, + mobile + }); + +// remove user +export const removeUser = id => userAlova.Delete('/user/' + id); + +//... +``` + +In the **user component**, the method function can be directly imported for use, and the method function can be used again to invalidate the corresponding cache after calling `invalidateCache`. + +```html title=views/user.vue + + +``` diff --git a/versioned_docs/version-2.x/tutorial/07-best-practice/02-skills.md b/versioned_docs/version-2.x/tutorial/07-best-practice/02-skills.md new file mode 100644 index 000000000..ab20c258e --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/07-best-practice/02-skills.md @@ -0,0 +1,330 @@ +--- +title: Skills +sidebar_position: 20 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +The following are the better usage skills used by alova developers when using alova. They are collected from multiple parties and organized here. I hope that everyone can use alova more smoothly. + +## Send request useRequest OR method + +The `useRequest` provided by alova will only send a request and get the response data under normal circumstances, so why not use the method instance to send the request directly, because `useRequest` can help us automatically manage `loading` and `data` , `error` and other responsive data that can be used directly, so if you need to use these states, use `useRequest` without maintaining the data yourself. But on the contrary, you don't need to only apply `useRequest` in the whole project. For example, when you only care about getting information and don't need to use `loading`, `error`, etc., when getting data outside the component, you can use method instance to send the request. + +## Update state and cache at the same time + +When you finish editing a piece of data in a list, you don't want to re-request to update the list data again, but manually update the list data. Many developers may directly modify the list data. + + + + +```html + + +``` + + + + + +```jsx +//... + +const App = () => { + const { data: listData } = useRequest(getList, { + initialData: [] + }); + + // directly updated listData + const handleItemSubmit = item => { + const index = listData.findIndex(({ id }) => id === item.id); + listData.splice(index, 1, item); + }; + + return ( + <> + + + + ); +}; +``` + + + + + +```html + + + +``` + + + + +**❌ This way of writing is not recommended** + +Although this can trigger the interface to refresh, it may cause another problem, that is, when the list data is cached, because the cached data has not been updated, the hit cache is still the original data when entering the list page again. + +So you can call `updateState` to update the stateful data and update the cache immediately. + + + + +```html + + +``` + + + + + +```jsx +//... + +const App = () => { + const { data: listData } = useRequest(getList, { + initialData: [] + }); + + // Update listData through updateState, the cache will be updated at the same time + const handleItemSubmit = item => { + updateState(getList(), oldListData => { + const index = oldListData.findIndex(({ id }) => id === item.id); + oldListData.splice(index, 1, item); + return oldListData; + }); + }; + + return ( + <> + + + + ); +}; +``` + + + + + +```html + + + +``` + + + + +## Quickly get sendArgs in onSuccess + +In actual projects, data is often passed through the `send` function. If you need to use these data in callback functions such as onSuccess, since they exist in the `event.sendArgs` array, you can use the double destructuring method to directly obtain them to the data. + +```javascript +onSuccess(({ sendArgs: [content] }) => { + console.log(content); +}); +``` + +## Use prefixes to manage similar method instances + +In many scenarios, we need to invalidate multiple caches at the same time. For example, the data of a page comes from multiple interfaces. When editing the data of this page, it is necessary to invalidate the cached data of these interfaces at the same time. You can method instances with the same prefix to classify them, and use this regex to invalidate caches with the same prefix. + +```javascript +const getData1 = id => alovaInstance.Get('/data1', { + name: `data-${id}-1`, + params: { + id + } +}); +const getData2 = id => alovaInstance.Get('/data2', { + name: `data-${id}-2`, + params: { + id + } +}); +const getData3 = id => alovaInstance.Get('/data3', { + name: `data-${id}-3`, + params: { + id + } +}); + +const handleInvalidateCache = id => { + // Simultaneously invalidate the 3 cached data of the specified id + invalidateCache(new RegExp(`^data-${id}`); +} +``` + +## Mock data practice + +If your project needs to use mock data to simulate some or all interfaces in the development environment, and switch back to real network requests in production, you can control it through environment variables. + +```javascript +const globalFetch = GlobalFetch(); +const mockAdapter = createAlovaMockAdapter([mockGroup1 /** ... */], { + httpAdapter: globalFetch, + delay: 1000 +}); + +export const alovaInst = createAlova({ + baseURL: 'http://xxx', + + // Control the production environment through environment variables, and will not package mock related codes + requestAdapter: process.env.NODE_ENV === 'development' ? mockAdapter : globalFetch + //... +}); +``` + +And it is recommended that different developers in the team can create different mock interface data according to the version number of each iteration, so as to manage these mock data in the team. For details, please refer to the chapter of [mock Data](/tutorial/request-adapter/alova-mock) . + +## Use useRequest to make parallel requests + +For simple parallel requests, you only need to call multiple useRequest at the same time. + +```javascript +const { data: todoList } = useRequest(todoListGetter); +const { data: todoCounter } = useRequest(todoCountGetter); +``` + +But such a request only applies to simple parallel requests. If you need to perform certain operations after all parallel requests are completed, there are two ways to achieve it: + +### method 1 + +Manually create a promise object and use `Promise.all` to complete the effect. + +```javascript +const { data: todoList, onSuccess: onListSuccess, onError: onListError } = useRequest(todoListGetter); +const { data: todoCounter, onSuccess: onCountSuccess, onError: onCountError } = useRequest(todoCountGetter); + +// Manually create promise object +const listPromise = new Promise((resolve, reject) => { + onListSuccess(resolve); + onListError(reject); +}); +const countPromise = new Promise((resolve, reject) => { + onCountSuccess(resolve); + onCountError(reject); +}); +const [listEvent, countEvent] = await Promise.all([listPromise, countPromise]); +// Parallel request is completed, continue processing business... +``` + +### Method 2 + +Using the `send` function returned by the `useRequest` function, calling `send` will return a usable promise object. + +```javascript +// Let them not automatically send requests first +const { send: sendList } = useRequest(todoListGetter, { immediate: false }); +const { send: sendCount } = useRequest(todoCountGetter, { immediate: false }); + +//Use the promise object returned by the send function +const parallelRequest = async () => { + const [listResponse, countResponse] = await Promise.all([sendList(), sendCount()]); + // Parallel request is completed, continue processing business... +}; +``` + +## Use useRequest serial request + +Serial requests also have two modes. + +### method 1 + +Let the first request be sent automatically, and the second request be triggered in the `onSuccess` callback of the first request to complete the serial request. The serial request can be completed by the following writing method: + +```javascript +// +const { data: todoList, onSuccess } = useRequest(todoListGetter); +const { data: todoDetail, send: sendTodoDetail } = useRequest(todoId => todoDetailGetter(todoId), { immediate: false }); + +// Get the list first, then get the details of the first todo +onSuccess(event => { + sendTodoDetail(event.todoList[0].id); +}); +``` + +### Method 2 + +Using the `send` function returned by the `useRequest` function, calling `send` will return a usable promise object. + +```javascript +// Let them not automatically send requests first +const { send: sendList } = useRequest(todoListGetter, { immediate: false }); +const { send: sendTodoDetail } = useRequest(todoId => todoDetailGetter(todoId), { immediate: false }); + +//Use the promise object returned by the send function +const serialRequest = async () => { + const todoList = await sendList(); + const todoDetail = await sendTodoDetail(todoList[0].id); + // The serial request is completed, continue processing business... +}; +``` + +> For serial requests, it is recommended to use [useSerialRequest](/tutorial/strategy/useSerialRequest) and [useSerialWatcher](/tutorial/strategy/useSerialWatcher) directly. diff --git a/versioned_docs/version-2.x/tutorial/07-best-practice/03-manage-cache-by-indexeddb.md b/versioned_docs/version-2.x/tutorial/07-best-practice/03-manage-cache-by-indexeddb.md new file mode 100644 index 000000000..c3cc9a96a --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/07-best-practice/03-manage-cache-by-indexeddb.md @@ -0,0 +1,123 @@ +--- +title: Manage Cache with IndexedDB +sidebar_position: 30 +--- + +If you are developing applications that require extensive use of local cache, such as graphics editing applications, file management applications, etc., the low-capacity localStorage can no longer meet the development needs. At this time, you can use IndexedDB and alova for large-capacity local cache management. + +This feature is mainly due to alova's [Controlled Cache](/tutorial/cache/controlled-cache) function, which can implement custom cache management. Let's take a look at the practical steps. + +Here is an [example of managed cache with IndexedDB](/tutorial/example/controlled-cache-by-indexeddb) + +Let's take custom management of large image data as an example. + +## Create IndexedDB instance + +First create an IndexedDB instance to operate the local cache, and export the cache operate function. + +```javascript title=db.js +const dbVersion = 1; +let dbInstance; +const request = window.indexedDB.open('MyTestDatabase', dbVersion); +request.onupgradeneeded = ({ target }) => { + dbInstance = target.result; + const imgStore = dbInstance.createObjectStore('images', { + autoIncrement: true + }); + imgStore.createIndex('fileName', 'fileName', { + unique: true + }); +}; +request.onerror = () => { + throw new Error('Database open fail'); +}; +request.onsuccess = ({ target }) => { + dbInstance = target.result; +}; + +// Add new data to IndexedDB +export const addImage2Cache = async (fileName, data) => { + const tx = dbInstance.transaction(['images'], 'readwrite'); + const request = tx.objectStore('images').add({ + fileName, + data + }); + return new Promise((resolve, reject) => { + request.onerror = () => { + reject('data add fail'); + }; + request.onsuccess = ({ result }) => { + resolve(result); + }; + }); +}; + +// Get file data according to fileName +export const getImageFromCache = async fileName => { + const tx = dbInstance.transaction(['images']); + const request = tx.objectStore('images').index('fileName').get(fileName); + return new Promise((resolve, reject) => { + request.onerror = () => { + reject('data add fail'); + }; + request.onsuccess = ({ target }) => { + resolve(target.result); + }; + }); +}; +``` + +## save data + +When saving data, we can save the cache in the `transformData` of the method, because `transformData` will only be triggered when the network request responds, but will not be triggered when the cache is hit. In the sample code, convert the image blob instance to base64 data, cache and return this base64 data. + +```javascript-api.js +import { addImage2Cache } from './db'; + +export const image = fileName => + alovaInst.Get(`/image/${fileName}`, { + // highlight-start + async transformData(imgBlob) { + // Asynchronously convert the blob to base64 + const reader = new FileReader(); + reader.readAsDataURL(imgBlob); + const base64Img = await new Promise(resolve => { + reader.onload = ({ target }) => { + resolve(target.result); + }; + }); + + // Cache image data to IndexedDB + await addImage2Cache(fileName, base64Img); + return base64Img; + } + // highlight-end + }); +``` + +## retrieve data + +Specify `localCache` of this method instance as an asynchronous function to change the cache into a controlled state, match the cache in IndexedDB in this function, and return it if it matches, otherwise return `undefined` and continue to initiate a request to obtain data. + +```javascript title=api.js +import { getImageFromCache } from './db'; + +export const image = fileName => + alovaInst.Get(`/image/${fileName}`, { + async transformData(imgBlob) { + //... + }, + + // highlight-start + async localCache() { + // get cache + const cache = await getImageFromCache(fileName); + return cache && cache.data; + } + // highlight-end + }); +``` + +In this way, a basic custom cache management is basically completed. You can also save the expiration time of the cache, and judge whether it has expired when the cache is matched in `localCache`, so as to realize the cache expiration function. + +IndexedDB is just one example of managing caches asynchronously, you can also connect to your cache servers to manage them. diff --git a/versioned_docs/version-2.x/tutorial/07-best-practice/04-multiple-servers.md b/versioned_docs/version-2.x/tutorial/07-best-practice/04-multiple-servers.md new file mode 100644 index 000000000..a08908d29 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/07-best-practice/04-multiple-servers.md @@ -0,0 +1,20 @@ +--- +title: Multiple servers +sidebar_position: 40 +--- + +If your project needs to request multiple servers, you can create multiple alova instances to correspond to different servers. In order to easily distinguish between different environments, you can also use environment variables to manage the hosts of multiple servers. + +```ts +import { createAlova } from '@alova/core'; + +//Create user-related alova instance +const userAlova = createAlova({ + baseURL: VITE_API_USER +}); + +//Create order-related alova instances +const alova2 = createAlova({ + baseURL: VITE_API_ORDER +}); +``` diff --git a/versioned_docs/version-2.x/tutorial/07-best-practice/05-middleware.md b/versioned_docs/version-2.x/tutorial/07-best-practice/05-middleware.md new file mode 100644 index 000000000..0341d9f0c --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/07-best-practice/05-middleware.md @@ -0,0 +1,29 @@ +--- +title: Common middleware practices +sidebar_position: 50 +--- + +## Delay update loading + +When the response is very fast, the loading status will flash once, which will bring a bad experience to the user. Delaying the loading update can make the loading status display after a period of time. If the response is completed within this period, it will not appear. Loading status. Let's implement a middleware with delayed update loading. + +```javascript +const delayLoadingMiddleware = + (delayTimer = 1000) => + async (ctx, next) => { + //Control loading by yourself + ctx.controlLoading(); + + //Delay updates for a specific time + const timer = setTimeout(() => { + ctx.update({ loading: true }); + }, delayTimer); + await next(); + ctx.update({ loading: false }); + clearTimeout(timer); + }; + +useRequest(methodInstance, { + middleware: delayLoadingMiddleware() +}); +``` diff --git a/versioned_docs/version-2.x/tutorial/07-best-practice/_category_.json b/versioned_docs/version-2.x/tutorial/07-best-practice/_category_.json new file mode 100644 index 000000000..fe9037f89 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/07-best-practice/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Best practice", + "link": { + "type": "generated-index" + } +} diff --git a/versioned_docs/version-2.x/tutorial/08-request-adapter/01-alova-mock.md b/versioned_docs/version-2.x/tutorial/08-request-adapter/01-alova-mock.md new file mode 100644 index 000000000..e668d17c3 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/08-request-adapter/01-alova-mock.md @@ -0,0 +1,331 @@ +--- +title: Mock data +sidebar_position: 10 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +This mock plug-in is an alova request adapter. Different from the traditional Proxy form, you can control the scope of use of mock data. You can control the global scope, a group of interface scopes, and even the enabling and use of a certain interface. Disabled, which is very useful in our actual business scenarios. Each iteration will add or modify a set of interfaces. We hope that the previous functions will still follow the developed interfaces, and let the new or modified interfaces Taking the simulation data, at this time, each developer can group the interfaces involved in this iteration into a group, and turn them on or off. + +## Features + +- ✨Works seamlessly with alova +- ✨Arbitrary grouping of simulation requests to control global, group, and individual simulation interface enable and disable +- ✨Works with mockjs +- ✨Do not pollute the production environment + +## Install + + + + +```bash +npm install @alova/mock --save +``` + + + + +```bash +yarn add @alova/mock +``` + + + + +The following is the usage flow. + +## Usage + +### Define the mock interface + +Use `defineMock` to define a set of mock interfaces. You can directly specify the return response data in each mock interface, or specify the response data to be dynamically calculated for the callback function. + +```javascript title=mockGrou1.js +import { defineMock } from '@alova/mock'; + +export default defineMock( + { + // capture get request + '/todo': [1, 2, 3, 4], + + // rest style request + '/todo/{id}': ({ params }) => { + const id = params.id; + // ... + return { + title: '...', + time: '10:00' + }; + }, + + // capture post request + '[POST]/todo': ({ query, data }) => { + // ... + return { success: true }; + }, + + // return more detailed information + '[POST]/todo': ({ query, data }) => { + //... + return { + status: 403, + statusText: 'unknown error', + responseHeaders: { + //... + }, + body: { + success: true + } + }; + }, + + // simulate network error + '[POST]/todo': ({ query, data }) => { + throw new Error('network error'); + }, + + // Add `-` before the key to disable this mock interface + '-[DELETE]/todo/{id}': ({ params }) => { + // ... + return { success: true }; + } + }, + true +); // The second parameter indicates whether to enable this group of mock interfaces, the default is true, and can be specified as false to close +``` + +### Create mock request adapter + +Create a mock request adapter when calling `createAlova`, and pass in the mock interface to complete. + +```javascript +import GlobalFetch from 'alova/GlobalFetch'; +import { createAlovaMockAdapter } from '@alova/mock'; +import mockGroup1 from './mockGroup1'; + +// highlight-start +const mockAdapter = createAlovaMockAdapter([mockGroup1, /** ... */], { + // Global control whether the mock interface is enabled, the default is true + enable: true, + + // Non-mock request adapter, used to send requests when the mock interface is not matched + httpAdapter: GlobalFetch(), + + // mock interface response delay, in milliseconds + delay: 1000, + + // Whether to print mock interface request information + mockRequestLogger: true, + + // Simulation interface callback, data is the returned simulation data, you can use it to construct any object you want and return it to alova + // The following is the default callback function, which is suitable for requesting the adapter using GlobalFetch + // If you are using other request adapters, please customize the return data structure suitable for the adapter in the mock interface callback + onMockResponse: data => new Response(JSON.stringify(data)) +}); +// highlight-end + +export const alovaInst = createAlova({ + baseURL: 'http://xxx', + + // Use the mock request adapter, if you need to switch adapters, please see the following practical suggestions + requestAdapter: mockAdapter, + + statesHook: /** ... */ +}); +``` + +### Paths match mode + +:::info version required + +1.5.0+ + +::: + +By default, the path defined in `defineMock` is the full pathname of a url, see the following code snippet. + +```javascript +const alovaInst = createAlova({ + baseURL: 'https://api.alovajs.org' + //... +}); +alovaInst.Get('/user?id=1').send(); +``` + +When the request path in the example is `https://api.alovajs.org/user?id=1`, its full pathname is `/user`, which can match `/user` in `defineMock`. + +Usually this is enough, but when your baseURL is not just a domain name. + +```javascript +const alovaInst = createAlova({ + baseURL: 'https://api.alovajs.org/v1/subname' + //... +}); +alovaInst.Get('/user?id=1').send(); +``` + +In this example, the request path is `https://api.alovajs.org/v1/subname/user?id=1`, the matching path of the mock is `/v1/subname/user`, and `/ in the baseURL needs to be v1/subname` is also written together, which is slightly redundant when the number of interfaces is large. + +At this point, you can set `matchMode` to `methodurl` in `createAlovaMockAdapter`, it will only match the url defined in the method instance, for example, the above instance will match `/user?id=1` instead of The part in baseURL needs to be written. On the contrary, if the url in the method instance has a get parameter, it also needs to be written in the matching path of `defineMock`, just like `?id=1` here. + +```javascript +createAlovaMockAdapter([mockGroup1 /** ... */], { + //... + // highlight-start + matchMode: 'methodurl' + // highlight-end +}); +``` + +## Practical advice + +### Group interfaces per developer per version + +In the team development scenario, we often only need to simulate some undeveloped interfaces for each version development, and use the test environment interface for the interface of the previous version. At this time, in order to achieve better simulation interface management, you can use The two dimensions, development version and developer, group interfaces. + +For example, there are two developers named _August_, _kevin_, they are developing v1.1 product features, they can manage the mock interface like this. + +```javascript title=August-v1.1.js +import { defineMock } from '@alova/mock'; + +export default defineMock({ + '/todo': [ + /** */ + ], + '[POST]/todo': ({ data }) => { + // ... + // return... + } + // ... +}); +``` + +```javascript title=kevin-v1.1.js +import { defineMock } from '@alova/mock'; + +export default defineMock({ + '[PUT]/todo/add': ({ data }) => { + // ... + // return... + }, + '[DELETE]/todo/remove': ({ data }) => { + // ... + // return... + } + // ... +}); +``` + +```javascript title=request.js +import Augustv1_1 from './August-v1.1'; +import Keevenv1_1 from './kevin-v1.1'; + +const mockAdapter = createAlovaMockAdapter([Augustv1_1, kevinv1_1], { + httpAdapter: GlobalFetch(), + delay: 1000 +}); +export const alovaInst = createAlova({ + baseURL: 'http://xxx', + requestAdapter: mockAdapter + // ... +}); +``` + +### Exclude mock code in production + +The mock data is generally only used in the development environment, and will be switched to the actual interface in the production environment, so this mock code becomes useless in the production environment. At this time, we can exclude this code by judging the environment variables. , you just need to do: + +```javascript +const globalFetch = GlobalFetch(); +const mockAdapter = createAlovaMockAdapter([mockGroup1, /** ... */], { + httpAdapter: globalFetch, + delay: 1000, +}); + +export const alovaInst = createAlova({ + baseURL: 'http://xxx', + + // highlight-start + // In the production environment controlled by environment variables, the mock-related code will not be packaged in + requestAdapter: process.env.NODE_ENV === 'development' ? mockAdapter : globalFetch, + // highlight-end + + statesHook: /** ... */ +}); +``` + +### Use with mockjs + +If you don't want to write the mock data yourself, but use it with a mock js library (such as mockjs), you can do so. + +```javascript +import { defineMock } from '@alova/mock'; +import Mock from 'mockjs'; + +export default defineMock({ + '/api1': Mock.mock({ + 'id|1-10000': 100 + }) +}); +``` + +## Convert mock data + +**@alova/mock** By default, the response data is packaged as a Response instance, and the response header is packaged as a Headers instance by default, which is adapted for `GlobalFetch`, but if you use other request adapters, you need to mock the data Convert to the corresponding format. + +### Convert response data + +You can intercept the mock response data in the `onMockResponse` field and return the transformed response data and response headers. + +> You can also throw an ERROR in onMockResponse to indicate a failure request. + +```javascript +const mockAdapter = createAlovaMockAdapter( + [ + /* mock data */ + ], + { + //... + // highlight-start + onMockResponse(response, request, currentMethod) { + // response is the corresponding data set, which contains status, statusText, responseHeaders, body + // request is the request data, which contains query, params, headers, data + // currentMethod is the method instance of the current request + //... + // Return converted response data and response headers + return { + response: /** response data */, + headers: /** Response headers */ + }; + } + // highlight-end + } +); +``` + +### Convert Error Instance + +You can intercept the error instance in the `onMockError` field and return the converted error message. + +> You can also throw an ERROR in onMockResponse to indicate failure request. + +```javascript +const mockAdapter = createAlovaMockAdapter( + [ + /* mock data */ + ], + { + //... + // highlight-start + onMockError(error, currentMethod) { + // error is an error instance + // currentMethod is the method instance of the current request + //... + // Return the converted error message collection + } + // highlight-end + } +); +``` diff --git a/versioned_docs/version-2.x/tutorial/08-request-adapter/02-alova-adapter-xhr.md b/versioned_docs/version-2.x/tutorial/08-request-adapter/02-alova-adapter-xhr.md new file mode 100644 index 000000000..4959a1b1f --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/08-request-adapter/02-alova-adapter-xhr.md @@ -0,0 +1,337 @@ +--- +title: XMLHttpRequest Adapter +sidebar_position: 20 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## Install + + + + +```bash +npm install @alova/adapter-xhr --save +``` + + + + +```bash +yarn add @alova/adapter-xhr +``` + + + + +## Instructions + +### create alova + +Use **xhrRequestAdapter** as request adapter for alova. + +```javascript +import { createAlova } from 'alova'; +import { xhrRequestAdapter } from '@alova/adapter-xhr'; + +const alovaInst = createAlova({ + //... + requestAdapter: xhrResponseAdapter() + //... +}); +``` + +### Request + +The XMLHttpRequest adapter provides basic configuration parameters, including `responseType`, `withCredentials`, `mimeType`, `auth`, as follows: + + + + +```html + +
Loading...
+
The request data is: {{ data }}
+ + + +``` + +
+ + +```jsx +const list = () => + alovaInst.Get('/list', { + /** + * Set the response data type + * Can be set to change the response type. Values are: "arraybuffer", "blob", "document", "json" and "text" + * defaults to "json" + */ + responseType: 'text', + + /** + * True when credentials are to be included in cross-origin requests. false when they are excluded from cross-origin requests and when cookies are ignored in their responses. Default is false + */ + withCredentials: true, + + /** + * Set the mimeType of the response data + */ + mimeType: 'text/plain; charset=x-user-defined', + + /** + * auth means use HTTP Basic authentication and provide credentials. + * This will set an `Authorization` header, overriding any existing + * Custom headers for `Authorization` set using `headers`. + * Note that only HTTP Basic authentication can be configured via this parameter. + * For Bearer tokens etc., use the `Authorization` custom header instead. + */ + auth: { + username: 'name1', + password: '123456' + } + }); + +const App = () => { + const { loading, data } = useRequest(list); + + return ( + { loading ?
Loading...
: null } +
The request data is: { JSON.stringify(data) }
+ ) +}; +``` + +
+ + +```html + + +{#if $loading} +
Loading...
+{/if} +
The request data is: { data }
+``` + +
+
+ +### Upload + +Use `FormData` to upload files, and this `FormData` instance will be sent to the server through `xhr.send`. It will be set `Content-Type` automatically, you don't need to custom it with `multipart/form-data`. + +```javascript +const uploadFile = imageFile => { + const formData = new FormData(); + formData.append('file', imageFile); + return alovaInst.Post('/uploadImg', formData, { + // Start upload progress + enableUpload: true + }); +}; + +const { + loading, + data, + uploading, + send: sendUpload +} = useRequest(uploadFile, { + immediate: false +}); + +// Picture selection event callback +const handleImageChoose = ({ target }) => { + sendUpload(target.files[0]); +}; +``` + +### download + +Point the request url to the file address to download, you can also enable the download progress by setting `enableDownload` to true. + +```javascript +const downloadFile = () => + alovaInst.Get('/bigImage.jpg', { + // Start download progress + enableDownload: true, + responseType: 'blob' + }); + +const { loading, data, downloading, send, onSuccess } = useRequest(downloadFile, { + immediate: false +}); +onSuccess(({ data: imageBlob }) => { + // download image + const anchor = document.createElement('a'); + anchor.href = URL.createObjectURL(blob); + anchor.download = 'image.jpg'; + anchor.click(); + URL.revokeObjectURL(anchor.href); +}); +const handleImageDownload = () => { + send(); +}; +``` + +## Mock request adapter compatible + +When developing applications, we may still need to use simulated requests. Only by default, the response data of [Mock Request Adapter (@alova/mock)](/tutorial/request-adapter/alova-mock) is a `Response` instance, which is compatible with the `GlobalFetch` request adapter by default. When using the XMLHttpRequest adapter, we You need to adapt the response data of the mock request adapter to the XMLHttpRequest adapter. In this case, you need to use the `xhrMockResponse` exported in the **@alova/adapter-xhr** package as the response adapter. + +```javascript +import { defineMock, createAlovaMockAdapter } from '@alova/mock'; +import { xhrRequestAdapter, xhrMockResponse } from '@alova/adapter-xhr'; + +const mocks = defineMock({ + //... +}); + +// mock data request adapter +export default createAlovaMockAdapter([mocks], { + // After specifying the request adapter, requests that do not match the simulated interface will use this adapter to send requests + httpAdapter: xhrRequestAdapter(), + + // Use xhrMockResponse to adapt the simulated data to the XMLHttpRequest adapter + onMockResponse: xhrMockResponse +}); + +export const alovaInst = createAlova({ + //... + // Control whether to use the simulated request adapter through environment variables + requestAdapter: process.env.NODE_ENV === 'development' ? mockAdapter : xhrRequestAdapter() +}); +``` + +## Typescript + +The XMLHttpRequest request adapter provides complete type adaptation. + +### method configuration + +When creating a method instance, in addition to the common configuration items in the method, you can also use the configuration items in `AlovaXHRRequestConfig`. + +```typescript +/** + * xhr request configuration parameters + */ +interface AlovaXHRRequestConfig { + /** + * Set the response data type. + * + * Can be set to change the response type. Values are: "arraybuffer", "blob", "document", "json", and "text". + * Setting 1: If the current global object is not a Window object, the setting to "document" is ignored. + * Setup 2: Throw an "InvalidStateError" DOMException if the state is loading or complete. + * Setting 3: Throws an "InvalidAccessError" DOMException if the sync flag is set and the current global object is a Window object. + * @default "json" + */ + responseType?: XMLHttpRequestResponseType; + + /** + * True when credentials are to be included in cross-origin requests. false when they are excluded from cross-origin requests and when cookies are ignored in their responses. The default is false. + * An 'InvalidStateError' DOMException is thrown if the state is not sent or not opened, or if the send() flag is set. + * @default false + */ + withCredentials?: boolean; + + /** + * Set the mimeType of the response data + */ + mimeType?: string; + + /** + * `auth` indicates that HTTP Basic authentication should be used, and credentials are provided. + * This will set an `Authorization` header, overriding any existing + * Custom headers for `Authorization` set using `headers`. + * Note that only HTTP Basic authentication can be configured via this parameter. + * For Bearer tokens etc., use the `Authorization` custom header instead. + */ + auth?: { + username: string; + password: string; + }; +} +``` + +### Response data + +XMLHttpRequest adapter response data is as follows: + +```typescript +interface AlovaXHRResponseHeaders { + [x: string]: any; +} +interface AlovaXHRResponse { + status: number; + statusText: string; + data: T; + headers: AlovaXHRResponseHeaders; +} +``` diff --git a/versioned_docs/version-2.x/tutorial/08-request-adapter/03-alova-adapter-axios.md b/versioned_docs/version-2.x/tutorial/08-request-adapter/03-alova-adapter-axios.md new file mode 100644 index 000000000..93b936a8f --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/08-request-adapter/03-alova-adapter-axios.md @@ -0,0 +1,293 @@ +--- +title: axios adapter +sidebar_position: 30 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## Install + + + + +```bash +npm install @alova/adapter-axios --save +``` + + + + +```bash +yarn add @alova/adapter-axios +``` + + + + +## Instructions + +### create alova + +Use **axiosRequestAdapter** as request adapter for alova. + +```javascript +import { createAlova } from 'alova'; +import { axiosRequestAdapter } from '@alova/adapter-axios'; + +const alovaInst = createAlova({ + //... + requestAdapter: axiosRequestAdapter() + //... +}); +``` + +The adapter will use the default axios instance to make requests internally. If you set some global parameters for axios, you may need to pay attention to the following two points: + +1. Priority is given to using the `baseURL` and `timeout` parameters in the axios instance, so if you set these parameters on the axios instance, you do not need to set them in `createAlova`; +2. The `beforeRequest` hook of the alova instance will be triggered earlier than the `interceptor.request` of the axios instance, and the `responded` hook of the alova will be triggered later than the `interceptor.response` of the axios instance; + +> You can also [use custom axios instance](#use-custom-axios-instance) + +## usage + +### request + +The usage of request is exactly the same as that used in the web environment. Already fully compatible with **axios**, you can specify [all configuration items](https://axios-http.com/docs/req_config) supported by `axios` in _config_ of method instance creation. + + + + +```html + +
Loading...
+
The request data is: {{ data }}
+ + + +``` + +
+ + +```jsx +const list = () => + alovaInst.Get('/list', { + // The set parameters will be passed to axios + paramsSerializer: params => { + return Qs.stringify(params, {arrayFormat: 'brackets'}) + }, + }); + +const App = () => { + const { loading, data } = useRequest(list); + + return ( + { loading ?
Loading...
: null } +
The request data is: { JSON.stringify(data) }
+ ) +}; +``` + +
+ + +```html + + +{#if $loading} +
Loading...
+{/if} +
The request data is: { data }
+``` + +
+
+ +### Upload + +Use `FormData` to upload files, and this `FormData` instance will be passed to axios, which is consistent with the usage of axios upload files. + +```javascript +const uploadFile = imageFile => { + const formData = new FormData(); + formData.append('file', imageFile); + return alovaInst.Post('/uploadImg', formData, { + // Start upload progress + enableUpload: true + }); +}; + +const { + loading, + data, + uploading, + send: sendUpload +} = useRequest(uploadFile, { + immediate: false +}); + +// Picture selection event callback +const handleImageChoose = ({ target }) => { + sendUpload(target.files[0]); +}; +``` + +### download + +Point the request url to the file address to download, you can also enable the download progress by setting `enableDownload` to true. + +```javascript +const downloadFile = () => + alovaInst.Get('/bigImage.jpg', { + // Start download progress + enableDownload: true, + responseType: 'blob' + }); + +const { loading, data, downloading, send, onSuccess } = useRequest(downloadFile, { + immediate: false +}); +onSuccess(({ data: imageBlob }) => { + // download image + const anchor = document.createElement('a'); + anchor.href = URL.createObjectURL(blob); + anchor.download = 'image.jpg'; + anchor.click(); + URL.revokeObjectURL(anchor.href); +}); +const handleImageDownload = () => { + send(); +}; +``` + +## Use custom axios instance + +By default, this adapter will use the default axios instance for requests, but in some cases you need to use a custom created axios instance. You can do this: + +```javascript +const customAxios = axios.create({ + // ... +}); + +const alovaInst = createAlova({ + // ... + // highlight-start + requestAdapter: axiosRequestAdapter({ + axios: customAxios + }) + // highlight-end +}); +``` + +## Mock request adapter compatible + +When developing applications, we may still need to use simulated requests. Only by default, the response data of [Mock Request Adapter (@alova/mock)](/tutorial/request-adapter/alova-mock) is a `Response` instance, which is compatible with the `GlobalFetch` request adapter by default. When using the axios adapter, we The response data of the mock request adapter needs to be compatible with **AxiosResponse**, and the error instance is **AxiosError**, so you need to use `axiosMockResponse` exported from the **@alova/adapter-axios** package as the response adapter . + +```javascript +import { defineMock, createAlovaMockAdapter } from '@alova/mock'; +import { axiosRequestAdapter, axiosMockResponse } from '@alova/adapter-axios'; + +const mocks = defineMock({ + //... +}); + +// mock data request adapter +export default createAlovaMockAdapter([mocks], { + // After specifying axios request adapter, requests that do not match the simulated interface will use this adapter to send requests + httpAdapter: axiosRequestAdapter(), + + // axiosMockResponse contains onMockResponse and onMockError + // Used to convert mock data to AxiosResponse and AxiosError compatible format + ...axiosMockResponse +}); + +export const alovaInst = createAlova({ + //... + // Control whether to use the simulated request adapter through environment variables + requestAdapter: process.env.NODE_ENV === 'development' ? mockAdapter : axiosRequestAdapter() +}); +``` + +##Typescript + +The axios request adapter provides complete type adaptation. The type of method configuration and response data will exactly match the type of axios. + +### method configuration + +When creating a method instance, in addition to the general configuration items in the method, you can also use the configuration items in `AxiosRequestConfig`, we have removed the items that conflict with the general configuration of the method instance in the type. + +```typescript +/** + * axios request configuration parameters + * Removed the attributes that conflicted with the method + */ +export type AlovaAxiosRequestConfig = Omit< + AxiosRequestConfig, + | 'url' + | 'method' + | 'baseURL' + | 'headers' + | 'params' + | 'data' + | 'timeout' + | 'cancelToken' + | 'signal' + | 'onUploadProgress' + | 'onDownloadProgress' +>; +``` + +### Response data + +The response data type of axios is `AxiosResponse`, when you use the axios adapter, you will also get the response data in the same format. In actual use, we usually need to process response data globally. A simple example is as follows: + +```typescript +const alovaInst = createAlova( + baseURL: 'https://api.alovajs.org', + requestAdapter: axiosRequestAdapter(), + responded(response) { + // response is automatically inferred as AxiosResponse type + return response.data; + } +}); +``` + +### Error + +When axios receives non-20x and 30x response status codes, it will throw an error. In order to contain more information, axios custom-designed the error instance into an `AxiosError` instance instead of a normal Error instance, so when encountering An error will be thrown when there is a server error or a network error, and you can catch it in the global error callback. + +```typescript +const alovaInst = createAlova( + baseURL: 'https://api.alovajs.org', + requestAdapter: axiosRequestAdapter(), + responded: { + onSuccess(response) { + // response is automatically inferred as AxiosResponse type + return response.data; + }, + onError(err: AxiosError) { + // err type is any by default, you can cast it to AxiosError + //... + } + } +}); +``` diff --git a/versioned_docs/version-2.x/tutorial/08-request-adapter/04-alova-adapter-taro.md b/versioned_docs/version-2.x/tutorial/08-request-adapter/04-alova-adapter-taro.md new file mode 100644 index 000000000..20fec51a7 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/08-request-adapter/04-alova-adapter-taro.md @@ -0,0 +1,436 @@ +--- +title: Taro Adapter +sidebar_position: 40 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info Tips + +This plugin only supports the taro application of react 16.8+, vue3 version. + +::: + +## Install + + + + +```bash +npm install @alova/adapter-taro --save +``` + + + + +```bash +yarn add @alova/adapter-taro +``` + + + + +:::warning React-Native Application + +If you are develop a React-Native app with Taro, please ensure `metro >= 0.76.0` and enable `resolver.unstable_enablePackageExports` in the `metro.config.js`. + +[about unstable_enablePackageExports of metro](https://facebook.github.io/metro/docs/configuration/#unstable_enablepackageexports-experimental) + +::: + +:::warning Dependency precompilation issues + +[Dependency precompilation function](https://docs.taro.zone/blog/2022/05/19/Taro-3.5-beta#2-%E4%BE%9D%E8%B5%96%E9%A2%84%E7%BC%96%E8%AF%91) has been added in Taro v3.5 beta, and is enabled by default in development mode when you are using the `alova` library and `@alova/scene-react(vue)` may cause the error `` [alova]can not call useHooks until set the `statesHook` at alova instance. ``. This is caused by the prebundle feature repeatedly packaging two different `alova` packages. , turning off the prebundle function at this time can solve this problem. + +```js +// config/dev.ts +export default { + // ... + compiler: { + type: 'webpack5', + prebundle: { + // Close prebundle + enable: false + } + } +} satisfies UserConfigExport + +``` + +Thanks to [LBinin’s issue](https://github.com/alovajs/scene/issues/63). + +This problem has been committed to the Taro team [issue](https://github.com/NervJS/taro/issues/15728) and we look forward to solving this issue. + +::: + +## Usage + +### create alova + +Calling **AdapterTaro** will return _Request Adapter_, _Storage Adapter_, and _ReactHook_, so you no longer need to set these three items, and the usage is exactly the same. + + + + +```javascript +import { createAlova } from 'alova'; +import AdapterTaro from '@alova/adapter-taro'; + +const alovaInst = createAlova({ + baseURL: 'https://api.alovajs.org', + ...AdapterTaro() +}); +``` + + + + + +```javascript +import { createAlova } from 'alova'; +import AdapterTaroVue from '@alova/adapter-taro/vue'; + +const alovaInst = createAlova({ + baseURL: 'https://api.alovajs.org', + ...AdapterTaroVue() +}); +``` + + + + +### Request + +The usage method of the request is exactly the same as that used in the web environment. Already fully compatible with `Taro.request`, you can specify [all configuration items](https://taro-docs.jd.com/docs/apis/network/request/) supported by `Taro.request` in the _config_ of method instance creation + + + + +```jsx +const list = () => + alovaInst.Get('/list', { + // The set parameters will be passed to Taro.request + enableHttp2: true + }); + +const App = () => { + const { loading, data } = useRequest(list); + + return ( + { loading ? Loading... : null } + The requested data is: { JSON.stringify(data) } + ) +}; +``` + + + + +```html + + Loading... + The requested data is: {{ data }} + + + +``` + + + + +### Upload + +When `requestType: 'upload'` is set in the _config_ of the method instance, it means to upload the file, the request adapter will call `Taro.uploadFile`, and the uploaded file data is placed in the data of the method instance, you need to specify in the data `name` and `filePath`, these two parameters will be passed to `Taro.uploadFile`, at the same time, you can also specify other parameters in data, and the request adapter will pass them to `formData `in parameters. + +Similarly, it is fully compatible with `Taro.uploadFile`, you can specify [all configuration items](https://taro-docs.jd.com/docs/apis/network/upload/uploadFile) supported by `Taro.uploadFile`, if there are additional parameters to be set, please specify them in _config_ of the method instance. + + + + +```jsx +const uploadFile = (name, filePath, formData) => + alovaInst.Post( + '/uploadImg', + { + name, + filePath, + + // Additional data will be passed into formData of uni.uploadFile + ...formData + }, + { + // Set the request method to upload, and the adapter will call uni.uploadFile + requestType: 'upload', + + // Start upload progress + enableUpload: true + } + ); + +const App = () => { + const { loading, data, uploading, send } = useRequest(uploadFile, { + immediate: false + }); + + const handleImageChoose = () => { + Taro.chooseImage({ + success: chooseImageRes => { + const tempFilePaths = chooseImageRes.tempFilePaths; + send('fileName', tempFilePaths[0], { + extra1: 'a', + extra2: 'b' + }); + } + }); + }; + + return ( + { loading ? Uploading... : null } + Upload progress: { uploading.loaded }/{ uploading.total } + + {/* ... */} + ) +} +``` + + + + +```html + + Uploading... + Upload progress: {{ uploading.loaded }}/{{ uploading.total }} + + + + + +``` + + + + +### download + +When `requestType: 'download'` is set in the _config_ of the method instance, it means to download the file, and the request adapter will call `Taro.downloadFile`. + +Similarly, it is fully compatible with `Taro.downloadFile`, you can specify [all configuration items](https://taro-docs.jd.com/docs/apis/network/download/downloadFile) supported by `Taro.downloadFile`, if there are additional parameters to be set, please specify them in _config_ of the method instance. + + + + +```jsx +const downloadFile = filePath => + alovaInst.Get('/bigImage.jpg', { + // Set the request method to download, and the adapter will call uni.downloadFile + requestType: 'download', + filePath, + + // Start download progress + enableDownload: true + }); + +const App = () => { + const { loading, data, downloading, send } = useRequest(downloadFile, { + immediate: false + }); + const handleImageDownload = () => { + send('file_save_path'); + }; + + return ( + { loading ? Downloading... : null } + Download progress: { downloading.loaded }/{ downloading.total } + + {/* ... */} + ); +} +``` + + + + +```html + + Downloading... + Download progress: {{ downloading.loaded }}/{{ downloading.total }} + + + + + +``` + + + + +## Mock request adapter compatible + +When using Taro to develop applications, we may still need to use mock requests, but by default, the response data of [Mock Request Adapter (@alova/mock)](/tutorial/request-adapter/alova-mock) is a `Response` instance, That is, it is compatible with the `GlobalFetch` request adapter by default. When used in the Taro environment, we need to make the response data of the simulated request adapter compatible with the Taro adapter, so you need to use the **@alova/adapter-taro** package exported `taroMockResponse` as response adapter. + +```javascript +import { defineMock, createAlovaMockAdapter } from '@alova/mock'; +import AdapterTaro, { taroRequestAdapter, taroMockResponse } from '@alova/adapter-taro'; + +const mocks = defineMock({ + //... +}); + +// mock data request adapter +export default createAlovaMockAdapter([mocks], { + // After specifying the taro request adapter, requests that do not match the simulated interface will use this adapter to send requests + httpAdapter: taroRequestAdapter, + + // Simulate the response adapter, after specifying, the response data will be converted to a taro-compatible data format + onMockResponse: taroMockResponse +}); + +export const alovaInst = createAlova({ + baseURL: 'https://api.alovajs.org', + timeout: 5000, + ...AdapterTaro({ + // Control whether to use the simulated request adapter through environment variables + mockRequest: process.env.NODE_ENV === 'development' ? mockAdapter : undefined + }) + //... +}); +``` + +## Typescript + +The taro request adapter provides complete type adaptation, and the type of method configuration and response data will exactly match the type of taro. + +### method configuration + +When creating a method instance, in addition to the general configuration items in the method, you can also use the configuration items in `Taro.request`, `Taro.uploadFile` and `Taro.downloadFile`, we have removed the type and method Items that conflict with the common configuration of the instance. + +```typescript +/** + * Taro.request requests additional parameters + */ +export type TaroRequestConfig = Omit< + Taro.request.Option, + 'url' | 'data' | 'header' | 'method' | 'timeout' | 'success' | 'fail' | 'complete' +>; + +/** + * Taro.uploadFile additional parameter + */ +export type TaroUploadConfig = Omit< + Taro.uploadFile.Option, + 'url' | 'filePath' | 'name' | 'header' | 'formData' | 'timeout' | 'success' | 'fail' | 'complete' +>; + +/** + * Taro.downloadFile additional parameters + */ +export type TaroDownloadConfig = Omit< + Taro.downloadFile.Option, + 'url' | 'header' | 'timeout' | 'success' | 'fail' | 'complete' +>; + +/** + * Merged request configuration parameters + */ +export type TaroConfig = { + /** + * Request type, upload means upload, download means download, not filling means normal request + */ + requestType?: 'upload' | 'download'; +} & TaroRequestConfig & + TaroUploadConfig & + TaroDownloadConfig; +``` + +### Response data + +Because the taro request adapter is compatible with `Taro.request`, `Taro.uploadFile` and `Taro.downloadFile`, but their response value types are slightly different, so the response data type is as follows: + +```typescript +type TaroResponse = + // The response type of Taro.request + | Taro.request.SuccessCallbackResult + + // The response type of Taro.uploadFile + | Taro.uploadFile.SuccessCallbackResult + + // The response type of Taro.downloadFile + | Taro.downloadFile.FileSuccessCallbackResult; +``` + +In actual use, we usually need to process the response data globally. It is recommended to judge the returned data separately. A simple example is as follows: + +```typescript +const alovaInst = createAlova({ + baseURL: 'https://api.alovajs.org', + ...AdapterTaro(), + responded(response) { + const { statusCode, data } = response as Taro.request.SuccessCallbackResult; + if (statusCode >= 400) { + throw new Error('request error'); + } + return data || null; + } +}); +``` diff --git a/versioned_docs/version-2.x/tutorial/08-request-adapter/05-alova-adapter-uniapp.md b/versioned_docs/version-2.x/tutorial/08-request-adapter/05-alova-adapter-uniapp.md new file mode 100644 index 000000000..1466a4d15 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/08-request-adapter/05-alova-adapter-uniapp.md @@ -0,0 +1,279 @@ +--- +title: Uniapp Adapter +sidebar_position: 50 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info Tips + +This plugin only supports vue3 version of uniapp application. + +::: + +## Install + + + + +```bash +npm install @alova/adapter-uniapp --save +``` + + + + +```bash +yarn add @alova/adapter-uniapp +``` + + + + +## Usage + +### create alova + +Calling **AdapterUniapp** will return _Request Adapter_, _Storage Adapter_, and _VueHook_, so you no longer need to set these three items, and the usage is exactly the same. + +```javascript +import { createAlova } from 'alova'; +import AdapterUniapp from '@alova/adapter-uniapp'; + +const alovaInst = createAlova({ + baseURL: 'https://api.alovajs.org', + ...AdapterUniapp() +}); +``` + +### Request + +The usage method of the request is exactly the same as that used in the web environment. Already fully compatible with `uni.request`, you can specify [all configuration items] supported by `uni.request` in _config_ of method instance creation (https://uniapp.dcloud.net.cn/api/request/ request.html) + +```html + + Loading... + The requested data is: {{ data }} + + + +``` + +When using `useRequest/useWatcher` to send a request immediately, it will be executed asynchronously in the `onLoad` hook, so you can access the options data in `methodHandler` as follows: + +```javascript +import { onLoad } from '@dcloudio/uni-app'; + +let options = {}; +onLoad(opt => { + options = opt; +}); +const { loading, data } = useRequest(() => getDetail(options.id)); +``` + +### Upload + +When `requestType: 'upload'` is set in the _config_ of the method instance, it means to upload the file, the request adapter will call `uni.uploadFile`, and the uploaded file data is placed in the data of the method instance, you need to specify in the data `name`, `filePath or files`, and `file` (if necessary), these 4 parameters will be passed to `uni.uploadFile`, at the same time, you can also specify other parameters besides these 4 parameters in data , the request adapter will pass them into the `formData` parameter. + +Similarly, it is fully compatible with `uni.uploadFile`, you can specify [all configuration items] supported by `uni.uploadFile` in _config_ of method instance creation (https://uniapp.dcloud.net.cn/api /request/network-file.html#uploadfile), if there are additional parameters to be set, please specify them in _config_ of the method instance. + +```html + + Uploading... + Upload progress: {{ uploading.loaded }}/{{ uploading.total }} + + + + + +``` + +### download + +When `requestType: 'download'` is set in the _config_ of the method instance, it means to download the file, and the request adapter will call `uni.downloadFile`. + +Similarly, it is fully compatible with `uni.downloadFile`, you can specify [all configuration items] supported by `uni.downloadFile` in _config_ of method instance creation (https://uniapp.dcloud.net.cn/api /request/network-file.html#downloadfile), if there are additional parameters to be set, please specify them in _config_ of the method instance. + +```html + + Downloading... + Download progress: {{ downloading.loaded }}/{{ downloading.total }} + + + + + +``` + +## Mock request adapter compatible + +When using uniapp to develop applications, we may still need to use simulated requests, but by default, the response data of [mock adapter (@alova/mock)](/tutorial/request-adapter/alova-mock) is a `Response` instance, That is, it is compatible with the `GlobalFetch` request adapter by default. When used in the uniapp environment, we need to make the response data of the simulated request adapter compatible with the uniapp adapter, so you need to use the **@alova/adapter-uniapp** package exported `uniappMockResponse` as response adapter. + +```javascript +import { defineMock, createAlovaMockAdapter } from '@alova/mock'; +import AdapterUniapp, { uniappRequestAdapter, uniappMockResponse } from '@alova/adapter-uniapp'; + +const mocks = defineMock({ + //... +}); + +// mock data request adapter +export default createAlovaMockAdapter([mocks], { + // After specifying the uniapp request adapter, requests that do not match the simulated interface will use this adapter to send requests + httpAdapter: uniappRequestAdapter, + + // Simulate the response adapter, after specifying, the response data will be converted to a uniapp-compatible data format + onMockResponse: uniappMockResponse +}); + +export const alovaInst = createAlova({ + baseURL: 'https://api.alovajs.org', + timeout: 5000, + ...AdapterUniapp({ + // Control whether to use the simulated request adapter through environment variables + mockRequest: process.env.NODE_ENV === 'development' ? mockAdapter : undefined + }) + //... +}); +``` + +## Typescript + +uniapp request adapter provides complete type adaptation, and the type of method configuration and response data will exactly match the type of uniapp. + +### method configuration + +When creating a method instance, in addition to the general configuration items in the method, you can also use the configuration items in `uniapp.request`, `uniapp.uploadFile` and `uniapp.downloadFile`, we have removed and method from the type Items that conflict with the common configuration of the instance. + +```typescript +/** + * uni.request requests additional parameters + */ +export type UniappRequestConfig = Omit< + UniNamespace.RequestOptions, + 'url' | 'data' | 'header' | 'method' | 'timeout' | 'success' | 'fail' | 'complete' +>; + +/** + * uni.uploadFile additional parameters + */ +export type UniappUploadConfig = Omit< + UniNamespace.UploadFileOption, + 'url' | 'name' | 'header' | 'formData' | 'timeout' | 'success' | 'fail' | 'complete' +>; + +/** + * uni.downloadFile additional parameters + */ +export type UniappDownloadConfig = Omit< + UniNamespace.DownloadFileOption, + 'url' | 'header' | 'timeout' | 'success' | 'fail' | 'complete' +>; + +/** + * Merged request configuration parameters + */ +export type UniappConfig = { + /** + * Request type, upload means upload, download means download, not filling means normal request + */ + requestType?: 'upload' | 'download'; +} & UniappRequestConfig & + UniappUploadConfig & + UniappDownloadConfig; +``` + +### Response data + +Because the uniapp request adapter is compatible with `uniapp.request`, `uniapp.uploadFile` and `uniapp.downloadFile`, but their response value types are slightly different, so the response data type is as follows: + +```typescript +type UniappResponse = + // The response type of uni.request + | UniNamespace.RequestSuccessCallbackResult + + // The response type of uni.uploadFile + | UniNamespace.UploadFileSuccessCallbackResult + + // The response type of uni.downloadFile + | UniNamespace.DownloadSuccessData; +``` + +In actual use, we usually need to process the response data globally. It is recommended to judge the returned data separately. A simple example is as follows: + +```typescript +const alovaInst = createAlova( + baseURL: 'https://api.alovajs.org', + ...AdapterUniapp(), + responded(response) { + const { statusCode, data } = response as UniNamespace.RequestSuccessCallbackResult; + if (statusCode >= 400) { + throw new Error('request error'); + } + return data || null; + } +}); +``` diff --git a/versioned_docs/version-2.x/tutorial/08-request-adapter/_category_.json b/versioned_docs/version-2.x/tutorial/08-request-adapter/_category_.json new file mode 100644 index 000000000..2c4f02c49 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/08-request-adapter/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Request adapter", + "link": { + "type": "generated-index" + } +} diff --git a/versioned_docs/version-2.x/tutorial/09-framework/01-vue-options.md b/versioned_docs/version-2.x/tutorial/09-framework/01-vue-options.md new file mode 100644 index 000000000..4f2a5ddd9 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/09-framework/01-vue-options.md @@ -0,0 +1,333 @@ +--- +title: vue2/3 options +sidebar_position: 10 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +Usually, use hook can only be used in vue's setup, but through the helper function provided by `@alova/vue-options`, you can also use alova's use hook in vue's options, which is perfectly compatible with almost all functions of alova. + +> Available in both vue2 and vue3. + +[![npm](https://img.shields.io/npm/v/@alova/vue-options)](https://www.npmjs.com/package/@alova/vue-options) +[![build](https://github.com/alovajs/vue-options/actions/workflows/release.yml/badge.svg?branch=main)](https://github.com/alovajs/vue-options/actions/workflows/release.yml) +[![coverage status](https://coveralls.io/repos/github/alovajs/vue-options/badge.svg?branch=main)](https://coveralls.io/github/alovajs/vue-options?branch=main) +![typescript](https://badgen.net/badge/icon/typescript?icon=typescript&label) +![license](https://img.shields.io/badge/license-MIT-blue.svg) + +## Install + + + + +```bash +npm install alova @alova/vue-options --save +``` + + + + +```bash +yarn add alova @alova/vue-options +``` + + + + +:::info alova requirements + +alova version >= 2.13.2 + +::: + +## Usage + +### Map hook status and functions to vue instances + +First use `vueOptionHook` to create an alova instance. + +```javascript +import { createAlova, Method } from 'alova'; +import GlobalFetch from 'alova/GlobalFetch'; +import { VueOptionsHook } from '@alova/vue-options'; + +// api.js +const alovaInst = createAlova({ + baseURL: 'http://example.com', + statesHook: VueOptionsHook, + requestAdapter: GlobalFetch(), + responded: response => response.json() +}); + +/** @type {() => Method} */ +export const getData = () => alovaInst.Get('/todolist'); +``` + +Then use `mapAlovaHook` to map the return value set of use hook to the component instance. The following is how to access the reactive state and operation functions: + +1. You can access responsive status such as `loading/data/error` through the key of the collection, such as `this.key.loading`, `this.key.data`. +2. You can access the operation function through the key of the collection plus the function name, and use `$` to splice it, such as `this.key$send`, `this.key$onSuccess`. + +Below is a complete example. + +```html + + + +``` + +### Computed properties + +If you need to define a computed property that depends on hook-related request status, just write it as usual. + +```javascript +export default { + computed: { + todoRequestLoading() { + return this.todoRequest.loading; + }, + todoRequestData() { + return this.todoRequest.data; + } + } +}; +``` + +### Watch hook status changes + +Due to the limitations of vue2, all hook states are mounted on an object named `alovaHook$`, so you need to add the `alovaHook$` prefix when listening. + +```javascript +export default { + watch: { + // ❌Unable to watch + 'todoRequest.loading'(newVal, oldVal) { + // ... + }, + // ✅watching is work + 'alovaHook$.todoRequest.loading'(newVal, oldVal) { + // ... + } + } +}; +``` + +But this is a bit troublesome, so a `mapWatcher` helper function is provided, which can not only automatically add prefixes, nested watching, but also batch watching. + +#### Define single watch handler + +```javascript +export default { + watch: mapWatcher({ + // Usage 1 + 'todoRequest.loading'(newVal, oldVal) {}, + + // Usage 2 + todoRequest: { + loading(newVal, oldVal) {}, + data(newVal, oldVal) {} + } + }) +}; +``` + +Watching object is also supported. + +```javascript +export default { + watch: mapWatcher({ + todoRequest: { + data: { + handler(newVal, oldVal) {}, + deep: true + } + } + }) +}; +``` + +#### Batch define watch handlers + +Multiple watching keys are separated by `,`. + +```javascript +export default { + watch: mapWatcher({ + // Usage 1 + 'todoRequest1.data, todoRequest2.data'(newVal, oldVal) {}, + + // Usage 2 + 'todoRequest1, todoRequest2': { + loading(newVal, oldVal) {}, + data(newVal, oldVal) {} + }, + + // Usage 3 + todoRequest1: { + 'loading, data'(newVal, oldVal) {} + }, + + // Usage 4 + 'todoRequest1, todoRequest2': { + 'loading, data'(newVal, oldVal) {} + } + }) +}; +``` + +> Batch watching also supports watching object. + +## Function description + +### mapAlovaHook + +`mapAlovaHook` is used to map the state and function collection returned by alova's use hook to the vue component instance through mixins. It receives a callback function and returns the return value collection of use hook. + +It is worth noting that the callback function will be executed in the `created` phase, and you can access the vue component instance in the following way. + +```javascript +// 1. Access the component instance through this. Note that the callback function cannot be an arrow function. +mapAlovaHook(function () { + console.log(this); + return { + todoRequest: useRequest(getData) + }; +}); + +// ======================= +// 2. Access the component instance through function parameters. In this case, arrow functions can be used +mapAlovaHook(vm => { + console.log(vm); + return { + todoRequest: useRequest(getData) + }; +}); +``` + +### mapWatcher + +`mapWatcher` is an helper function used to quickly define the watching handlers of hook states. It receives an object whose key is the key of the hook state or a string representation of the nested value, and whose value is the watching handler or watching object. + +```javascript +mapWatcher({ + 'todoRequest.loading'(newVal, oldVal) { + //... + }, + todoRequest: { + data(newVal, oldVal) { + //... + } + }, + todoRequest: { + 'loading, data'(newVal, oldVal) { + //... + } + } +} +``` + +In addition to supporting watching assistance for alova useHook, `mapWatcher` can also be used to batch set watching handlers of custom states. + +```javascript +export default { + data() { + state1: '', + state2: 0 + }, + + // pass false at the second parameter to watch the custom states + watch: mapWatcher({ + 'state1, state2'(newVal, oldVal) { + //... + } + }, false) +} +``` + +## Type support + +### Automatic inference + +`@alova/vue-options` provides complete ts type support. Whether using typescript or not, the mapped value type will be automatically inferred, for example: + +```javascript +this.todoRequest.loading; // boolean +this.todoRequest.error; // Error | undefined +this.todoRequest.data; // any +this.todoRequest$send; // (...args: any[]) => Promise +this.todoRequest$onSuccess; // (handler: SuccessHandler) => void +this.todoRequest$onError; // (handler: ErrorHandler) => void +this.todoRequest$onComplete; // (handler: CompleteHandler) => void +// ... +``` + +### Add types for response data + +Except for `this.todoRequest.data`, all other values have the correct type, so how to set the type for `data` too? In fact, it is the same as alova used in other environments. + +**javascript** + +In javascript, you can use type annotations to add types. The first two generic parameters of Method are `unknown`, and the third generic parameter is the type of response data. + +```javascript +import { Method } from 'alova'; + +/** @type {() => Method} */ +export const getData = () => alovaInst.Get('/todolist'); +``` + +**typescript** + +To add response data type in typescript, please read [alova documentation typescript chapter](/tutorial/combine-framework/typescript) + +## limit + +1. [Manage extra states](/tutorial/advanced/manage-extra-states) is not supported yet. +2. Currently, only alova’s three core useHooks of `useRequest/useWatcher/useFetcher` are supported, as well as the encapsulation based on the core useHook in your own project. [@alova/scene](https://github.com/alovajs/scene) is not supported yet. extension useHook. diff --git a/versioned_docs/version-2.x/tutorial/09-framework/02-solid.md b/versioned_docs/version-2.x/tutorial/09-framework/02-solid.md new file mode 100644 index 000000000..6f7e0e099 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/09-framework/02-solid.md @@ -0,0 +1,6 @@ +--- +title: solid +sidebar_position: 20 +--- + +Comming soon... diff --git a/versioned_docs/version-2.x/tutorial/09-framework/03-angular.md b/versioned_docs/version-2.x/tutorial/09-framework/03-angular.md new file mode 100644 index 000000000..245469211 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/09-framework/03-angular.md @@ -0,0 +1,6 @@ +--- +title: angular +sidebar_position: 30 +--- + +Comming soon... diff --git a/versioned_docs/version-2.x/tutorial/09-framework/04-native-mp.md b/versioned_docs/version-2.x/tutorial/09-framework/04-native-mp.md new file mode 100644 index 000000000..a55050983 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/09-framework/04-native-mp.md @@ -0,0 +1,6 @@ +--- +title: Native mini program(China🇨🇳) +sidebar_position: 40 +--- + +Comming soon... diff --git a/versioned_docs/version-2.x/tutorial/09-framework/05-preact.md b/versioned_docs/version-2.x/tutorial/09-framework/05-preact.md new file mode 100644 index 000000000..c70ee8364 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/09-framework/05-preact.md @@ -0,0 +1,6 @@ +--- +title: preact +sidebar_position: 50 +--- + +Comming soon... diff --git a/versioned_docs/version-2.x/tutorial/09-framework/06-qwik.md b/versioned_docs/version-2.x/tutorial/09-framework/06-qwik.md new file mode 100644 index 000000000..3d2cd2c94 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/09-framework/06-qwik.md @@ -0,0 +1,6 @@ +--- +title: qwik +sidebar_position: 60 +--- + +Comming soon... diff --git a/versioned_docs/version-2.x/tutorial/09-framework/07-lit.md b/versioned_docs/version-2.x/tutorial/09-framework/07-lit.md new file mode 100644 index 000000000..8d3500f34 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/09-framework/07-lit.md @@ -0,0 +1,6 @@ +--- +title: lit +sidebar_position: 70 +--- + +Comming soon... diff --git a/versioned_docs/version-2.x/tutorial/09-framework/08-stencil.md b/versioned_docs/version-2.x/tutorial/09-framework/08-stencil.md new file mode 100644 index 000000000..e651aacfd --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/09-framework/08-stencil.md @@ -0,0 +1,6 @@ +--- +title: stencil +sidebar_position: 80 +--- + +Comming soon... diff --git a/versioned_docs/version-2.x/tutorial/09-framework/_category_.json b/versioned_docs/version-2.x/tutorial/09-framework/_category_.json new file mode 100644 index 000000000..6edd9139c --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/09-framework/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Framework", + "link": { + "type": "generated-index" + } +} diff --git a/versioned_docs/version-2.x/tutorial/10-custom/01-overview.md b/versioned_docs/version-2.x/tutorial/10-custom/01-overview.md new file mode 100644 index 000000000..0c66df6ee --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/10-custom/01-overview.md @@ -0,0 +1,26 @@ +--- +title: Overview +sidebar_position: 10 +--- + +Alova has high scalability. In addition to providing common features such as core caching mechanism, request sharing mechanism, and state management, it also provides various customization functions and middleware mechanisms, which can adapt to different js environments and customize requests. Strategy. + +## Adapter + +In order to meet the running requirements of js in different environments, you can customize the request adapter, storage adapter, and even the state adapter of the UI framework, which will be introduced in detail in the next chapters. Some adapter examples are listed below. + +- [fetch adapter](https://github.com/alovajs/alova/blob/main/src/predefine/GlobalFetch.ts) +- [localStorage storage adapter](https://github.com/alovajs/alova/blob/main/src/predefine/globalLocalStorage.ts) +- [vue states hook](https://github.com/alovajs/alova/blob/main/src/predefine/VueHook.ts) + +You can also combine multiple types of adapters into a collection, for example [Uniapp Adapter](/tutorial/request-adapter/alova-adapter-uniapp). + +## Write request strategy + +alova's request strategy is separate from the alova core library, so that developers can also take advantage of alova's high scalability to write their own request strategies. Usually, a custom request strategy is based on the combination of `useRequest`, `useWatcher` and `useFetcher`, and writing [middleware](/tutorial/advanced/middleware), cache manipulation functions for them to control their The request method, so as to realize the request strategy of various effects. + +The request strategies in **@alova/scene** are well represented, and it is strongly recommended that you refer to the source code for inspiration. + +- [Paging request strategy source code](https://github.com/alovajs/scene/blob/main/src/hooks/pagination/usePagination.js) +- [Captcha strategy source code](https://github.com/alovajs/scene/blob/main/src/hooks/useCaptcha.ts) +- [Form submission strategy source code](https://github.com/alovajs/scene/blob/main/src/hooks/useForm.ts) diff --git a/versioned_docs/version-2.x/tutorial/10-custom/02-custom-http-adapter.md b/versioned_docs/version-2.x/tutorial/10-custom/02-custom-http-adapter.md new file mode 100644 index 000000000..ad6a9729a --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/10-custom/02-custom-http-adapter.md @@ -0,0 +1,193 @@ +--- +title: Custom Request Adapter +sidebar_position: 20 +--- + +Remember how to create an Alova instance? + +```javascript +const alovaInstance = createAlova({ + //... + requestAdapter: GlobalFetch() +}); +``` + +`requestAdapter` is a request adapter. Internal request sending and receiving will depend on the request adapter. `GlobalFetch` manages requests through fetch api. In most cases, we can use it. However, when `alova` When running in an environment where fetch api is not available (such as app, applet), it is necessary to replace a request adapter that supports the current environment. + +So how should you customize a request adapter? Very simple, it is actually a function, which is called every time a request is made, and returns an object, which contains such things as `url`, `method`, `data`, `headers`, `timeout`, etc. Request related data sets. Although there are many fields, we only need to access the data we need. + +## Request adapter structure + +The request adapter will receive request-related parameters and the currently requesting method instance, and return a set of response-related functions. + +```javascript +function CustomRequestAdapter(requestElements, methodInstance) { + // send request... + return { + async response() { + // ...return the response data + }, + async headers() { + // Asynchronous function that returns response headers + }, + abort() { + // Abort request, this function will be triggered when abort is called externally + }, + onDownload(updateDownloadProgress) { + // Download progress information, internally call updateDownloadProgress continuously to update the download progress + }, + onUpload(updateUploadProgress) { + // Upload progress information, internally call updateUploadProgress continuously to update the upload progress + } + }; +} +``` + +### Request parameter details + +**requestElements** + +Relevant elements of the send request, including the following data. + +```typescript +interface RequestElements { + // request url, the get parameter is already included + readonly url: string; + + // Request type, such as GET, POST, PUT, etc. + readonly type: MethodType; + + // Request header information, object + readonly headers: Arg; + + // request body information + readonly data?: RequestBody; +} +``` + +**methodInstance** + +The method instance of the current request + +### Return parameter details + +**response (required)** + +An asynchronous function, the function returns the response value, which will be passed to the global response interceptor responded; + +**headers (required)** + +An asynchronous function, the response header object returned by the function will be passed to the transformData conversion hook function of the Method instance; + +**abort (required)** + +A common function, which is used for aborting request. All aborting requests will eventually call this function to execute; + +**onDownload (optional)** + +An ordinary function that receives a callback function that updates the download progress, and customizes the frequency of the progress update within this function, in this example simulating an update every 100 milliseconds. The `updateDownloadProgress` callback function receives two parameters, the first parameter is the total size, and the second parameter is the downloaded size; + +**onUpload (optional)** + +An ordinary function that receives a callback function that updates the upload progress, and customizes the frequency of the progress update within this function, in this example simulating an update every 100 milliseconds. The `updateUploadProgress` callback function receives two parameters, the first parameter is the total size, and the second parameter is the uploaded size; + +## XMLHttpRequest request adapter example + +The following is an example of an adapter that sends requests through XMLHttpRequest, mainly to demonstrate how to write the adapter, the code is incomplete and cannot be run. + +```javascript +function XMLHttpRequestAdapter(requestElements, methodInstance) { + // Deconstruct the data that needs to be used + const { url, type, data, headers } = config; + + // send request + const xhr = new XMLHttpRequest(); + xhr.open(type, url); + for (const key in headers) { + xhr.setRequestHeader(key, headers[key]); + } + const responsePromise = new Promise((resolve, reject) => { + xhr.addEventListener('load', event => { + // process response data + resolve(/* ... */); + }); + xhr.addEventListener('error', event => { + // Handle request errors + reject(/* ... */); + }); + }); + + xhr.send(JSON.stringify(data)); + + return { + // Asynchronous function that returns response data + response: () => responsePromise, + + // Asynchronous function that returns response headers + headers: () => responsePromise.then(() => xhr.getAllResponseHeaders()), + abort: () => { + xhr.abort(); + }, + + // Download progress information, internally call updateDownloadProgress continuously to update the download progress + onDownload: updateDownloadProgress => { + xhr.addEventListener('progress', event => { + // data receiving progress + updateDownloadProgress(event.total, event.loaded); + }); + }, + + // Upload progress information, internally call updateUploadProgress continuously to update the upload progress + onUpload: updateUploadProgress => { + xhr.upload.onprogress = event => { + updateUploadProgress(event.total, event.loaded); + }; + } + }; +} +``` + +:::note + +More complete request adapter details can be found in [GlobalFetch source code](https://github.com/alovajs/alova/blob/main/src/predefine/GlobalFetch.ts) to understand. + +::: + +## Request adapter type + +The types of the global `beforeRequest`, `responded` interceptors, and the configuration object when the `Method` instance is created will be automatically inferred based on the type provided by the request adapter. The following are the types of GlobalFetch. + +```javascript +import type { RequestElements, Method, ProgressUpdater } from 'alova'; + +export type GlobalFetch = () => ( + elements: RequestElements, + method: Method +) => { + response: () => Promise, + headers: () => Promise, + onDownload: (handler: ProgressUpdater) => void, + abort: () => void +}; +``` + +Three types of values ​​of `RC`, `RE` and `RH` are specified in this type, so the type given by the request adapter will be automatically inferred in the global interceptor, method instance configuration and other places. + +They are expressed as: + +- **RC**: Abbreviation of _RequestConfig_, request configuration object type +- **RH**: Abbreviation of _ResponseHeader_, response header object type +- **RE**: Abbreviation of _Response_, response type + +If you are using **GlobalFetch**, their types will be inferred as: + +- **RC**: fetch api's request configuration object `RequestInit`; +- **RH**: Response header object `Headers`; +- **RE**: response object `Response`; + +In order to facilitate the definition of the request adapter type, alova also provides an adapter type. You only need to pass in the `RC/RE/RH` generic parameters as needed. + +```typescript +import type { AlovaRequestAdapter } from 'alova'; +type CustomRequestAdpater = AlovaRequestAdapter; +``` diff --git a/versioned_docs/version-2.x/tutorial/10-custom/03-custom-storage-adapter.md b/versioned_docs/version-2.x/tutorial/10-custom/03-custom-storage-adapter.md new file mode 100644 index 000000000..2cd749f8f --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/10-custom/03-custom-storage-adapter.md @@ -0,0 +1,48 @@ +--- +title: Custom Storage Adapter +sidebar_position: 30 +--- + +Alova involves multiple functions that require data persistence, such as persistent cache, silent submission, and offline submission. **By default, alova will use `localStorage` to store persistent data**, but considering the non-browser environment, it also supports customization. + +Customizing the storage adapter is also very simple, you only need to specify the functions for saving data, getting data, and removing data, roughly like this. + +```javascript +const customStorageAdapter = { + set(key, value) { + // Save the data, value is structured data, which can be converted to a string by calling JSON.stringify + }, + get(key) { + // get data, return structured data, which can be converted by calling JSON.parse + }, + remove(key) { + // remove data + } +}; +``` + +Then pass in this adapter when creating an `alova` instance. + +```javascript +const alovaInstance = createAlova({ + //... + storageAdapter: customStorageAdapter +}); +``` + +## SessionStorage storage adapter example + +```javascript +const sessionStorageAdapter = { + set(key, value) { + sessionStorage.setItem(key, JSON.stringify(value)); + }, + get(key) { + const data = sessionStorage.getItem(key); + return data ? JSON.parse(data) : data; + }, + remove(key) { + sessionStorage.removeItem(key); + } +}; +``` diff --git a/versioned_docs/version-2.x/tutorial/10-custom/04-custom-stateshook.md b/versioned_docs/version-2.x/tutorial/10-custom/04-custom-stateshook.md new file mode 100644 index 000000000..e2b686346 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/10-custom/04-custom-stateshook.md @@ -0,0 +1,101 @@ +--- +title: Custom States Hook +sidebar_position: 50 +--- + +Remember how to create an Alova instance? + +```javascript +const alovaInstance = createAlova({ + //... + statesHook: ReactHook +}); +``` + +`statesHook` will decide which MVVM library state to return when requesting, alova currently provides **VueHook, ReactHook, svelteHook**. + +In most cases, you should not use this function, but if you need to adapt to more MVVM libraries that alova does not support, you need to customize `statesHook`. + +`statesHook` is an ordinary object that contains specific functions, but these basically do not involve algorithms, let's see how **VueHook** is written. + +## statesHook structure + +statesHook is represented by an object, the following is an example of **VueHook**. + +```javascript +import { ref, watch, onUnmounted } from 'vue'; + +const VueHook = { + // state creation function + create: rawData => ref(data), + + // state export function + export: state => state, + + // dehydration function + dehydrate: state => state.value, + + // Reactive state update function + update: (newVal, states) => { + Object.keys(newVal).forEach(key => { + states[key].value = newVal[key]; + }); + }, + + // request send control function + effectRequest({ handler, removeStates, saveStates, immediate, frontStates, watchingStates }) { + // Remove the corresponding state when the component is uninstalled + onUnmounted(removeStates); + + // When calling useRequest and useFetcher, watchingStates is undefined + if (!watchingStates) { + handler(); + return; + } + + // When calling useWatcher, watchingStates is an array of states that need to be monitored + // When immediate is true, it means that the request needs to be sent immediately + watch(watchingStates, handler, { immediate }); + } +}; +``` + +## Custom statesHook function description + +> All of the following 5 functions must be specified. + +**create** + +Responsive state creation function, `loading`, `error`, `data`, `downloading`, `uploading`, etc. are all created by calling this function, such as the vue3 project will be created as a ref value; + +**export** + +State export function, this function receives the responsive state created by the create function, and exports the final state for developers to use, where the state exported by `VueHook` is the original state; + +**dehydrate** + +Dehydration function, which means converting the responsive state into normal data, is the opposite operation to create, in `updateState`; + +**update** + +Responsive status update function, the status update maintained internally by `alova` is done through this function. This function receives two parameters, the first parameter is the new data object, and the second parameter is the map collection of the original responsive state, here you can write a fixed cycle to update `states`; + +**effectRequest** + +Request sending control function, it will execute this function immediately when `useRequest`, `useWatcher`, `useFetcher` are called, we need to complete three things in this function: + +1. When the current component is uninstalled, call the removeStates function to remove the responsive state involved in the current component to avoid memory overflow; +2. When calling useWatcher, bind the state monitor, and call the sendRequest function when the state changes. You can use whether `states` is an array to judge whether `useWatcher` is called. At the same time, the `immediate` parameter is used to judge whether `useWatcher` is called Whether the request needs to be sent immediately; +3. When calling `useRequest` and `useFetcher`, call sendRequest to send a request, at this time `states` is `undefined`; + +:::warning Caution + +If the library involved in statesHook is like `react`, the use hook of `alova` will be called every time it is re-rendered, then the `saveStates` function needs to be triggered every time it is re-rendered in `effectRequest`, this is because `react `Every re-render refreshes its state references, so we need to re-save them again. + +::: + +[ReactHook source code click here to view](https://github.com/alovajs/alova/blob/main/src/predefine/ReactHook.ts) + +## statesHook type + +If you want it to support typescript when customizing statesHook, you can [click here to view](/tutorial/combine-framework/typescript) diff --git a/versioned_docs/version-2.x/tutorial/10-custom/_category_.json b/versioned_docs/version-2.x/tutorial/10-custom/_category_.json new file mode 100644 index 000000000..505fadaab --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/10-custom/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Custom", + "link": { + "type": "generated-index" + } +} diff --git a/versioned_docs/version-2.x/tutorial/11-others/01-RSM.md b/versioned_docs/version-2.x/tutorial/11-others/01-RSM.md new file mode 100644 index 000000000..854517793 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/11-others/01-RSM.md @@ -0,0 +1,61 @@ +--- +title: Request Scene Model (RSM) +sidebar_position: 10 +--- + +## What is the request scene model + +The request scenario model is based on the perspective of the client. It describes the abstract model of the client from triggering the request intent to receiving the request result. It consists of four stages: request timing, request behavior, request event, and response management. For example, when making a request, you often need to think about the following questions, + +1. When the request is made; +2. Whether to display the request status; +3. Do you need to retry the request on failure; +4. How to process the response data; +5. Do you need to encrypt the request parameters; +6. Whether to cache the frequently used response data; +7. How to operate data across pages; +8. How to process requests in a weak or disconnected network environment; +9. ... + +`fetch` or `axios` are often more focused on how to interact with the server, but we always need to deal with the above problems by ourselves. These functions that are beneficial to application performance and stability will always allow programmers to write low-maintenance sexual code. The request scene model is to abstract all links from preparing the request to processing the response data, so as to cover the model of the entire CS interaction life cycle from the perspective of the front end. `alova` is a library that requests scene models, it is a supplement to request libraries such as `axios`, not a substitute. + +> CS interaction: generally refers to data interaction between all client types and servers + +## Request scene model + +![RSM](/img/rsm-en.png) + +## request timing + +Describe when a request needs to be made, implemented with `useHook` in `alova`. + +- Initialize display data, such as just entering a certain interface or sub-interface; +- Human-computer interaction triggers CS interaction, and the request needs to be changed again, such as page turning, filtering, sorting, fuzzy search, etc.; +- Send requests in an anti-shake manner, avoid view data flickering, and reduce server pressure +- Preloading data, such as preloading the content of the next page in a page, predicting that the user clicks a button to pre-fetch data; +- To operate server data, it is necessary to issue a request for addition, deletion, modification and query, such as submitting data, deleting data, etc.; +- Synchronize server status, such as polling requests in scenarios where data changes rapidly, and re-pull data after operating a certain data; + +## Request Behavior + +Describes how to process the request, implemented as a Method abstraction in `alova`. + +- Placeholder request, when requesting, display loading, skeleton diagram, or real data used last time; +- Cache high-frequency responses, multiple execution requests will use fresh data; +- Multi-request serial and parallel; +- The retry mechanism of important interfaces reduces the probability of request failure caused by network instability; +- Submit silently. When you only care about submitting data, directly respond to the success event after submitting the request, and the background guarantees that the request is successful; +- Offline submission, the submitted data will be temporarily stored locally when offline, and then submitted after the network connection; + +## request event + +Indicates sending a request with request parameters and getting a response. `alova` can work with any request library or native solution such as `axios`, `fetch`, `XMLHttpRequest`. + +## Response management + +`alova` makes the response data stateful and manages it in a unified manner, refreshes the view data and operates the cache at the request level, avoids operations at the component level, and is more elegant and unified. + +- Remove the cached response data, which will be pulled from the server when the request is made again; +- Update the cached response data, which can update the response data at any location, which is very helpful for updating data across pages; +- Refresh the response data, which can re-refresh the response data at any position, and is also very helpful for updating data across pages; +- Customize the cache setting. When requesting batch data, you can manually set the cache for the batch data one by one, so as to meet the cache hit of subsequent single data; diff --git a/versioned_docs/version-2.x/tutorial/11-others/02-comparison.md b/versioned_docs/version-2.x/tutorial/11-others/02-comparison.md new file mode 100644 index 000000000..c7099fc71 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/11-others/02-comparison.md @@ -0,0 +1,93 @@ +--- +title: Compare with other libraries +sidebar_position: 20 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## Compare with axios + +axios provides a very simple and easy-to-use HTTP request function based on promise. It only requires a simple line of code to send and receive requests, and can run in the browser and nodejs environment. It is a very excellent request js library. + +But axios focuses on sending requests and receiving responses, which means that if you need to write more code by yourself to actively optimize the request function, and alova is like a weapon of axios, combining axios with alova can get more powerful request capabilities. The following are the request management capabilities added by alova to axios. + +### alova provides automated request status management for axios + +When you only use axios, you usually need to maintain request-related status by yourself. You can get automated request status management capabilities by using alova's use hook. + + + + +```javascript +// vue3 example +const loading = ref(false); +const data = ref({}); +const error = ref(null); +const request = async () => { + try { + loading.value = true; + data.value = await axios.get('/xxx'); + } catch (e) { + error.value = e; + } + loading.value = false; +}; +mounted(request); +``` + + + + +```javascript +// Use axios as alova's request adapter +const { loading, data, error } = useRequest(alova.Get('/xxx')); +``` + + + + +### alova provides high-performance request strategies out of the box + +alova provides you with [multiple high-performance request strategy modules](/tutorial/strategy). You can use different modules according to different request scenarios, which axios does not have. + +### alova provides response data cache for axios + +alova provides 3 caching modes to meet different caching scenarios, namely memory mode, cache occupying mode, and recovery mode. They are component-independent and can hit the cache as long as the request address and parameters are the same, unless you turn it off. Response data caching can greatly improve request fluency and reduce server pressure. + +### alova provides request sharing function for axios + +Request sharing will reuse the same request when sending multiple identical requests at the same time. It can also improve application fluency and reduce server pressure. + +### alova provides data pre-fetching for axios + +Requesting the data to be used in advance can also greatly improve application fluency. + +### alova can manage request states + +You can use alova across any component hierarchy to access stateful data in other components, which allows you to reduce some of the trouble of cross-component communication. + +## Compared with react-query and swr + +react-query is a powerful asynchronous state management, and swr is a React Hooks library for data requests. Their common feature is the use of use hooks to send and manage requests, and data caching functions. For them, alova has the following differences at. + +### alova has different goals + +In fact, alova's use hook also refers to the design of react-query and swr, but alova chooses the direction of the request strategy library. You can use different request strategy modules in different request scenarios, allowing you to write less code. At the same time, more efficient Client-Server data interaction can also be achieved. + +### Method proxy design + +Both react-query and swr use `axios` or `fetch api` directly in use hook to send requests, while alova uses the `Method` proxy design mode. This design has the following 3 benefits: + +1. Unified usage without different usage depending on the platform or UI framework. +2. Request libraries such as `axios` and `fetch api` are decoupled from each API in the form of request adapters, which allows alova to provide a unified development experience and perfect coding migration. +3. Each `Method` instance represents an API, you can aggregate the request parameters and request behavior parameters of the same API into the same `Method` instance without spreading them to different files, which is more suitable for managing a large number of APIs. +4. alova realizes automatic management of response data cache by serializing request parameters on the `Method` instance. You do not need to specify the cache key, and both react-query and swr need to customize the `queryKey` to manage the cache. + +### High flexibility + +Alova achieves high flexibility through various adapters and middleware. It can not only run in any js environment, but also support users to customize request modules in different scenarios. + +### Lightweight + +alova is very lightweight, and its size is only 30%+ of react-query and axios. Similar in size to swr, but provides richer functionality. diff --git a/versioned_docs/version-2.x/tutorial/11-others/03-Q&A.md b/versioned_docs/version-2.x/tutorial/11-others/03-Q&A.md new file mode 100644 index 000000000..2b0f56323 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/11-others/03-Q&A.md @@ -0,0 +1,24 @@ +--- +title: Question & Answer +sidebar_position: 30 +--- + +## Why to create alova? + +Data requests have always been an indispensable and important part of applications. Since the birth of XMLHttpRequest, request schemes have emerged in endlessly. Client-side data interaction exploration has always focused on the simplicity of requests, such as `$.ajax`, `axios`, `fetch api` and Request tools such as `react-query`, the coding form continues to evolve from callback functions, Promise, to usehook. These js libraries have done a good job in making requests simple, but they only provide common functions, which means For different request scenarios such as sharing requests, paging requests, form submissions, uploading and downloading files, etc., developers still need to write complex codes themselves, which reduces development efficiency and performance cannot be guaranteed. As user experience becomes more and more important, In this era, application fluency has become more and more important. + +Additionally, the collaboration between client and server is also separated. Front-end engineers need to consult API documents and manually write API functions, and any changes of APIs need to actively notify front-end engineers, which will make your product more uncontrollable. + +**We think there is a simpler solution is that based on your request scenarios such as pagination, form submission, breakpoint resumption, etc., select the corresponding useHook, which will help you manage data and control when requests should be sent**. This allows developers to achieve more efficient Client-Server data interaction while writing little code. + +Additionally, alova has very flexible expansion capabilities to implement request strategies in different scenarios. You can also customize your own request scenarios. This part is in [Custom Chapter](/category/custom). + +In order to cover more request scenarios, we also abstracted the request scenarios into [Request scene model(RSM)](/tutorial/others/RSM), which explains alova's request strategy scheme well. In the future, alova will continue to carry forward our exploration of request strategies. + +## Alternative to the request libraries? + +alova is a request strategy library, which was originally created to provide specific request strategy solutions for different request scenarios, so as to achieve a smooth request experience more concisely and elegantly, such as `$.ajax`, `axios` and `fetch- api`, etc. provide good support for request sending and response receiving, they are an essential part of the [RSM](/tutorial/others/RSM) process (request events), alova still needs to dependent on them to make requests, so we can Think of alova as an weaponry of the request library, making the request library more powerful. + +## Why binding UI framework? + +Decoupling a js library means using it in more scenarios. For example, axios can be used in nodejs, but it also means that developers need to write more template code, such as using useHooks to encapsulate axios. However, alova abandons more usage scenarios brought about by decoupling, and positions the scope of use in conjunction with the UI framework to use alova in the most streamlined way. This is for the benefit of developers and is prevalent in a UI framework. Sometimes, deep binding can provide developers with direct-use functions and improve the developer's experience without requiring too much template code. diff --git a/versioned_docs/version-2.x/tutorial/11-others/04-future.md b/versioned_docs/version-2.x/tutorial/11-others/04-future.md new file mode 100644 index 000000000..d889d2646 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/11-others/04-future.md @@ -0,0 +1,36 @@ +--- +title: Future of alova +sidebar_position: 40 +--- + +alovajs is positioned as a lightweight request strategy library. It currently provides good support in terms of request functions and request strategies, but the future of alovajs does not stop there. + +## More request strategies + +This is the direction that remains unchanged, and we will continue to explore efficient and easy-to-use request strategies based on common businesses. + +## More UI framework support + +Although alovajs is a request tool based on UI framework, its flexible design allows us to use it in various UI frameworks. It will eventually be compatible with the following UI frameworks and js environments: + +- Functional style framework, such as `react/react-native/vue-composntion/svelte/solid/preact/qwik`. +- SSR frameworks such as `next/nuxt/sveltekit`. +- class style framework, such as `angular/lit/stencil`. +- options style framework, such as `vue-options/native applet (China🇨🇳)`. +- Multi-end adaptation framework, such as `Uniapp/Taro` (China 🇨🇳). + +Please check [Go to UI Framework](/category/framework) for details. + +## Automatic management and maintenance of APIs + +In the future, alovajs is also committed to solving front-end API problems and further simplifying the workflow of front-end development. This is the next development direction of alovajs: **automatic management and maintenance of APIs**, which specifically includes the following three points. + +1. Automatically generate a request function with complete ts type and complete description. Whether it is a js project or a ts project, the call does not need to be introduced, making it as convenient for developers as directly calling `location.reload`, and the request function can be directly seen A complete description and request parameter type hints, which can be automatically generated by openAPI. + +2. Since the automatically generated request function has a complete description and type hint, develop a vscode plug-in to quickly retrieve the API you need to use through keywords, and you no longer need to consult the API documentation. + +3. Solve the problem of front-end and back-end collaboration fault. Any changes in the interface are known to the front-end and can be notified when starting the project. If changes are found when building the project, an error will be thrown to stop the build. If it is a ts project, it will also be compiled. When an error is thrown, you can also view the change record through the vscode plugin. + +## Development checklist + +[Click here to view all the development checklist](/contributing/become-core-member/#list-of-participating-repositories) diff --git a/versioned_docs/version-2.x/tutorial/11-others/05-react-native.md b/versioned_docs/version-2.x/tutorial/11-others/05-react-native.md new file mode 100644 index 000000000..dbd0d47df --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/11-others/05-react-native.md @@ -0,0 +1,15 @@ +--- +title: React-Native app development +sidebar_position: 50 +--- + +You can develop a React-Native app with alova, and you can also use request adapter `GlobalFetch` directly to handle request event. + +But...there are some cautions: + +## metro version + +In the alova, `exports` is used to define multiple items in `package.json`, so it's necessary to ensure 2 points below: + +1. version of metro must >= 0.76.0 +2. enable `resolver.unstable_enablePackageExports` in `metro.config.js`. [Click here for detail](https://facebook.github.io/metro/docs/configuration/#unstable_enablepackageexports-experimental) diff --git a/versioned_docs/version-2.x/tutorial/11-others/06-use-in-static.md b/versioned_docs/version-2.x/tutorial/11-others/06-use-in-static.md new file mode 100644 index 000000000..d47608206 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/11-others/06-use-in-static.md @@ -0,0 +1,139 @@ +--- +title: Use in static html +sidebar_position: 60 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +You can also use alova via importing from CDN. + + + + +```html + + + + + + + + + +
+
Loading...
+
{{ error.message }}
+ responseData: {{ data }} +
+ + + +``` + +
+ + +```html + + + + + + + + + + + +
+ + + +``` + +
+ + +:::tip + +svelte 依赖于编译工具,不能通过 CDN 直接使用,详情见 [svelte.dev](https://svelte.dev/) + +::: + + + + +```html + + + + + + + + + +
+
Loading...
+
{{ todo.error.message }}
+ responseData: {{ todo.data }} +
+ + + +``` + +
+
diff --git a/versioned_docs/version-2.x/tutorial/11-others/07-hide-recommend-tips.md b/versioned_docs/version-2.x/tutorial/11-others/07-hide-recommend-tips.md new file mode 100644 index 000000000..75aad3a62 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/11-others/07-hide-recommend-tips.md @@ -0,0 +1,46 @@ +--- +title: Hide recommend Tips +sidebar_position: 70 +--- + +:::info version required + +v2.7.0- + +::: + +Alova can cooperate with the extension library to obtain a better development experience. In order to allow more developers to obtain a better development experience, the extension of alova will be recommended in the console when using it. + +![tips](/img/alova-tips.png) + +These prompt codes will be automatically removed when building the production environment package. If you want to hide them in the development environment, you can do the following: + +## Vite + +Set environment variable **VITE_ALOVA_TIPS=0** in `.env.development` file + +```bash title=.env.development +VITE_ALOVA_TIPS=0 +``` + +:::warning Warning +If info still exists. you can try to remove the deps cache of vite, which at the dir `node_modules/.vite/deps`. +::: + +## Webpack + +### Vue + +Set environment variable **VUE_APP_ALOVA_TIPS=0** in `.env.development` file + +```bash title=.env.development +VUE_APP_ALOVA_TIPS=0 +``` + +### React + +Set environment variable **REACT_APP_ALOVA_TIPS=0** in `.env.development` file + +```bash title=.env.development +REACT_APP_ALOVA_TIPS=0 +``` diff --git a/versioned_docs/version-2.x/tutorial/11-others/_category_.json b/versioned_docs/version-2.x/tutorial/11-others/_category_.json new file mode 100644 index 000000000..07d61bd10 --- /dev/null +++ b/versioned_docs/version-2.x/tutorial/11-others/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Others", + "link": { + "type": "generated-index" + } +} diff --git a/versioned_sidebars/version-2.x-sidebars.json b/versioned_sidebars/version-2.x-sidebars.json new file mode 100644 index 000000000..cf7852543 --- /dev/null +++ b/versioned_sidebars/version-2.x-sidebars.json @@ -0,0 +1,20 @@ +{ + "tutorialSidebar": [ + { + "type": "autogenerated", + "dirName": "tutorial" + } + ], + "apiSidebar": [ + { + "type": "autogenerated", + "dirName": "api" + } + ], + "contributingSidebar": [ + { + "type": "autogenerated", + "dirName": "contributing" + } + ] +} diff --git a/versions.json b/versions.json new file mode 100644 index 000000000..a61da5c98 --- /dev/null +++ b/versions.json @@ -0,0 +1,3 @@ +[ + "2.x" +] From 0cc17c0f6514902a1dc99effb29749195dd6a19f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=83=A1=E9=95=87?= Date: Mon, 17 Jun 2024 18:18:17 +0800 Subject: [PATCH 02/11] docs: doc refactor 2 --- docs/tutorial/02-getting-started/README.md | 244 +-- .../03-use-fetcher.md} | 546 +++--- ...use-pagination.md => 04-use-pagination.md} | 20 +- .../{04-use-form.md => 05-use-form.md} | 10 +- ...tication.md => 06-token-authentication.md} | 65 +- ...auto-request.md => 07-use-auto-request.md} | 8 +- ....md => 08-action-delegation-middleware.md} | 6 +- .../02-virtual-data.md | 0 .../03-start-silent-factory.md | 0 .../04-conservative-request.md | 0 .../05-modify-response.md | 0 .../06-request-retry.md | 0 .../07-data-compensation.md | 0 .../08-edit-item.md | 0 .../09-what-more.md | 0 .../README.md | 0 .../_category_.json | 0 .../{09-use-captcha.md => 10-use-captcha.md} | 8 +- ...al-request.md => 11-use-serial-request.md} | 8 +- ...al-watcher.md => 12-use-serial-watcher.md} | 8 +- ...request.md => 13-use-retriable-request.md} | 8 +- .../{13-use-sse.md => 14-use-sse.md} | 6 +- ...oader.md => 15-use-breakpoint-uploader.md} | 2 +- ...{15-use-uploader.md => 16-use-uploader.md} | 2 +- ...dleware.md => 17-rate-limit-middleware.md} | 2 +- docs/tutorial/04-server/01-retry.md | 4 +- docs/tutorial/05-cache/01-mode.md | 331 ++++ docs/tutorial/05-cache/02-auto-invalidate.md | 86 + .../05-cache/03-manually-invalidate.md | 99 ++ docs/tutorial/05-cache/04-force-request.md | 18 + docs/tutorial/05-cache/05-set-and-query.md | 89 + docs/tutorial/05-cache/06-controlled-cache.md | 56 + docs/tutorial/05-cache/README.md | 11 + docs/tutorial/05-cache/_category_.json | 3 + ...ents.md => 01-update-across-components.md} | 210 +-- .../tutorial/06-advanced/02-method-matcher.md | 88 + .../tutorial/06-advanced/03-method-matcher.md | 141 -- docs/tutorial/06-advanced/06-error-logger.md | 37 - .../01-manage-apis.md | 0 .../02-skills.md | 0 .../03-manage-cache-by-indexeddb.md | 0 .../04-multiple-servers.md | 0 .../05-middleware.md | 0 .../07-best-practice/06-parallel-request.md | 68 + .../07-best-practice/07-serial-request.md | 54 + .../07-best-practice/08-l2-storage.md | 0 .../_category_.json | 0 docs/tutorial/07-cache/01-mode.md | 215 --- docs/tutorial/07-cache/02-auto-invalidate.md | 91 - .../07-cache/03-manually-invalidate.md | 102 -- docs/tutorial/07-cache/04-force-request.md | 75 - docs/tutorial/07-cache/05-set-and-query.md | 284 ---- docs/tutorial/07-cache/06-controlled-cache.md | 83 - docs/tutorial/07-cache/07-server-cache.md | 8 - docs/tutorial/07-cache/README.md | 9 - docs/tutorial/07-cache/_category_.json | 3 - .../08-request-adapter/01-alova-mock.md | 662 ++++---- .../current.json | 6 +- .../tutorial/02-getting-started/08-server.md | 2 +- .../tutorial/02-getting-started/README.md | 266 ++- .../03-use-fetcher.md} | 548 +++---- ...use-pagination.md => 04-use-pagination.md} | 20 +- .../{04-use-form.md => 05-use-form.md} | 10 +- ...tication.md => 06-token-authentication.md} | 67 +- ...auto-request.md => 07-use-auto-request.md} | 8 +- ....md => 08-action-delegation-middleware.md} | 6 +- .../02-virtual-data.md | 0 .../03-start-silent-factory.md | 0 .../04-conservative-request.md | 0 .../05-modify-response.md | 0 .../06-request-retry.md | 0 .../07-data-compensation.md | 0 .../08-edit-item.md | 0 .../09-what-more.md | 0 .../README.md | 0 .../_category_.json | 0 .../{09-use-captcha.md => 10-use-captcha.md} | 8 +- ...al-request.md => 11-use-serial-request.md} | 8 +- ...al-watcher.md => 12-use-serial-watcher.md} | 8 +- ...request.md => 13-use-retriable-request.md} | 8 +- .../{13-use-sse.md => 14-use-sse.md} | 6 +- ...oader.md => 15-use-breakpoint-uploader.md} | 2 +- ...{15-use-uploader.md => 16-use-uploader.md} | 2 +- ...dleware.md => 17-rate-limit-middleware.md} | 2 +- .../current/tutorial/04-server/01-retry.md | 4 +- .../current/tutorial/05-cache/01-mode.md | 329 ++++ .../02-auto-invalidate.md | 177 +- .../03-manually-invalidate.md | 201 ++- .../tutorial/05-cache/04-force-request.md | 18 + .../tutorial/05-cache/05-set-and-query.md | 89 + .../tutorial/05-cache/06-controlled-cache.md | 55 + .../current/tutorial/05-cache/README.md | 11 + .../current/tutorial/05-cache/_category_.json | 3 + ...ents.md => 01-update-across-components.md} | 210 +-- .../tutorial/06-advanced/02-method-matcher.md | 88 + .../tutorial/06-advanced/03-method-matcher.md | 141 -- .../tutorial/06-advanced/04-middleware.md | 674 +++----- .../tutorial/06-advanced/06-error-logger.md | 51 - .../01-manage-apis.md | 0 .../02-skills.md | 0 .../03-manage-cache-by-indexeddb.md | 0 .../04-multiple-servers.md | 0 .../05-middleware.md | 58 +- .../06-parallel-request.md} | 128 +- .../07-serial-request.md} | 103 +- .../07-best-practice/08-l2-storage.md | 138 ++ .../current/tutorial/07-cache/01-mode.md | 218 --- .../tutorial/07-cache/04-force-request.md | 75 - .../tutorial/07-cache/05-set-and-query.md | 284 ---- .../tutorial/07-cache/06-controlled-cache.md | 83 - .../tutorial/07-cache/07-server-cache.md | 8 - .../current/tutorial/07-cache/README.md | 9 - .../current/tutorial/07-cache/_category_.json | 3 - .../08-request-adapter/01-alova-mock.md | 658 ++++---- .../tutorial/05-strategy/02-usePagination.md | 1460 +++++++++-------- .../tutorial/05-strategy/03-useForm.md | 1050 ++++++------ .../05-strategy/04-tokenAuthentication.md | 1137 ++++++------- .../tutorial/05-strategy/06-useAutoRequest.md | 339 ++-- .../tutorial/05-strategy/08-useCaptcha.md | 426 ++--- .../09-actionDelegationMiddleware.md | 488 +++--- .../05-strategy/10-useSerialRequest.md | 288 ++-- .../05-strategy/11-useSerialWatcher.md | 290 ++-- .../05-strategy/12-useRetriableRequest.md | 538 +++--- .../tutorial/05-strategy/13-useSSE.md | 576 ++++--- .../08-request-adapter/01-alova-mock.md | 658 ++++---- .../tutorial/05-strategy/02-usePagination.md | 1460 +++++++++-------- .../tutorial/05-strategy/03-useForm.md | 1050 ++++++------ .../05-strategy/04-tokenAuthentication.md | 1133 ++++++------- .../tutorial/05-strategy/06-useAutoRequest.md | 339 ++-- .../tutorial/05-strategy/08-useCaptcha.md | 432 ++--- .../09-actionDelegationMiddleware.md | 488 +++--- .../05-strategy/10-useSerialRequest.md | 288 ++-- .../05-strategy/11-useSerialWatcher.md | 290 ++-- .../05-strategy/12-useRetriableRequest.md | 538 +++--- .../tutorial/05-strategy/13-useSSE.md | 576 ++++--- .../08-request-adapter/01-alova-mock.md | 662 ++++---- 136 files changed, 11337 insertions(+), 11732 deletions(-) rename docs/tutorial/{06-advanced/01-use-fetcher.md => 03-client/03-use-fetcher.md} (61%) rename docs/tutorial/03-client/{03-use-pagination.md => 04-use-pagination.md} (97%) rename docs/tutorial/03-client/{04-use-form.md => 05-use-form.md} (99%) rename docs/tutorial/03-client/{05-token-authentication.md => 06-token-authentication.md} (86%) rename docs/tutorial/03-client/{06-use-auto-request.md => 07-use-auto-request.md} (90%) rename docs/tutorial/03-client/{07-action-delegation-middleware.md => 08-action-delegation-middleware.md} (98%) rename docs/tutorial/03-client/{08-sensorless-data-interaction => 09-sensorless-data-interaction}/02-virtual-data.md (100%) rename docs/tutorial/03-client/{08-sensorless-data-interaction => 09-sensorless-data-interaction}/03-start-silent-factory.md (100%) rename docs/tutorial/03-client/{08-sensorless-data-interaction => 09-sensorless-data-interaction}/04-conservative-request.md (100%) rename docs/tutorial/03-client/{08-sensorless-data-interaction => 09-sensorless-data-interaction}/05-modify-response.md (100%) rename docs/tutorial/03-client/{08-sensorless-data-interaction => 09-sensorless-data-interaction}/06-request-retry.md (100%) rename docs/tutorial/03-client/{08-sensorless-data-interaction => 09-sensorless-data-interaction}/07-data-compensation.md (100%) rename docs/tutorial/03-client/{08-sensorless-data-interaction => 09-sensorless-data-interaction}/08-edit-item.md (100%) rename docs/tutorial/03-client/{08-sensorless-data-interaction => 09-sensorless-data-interaction}/09-what-more.md (100%) rename docs/tutorial/03-client/{08-sensorless-data-interaction => 09-sensorless-data-interaction}/README.md (100%) rename docs/tutorial/03-client/{08-sensorless-data-interaction => 09-sensorless-data-interaction}/_category_.json (100%) rename docs/tutorial/03-client/{09-use-captcha.md => 10-use-captcha.md} (96%) rename docs/tutorial/03-client/{10-use-serial-request.md => 11-use-serial-request.md} (93%) rename docs/tutorial/03-client/{11-use-serial-watcher.md => 12-use-serial-watcher.md} (93%) rename docs/tutorial/03-client/{12-use-retriable-request.md => 13-use-retriable-request.md} (98%) rename docs/tutorial/03-client/{13-use-sse.md => 14-use-sse.md} (98%) rename docs/tutorial/03-client/{14-use-breakpoint-uploader.md => 15-use-breakpoint-uploader.md} (69%) rename docs/tutorial/03-client/{15-use-uploader.md => 16-use-uploader.md} (72%) rename docs/tutorial/03-client/{16-rate-limit-middleware.md => 17-rate-limit-middleware.md} (89%) create mode 100644 docs/tutorial/05-cache/01-mode.md create mode 100644 docs/tutorial/05-cache/02-auto-invalidate.md create mode 100644 docs/tutorial/05-cache/03-manually-invalidate.md create mode 100644 docs/tutorial/05-cache/04-force-request.md create mode 100644 docs/tutorial/05-cache/05-set-and-query.md create mode 100644 docs/tutorial/05-cache/06-controlled-cache.md create mode 100644 docs/tutorial/05-cache/README.md create mode 100644 docs/tutorial/05-cache/_category_.json rename docs/tutorial/06-advanced/{02-update-across-components.md => 01-update-across-components.md} (98%) create mode 100644 docs/tutorial/06-advanced/02-method-matcher.md delete mode 100644 docs/tutorial/06-advanced/03-method-matcher.md delete mode 100644 docs/tutorial/06-advanced/06-error-logger.md rename docs/tutorial/{05-best-practice => 07-best-practice}/01-manage-apis.md (100%) rename docs/tutorial/{05-best-practice => 07-best-practice}/02-skills.md (100%) rename docs/tutorial/{05-best-practice => 07-best-practice}/03-manage-cache-by-indexeddb.md (100%) rename docs/tutorial/{05-best-practice => 07-best-practice}/04-multiple-servers.md (100%) rename docs/tutorial/{05-best-practice => 07-best-practice}/05-middleware.md (100%) create mode 100644 docs/tutorial/07-best-practice/06-parallel-request.md create mode 100644 docs/tutorial/07-best-practice/07-serial-request.md create mode 100644 docs/tutorial/07-best-practice/08-l2-storage.md rename docs/tutorial/{05-best-practice => 07-best-practice}/_category_.json (100%) delete mode 100644 docs/tutorial/07-cache/01-mode.md delete mode 100644 docs/tutorial/07-cache/02-auto-invalidate.md delete mode 100644 docs/tutorial/07-cache/03-manually-invalidate.md delete mode 100644 docs/tutorial/07-cache/04-force-request.md delete mode 100644 docs/tutorial/07-cache/05-set-and-query.md delete mode 100644 docs/tutorial/07-cache/06-controlled-cache.md delete mode 100644 docs/tutorial/07-cache/07-server-cache.md delete mode 100644 docs/tutorial/07-cache/README.md delete mode 100644 docs/tutorial/07-cache/_category_.json rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{06-advanced/01-use-fetcher.md => 03-client/03-use-fetcher.md} (57%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{03-use-pagination.md => 04-use-pagination.md} (98%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{04-use-form.md => 05-use-form.md} (98%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{05-token-authentication.md => 06-token-authentication.md} (86%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{06-use-auto-request.md => 07-use-auto-request.md} (90%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{07-action-delegation-middleware.md => 08-action-delegation-middleware.md} (98%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{08-sensorless-data-interaction => 09-sensorless-data-interaction}/02-virtual-data.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{08-sensorless-data-interaction => 09-sensorless-data-interaction}/03-start-silent-factory.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{08-sensorless-data-interaction => 09-sensorless-data-interaction}/04-conservative-request.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{08-sensorless-data-interaction => 09-sensorless-data-interaction}/05-modify-response.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{08-sensorless-data-interaction => 09-sensorless-data-interaction}/06-request-retry.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{08-sensorless-data-interaction => 09-sensorless-data-interaction}/07-data-compensation.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{08-sensorless-data-interaction => 09-sensorless-data-interaction}/08-edit-item.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{08-sensorless-data-interaction => 09-sensorless-data-interaction}/09-what-more.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{08-sensorless-data-interaction => 09-sensorless-data-interaction}/README.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{08-sensorless-data-interaction => 09-sensorless-data-interaction}/_category_.json (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{09-use-captcha.md => 10-use-captcha.md} (97%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{10-use-serial-request.md => 11-use-serial-request.md} (93%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{11-use-serial-watcher.md => 12-use-serial-watcher.md} (93%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{12-use-retriable-request.md => 13-use-retriable-request.md} (98%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{13-use-sse.md => 14-use-sse.md} (98%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{14-use-breakpoint-uploader.md => 15-use-breakpoint-uploader.md} (69%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{15-use-uploader.md => 16-use-uploader.md} (99%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{16-rate-limit-middleware.md => 17-rate-limit-middleware.md} (85%) create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/01-mode.md rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{07-cache => 05-cache}/02-auto-invalidate.md (67%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{07-cache => 05-cache}/03-manually-invalidate.md (71%) create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/04-force-request.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/05-set-and-query.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/06-controlled-cache.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/README.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/_category_.json rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/{02-update-across-components.md => 01-update-across-components.md} (97%) create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/02-method-matcher.md delete mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/03-method-matcher.md delete mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/06-error-logger.md rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{05-best-practice => 07-best-practice}/01-manage-apis.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{05-best-practice => 07-best-practice}/02-skills.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{05-best-practice => 07-best-practice}/03-manage-cache-by-indexeddb.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{05-best-practice => 07-best-practice}/04-multiple-servers.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{05-best-practice => 07-best-practice}/05-middleware.md (96%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{05-best-practice/12-parallel-request.md => 07-best-practice/06-parallel-request.md} (85%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{05-best-practice/13-serial-request.md => 07-best-practice/07-serial-request.md} (84%) create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/08-l2-storage.md delete mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/01-mode.md delete mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/04-force-request.md delete mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/05-set-and-query.md delete mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/06-controlled-cache.md delete mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/07-server-cache.md delete mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/README.md delete mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/_category_.json diff --git a/docs/tutorial/02-getting-started/README.md b/docs/tutorial/02-getting-started/README.md index 17ece98ed..62ef3324e 100644 --- a/docs/tutorial/02-getting-started/README.md +++ b/docs/tutorial/02-getting-started/README.md @@ -1,122 +1,122 @@ ---- -title: What is alova ---- - -import Link from '@docusaurus/Link'; -import NavCard from '@site/src/components/NavCard'; -import SupportList from '@site/src/components/SupportList'; - -alova.js is an innovative next-generation request tool that can help you save most of the work in request, solve the problem of front-end and back-end collaboration, and make network requests very simple. Let's see how alova can help you simplify your work. - -![](/img/overview_flow_en.png) - -It can simplify API consumption from 7 steps to 1 step. You only need to choose the API to use. - -## How to do it? - -### Request strategy - -In actual projects, front-end requests always need to consider when to make requests, when not to make requests, how to process response data, etc. according to different scenarios to meet the performance and performance improvement of the project. This will lead to an increase in the developer's time cost and code maintenance cost. In alova, a complete set of solutions for complex request scenarios is provided, which we call **request strategy**. Only one line of code can quickly implement various complex request logics, which can not only help you improve development efficiency, but also help you improve the running efficiency of the App and reduce the pressure on the server. - -For example, `useRequest` can automatically manage the request status, **`loading/error/data` is responsive data**, which can be directly bound in the view in UI frameworks such as react, vue, and svelte, and these responsive data will be automatically maintained according to the request status. - -```javascript -const { loading, error, data } = useRequest(alova.Get('/api/user')); -``` - -Another paging request strategy, **when `page/pageSize` changes, it will automatically trigger requests with different parameters**. - -```javascript -const { loading, error, data, page, pageSize, total } = usePagination((page, size) => - alova.Get('/api/user/list', { - params: { page, size } - }) -); -``` - -alova provides 10+ request strategy modules based on the [RSM](/tutorial/others/RSM) specification, which are implemented in the form of useHook. - -### Alova editor extension - -Using the alova plugin in vscode can help you automatically generate request code with complete API document annotations and response types. In the past, you needed to query the API documentation first and constantly switch between the API documentation and the editor to write request code. After using the alova plugin, you no longer need to leave the editor, and you can directly use the API while checking in the editor to experience a different API usage experience. - -> For a detailed introduction to the alova plugin, please refer to [Integrated IDE plugin](/tutorial/getting-started/plugin-integration). - -## Is there any difference? - -Unlike other request libraries, alova aims to make requests very simple and maintain more efficient data interaction. - -We consider both developers and App users. For developers, alova provides them with the ultimate user experience, and for application users, they can enjoy the smooth experience brought by alova's high-performance data interaction. - -In addition, let's take a look at the specific features: - -- Similar API design to Axios, which makes the user's learning cost lower; -- High-performance client and server request strategies make the application smoother; -- High flexibility, Alova's adapter allows Alova to work with any UI framework in any JS environment, and provides a unified user experience and perfect code migration; -- 2 cache modes and request sharing mechanisms to improve request performance and reduce server pressure; -- High aggregation organization of API code, each API's request parameters, cache behavior, response data conversion, etc. will be gathered in the same code block, which is a great advantage for managing a large number of APIs; - -:::info comparison - -You can also check [Comparison with other request libraries](/tutorial/others/comparison) to learn more about the differences of Alova. - -::: - -## Run in any JS environment - -Not only that, Alova is very flexible, you can use it with different request tools in any of the following JS environments (the gray part will be gradually supported in the future). - - - -## Online trial - -You can run the project directly in the browser via Codesandbox [online editable examples try alovajs](/category/examples), so it's almost indistinguishable from local development without having to install anything on your machine. - -## Join alova community - -import ImgDiscord from '@site/static/img/discord.svg'; -import ImgX from '@site/static/img/x.svg'; -import ImgWechat from '@site/static/img/wechat.svg'; -import wechatQrcode from '@site/static/img/wechat_qrcode.jpg'; - -, -title: 'Discord', -desc: 'The community\'s GPT robot will answer your questions', -link: 'https://discord.gg/S47QGJgkVb', -target: '__blank' -}, -{ -Image: , -title: 'WeChat', -desc: 'Communicate in group chat and get responses faster', -link: wechatQrcode, -target: '__blank' -}, -{ -Image: , -title: 'X', -desc: 'Follow us and continue to receive the latest news', -link: 'https://x.com/alovajs', -target: '__blank' -} -]}> - -## Welcome to contribute - -Before contributing, please be sure to read the [Contribution Guide](/contributing/overview) in detail to ensure your effective contribution. - -## Start - -Next, we will start with the simplest request, then explain the request strategy, understand how alova simplifies your work, and then go into the advanced guide and the best practices summarized in actual projects. - -Let’s start learning to send our first request! - - +--- +title: What is alova +--- + +import Link from '@docusaurus/Link'; +import NavCard from '@site/src/components/NavCard'; +import SupportList from '@site/src/components/SupportList'; + +Alova is an creative next-generation request tool that takes front-end and back-end collaboration and API consumption as its starting point, simplifies API consumption from 7 steps to only 1, saving you most of the work in requesting and making network requests very simple. Let's see how Alova can help you simplify your work. + +![](/img/overview_flow_en.png) + +It can simplify API consumption from 7 steps to 1 step. You only need to choose the API to use. + +## How to do it? + +### Request strategy + +In actual projects, front-end requests always need to consider when to make requests, when not to make requests, how to process response data, etc. according to different scenarios to meet the performance and performance improvement of the project. This will lead to an increase in the developer's time cost and code maintenance cost. In alova, a complete set of solutions for complex request scenarios is provided, which we call **request strategy**. Only one line of code can quickly implement various complex request logics, which can not only help you improve development efficiency, but also help you improve the running efficiency of the App and reduce the pressure on the server. + +For example, `useRequest` can automatically manage the request status, **`loading/error/data` is responsive data**, which can be directly bound in the view in UI frameworks such as react, vue, and svelte, and these responsive data will be automatically maintained according to the request status. + +```javascript +const { loading, error, data } = useRequest(alova.Get('/api/user')); +``` + +Another paging request strategy, **when `page/pageSize` changes, it will automatically trigger requests with different parameters**. + +```javascript +const { loading, error, data, page, pageSize, total } = usePagination((page, size) => + alova.Get('/api/user/list', { + params: { page, size } + }) +); +``` + +alova provides 10+ request strategy modules based on the [RSM](/tutorial/others/RSM) specification, which are implemented in the form of useHook. + +### Alova editor extension + +Using the alova plugin in vscode can help you automatically generate request code with complete API document annotations and response types. In the past, you needed to query the API documentation first and constantly switch between the API documentation and the editor to write request code. After using the alova plugin, you no longer need to leave the editor, and you can directly use the API while checking in the editor to experience a different API usage experience. + +> For a detailed introduction to the alova plugin, please refer to [Integrated IDE plugin](/tutorial/getting-started/plugin-integration). + +## Is there any difference? + +Unlike other request libraries, alova aims to make requests very simple and maintain more efficient data interaction. + +We consider both developers and App users. For developers, alova provides them with the ultimate user experience, and for application users, they can enjoy the smooth experience brought by alova's high-performance data interaction. + +In addition, let's take a look at the specific features: + +- Similar API design to Axios, which makes the user's learning cost lower; +- High-performance client and server request strategies make the application smoother; +- High flexibility, Alova's adapter allows Alova to work with any UI framework in any JS environment, and provides a unified user experience and perfect code migration; +- 2 cache modes and request sharing mechanisms to improve request performance and reduce server pressure; +- High aggregation organization of API code, each API's request parameters, cache behavior, response data conversion, etc. will be gathered in the same code block, which is a great advantage for managing a large number of APIs; + +:::info comparison + +You can also check [Comparison with other request libraries](/tutorial/others/comparison) to learn more about the differences of Alova. + +::: + +## Run in any JS environment + +Not only that, Alova is very flexible, you can use it with different request tools in any of the following JS environments (the gray part will be gradually supported in the future). + + + +## Online trial + +You can run the project directly in the browser via Codesandbox [online editable examples try alovajs](/category/examples), so it's almost indistinguishable from local development without having to install anything on your machine. + +## Join alova community + +import ImgDiscord from '@site/static/img/discord.svg'; +import ImgX from '@site/static/img/x.svg'; +import ImgWechat from '@site/static/img/wechat.svg'; +import wechatQrcode from '@site/static/img/wechat_qrcode.jpg'; + +, +title: 'Discord', +desc: 'The community\'s GPT robot will answer your questions', +link: 'https://discord.gg/S47QGJgkVb', +target: '__blank' +}, +{ +Image: , +title: 'WeChat', +desc: 'Communicate in group chat and get responses faster', +link: wechatQrcode, +target: '__blank' +}, +{ +Image: , +title: 'X', +desc: 'Follow us and continue to receive the latest news', +link: 'https://x.com/alovajs', +target: '__blank' +} +]}> + +## Welcome to contribute + +Before contributing, please be sure to read the [Contribution Guide](/contributing/overview) in detail to ensure your effective contribution. + +## Start + +Next, we will start with the simplest request, then explain the request strategy, understand how alova simplifies your work, and then go into the advanced guide and the best practices summarized in actual projects. + +Let’s start learning to send our first request! + + diff --git a/docs/tutorial/06-advanced/01-use-fetcher.md b/docs/tutorial/03-client/03-use-fetcher.md similarity index 61% rename from docs/tutorial/06-advanced/01-use-fetcher.md rename to docs/tutorial/03-client/03-use-fetcher.md index f51eab080..7eccb58ed 100644 --- a/docs/tutorial/06-advanced/01-use-fetcher.md +++ b/docs/tutorial/03-client/03-use-fetcher.md @@ -1,307 +1,239 @@ ---- -title: Fetch Data -sidebar_position: 10 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -When you have the following needs: - -1. Preload the data that will be used in subsequent processes and store it in the cache, so that users no longer have to wait for the data loading process; -2. Conveniently implement cross-page data update (similar to global state), for example, modify an item in the todo list and then re-fetch the latest data, and the interface will be refreshed after the response. - -`useFetcher` is the hook used to implement the above scenario. The response data obtained through it cannot be received directly, but the data fetched through it will not only update the cache, but also update the corresponding state, thereby re-rendering the view. - -## Preload data - -Let's implement a paging list to automatically preload the next page of data. Before preloading data, please make sure that the Method instance used has enabled caching. - - - - -```html - - - -``` - - - - -```jsx -import { useState } from 'react'; - -//method instance creation function -const getTodoList = currentPage => { - return alovaInstance.Get('/todo/list', { - localCache: 60000, - params: { - currentPage, - pageSize: 10 - } - }); -}; - -const App = () => { - const { - // The fetching attribute is the same as loading. It is true when sending the fetch request and false after the request is completed. - fetching, - error, - onSuccess, - onError, - onComplete, - - // Only after calling fetch will a request be sent to fetch data. You can call fetch repeatedly to fetch data from different interfaces. - fetch - } = useFetcher({ - updateState: false - }); - const [currentPage, setCurrentPage] = useState(1); - const { data, onSuccess } = useWatcher(() => getTodoList(currentPage), [currentPage], { - immediate: true - }); - - // After the current page is loaded successfully, pass in the method instance of the next page to pre-fetch the data of the next page. - onSuccess(() => { - fetch(getTodoList(currentPage + 1)); - }); - - return ( - <> - {fetching ?
Fetching...
: null} - {/* list view */} - - ); -}; -``` - -
- - -```html - - -{#if fetching} -
Fetching...
-{/if} - -``` - -
- - -```html - - - -``` - - -
- -:::warning - -The above example is set `updateState` to false when calling `useFetcher`. This is because the data fetching will automatically trigger a states cross-component updating by default, causing the view to be re-rendered. When preload data is the same as the currently requested data. You can set it to false to avoid affecting view errors. - -::: - -## Update views across modules/components - -Next, we will modify a todo data and re-fetch the latest todo list data to update the view. We may not know which page the todo list is currently on. In this case, when using the `fetch` function, we can use [Method instance matcher](/tutorial/advanced/method-matcher) to dynamically fetch the data of the current page. - -> The Method instance matcher is used to find method instances that meet the conditions among the requested method instances. - -First, set a name for the method instance in the todo list, which is used to filter out the required Method instance when the Method instance cannot be specified directly. - -```javascript title="api/todoList.js" -const getTodoList = currentPage => { - return alovaInstance.Get('/todo/list', { - // highlight-start - name: 'todoList', - // highlight-end - params: { - currentPage, - pageSize: 10 - } - }); -}; -``` - -Then in the `EditTodo` component, use the `fetch` function to dynamically find the last name of `todoList` in the requested Method instance to fetch data. - -```javascript title="EditTodo Component" -const { fetch } = useFetcher(); - -// Trigger data fetch in event -const handleSubmit = () => { - // submit data... - await fetch({ - name: 'todoList', - filter: (method, index, ary) => { - // Return true to specify the Method instance that needs to be fetched - return index === ary.length - 1; - } - }); -}; -``` - -:::warning Notes - -useFetcher only updates the cache after the request is completed, and if this Method is foundIf the instance has been requested using useHook before, the `data` state created by this useHook will also be updated to ensure that the page data is consistent. This is the guarantee that `useFetcher` is used to update views across modules/components. - -::: - -> For more methods of using `Method` instance matcher, see [Method instance matcher](/tutorial/advanced/method-matcher). - -## Force sending request - -Same as `useRequest` and `useWatcher`, please read [Force Request](/tutorial/cache/force-request) for more information. - -## Bind response callback - -useFetcher also supports binding `onSuccess/onError/onComplete` callback functions. - -```javascript -const { onSuccess, onError, onComplete } = useFetcher(); -``` - -Please read [Response Processing](/tutorial/combine-framework/response) for details. - -## send function parameter passing rules - -Different from `useRequest` and `useWatcher`, the custom parameters of the fetch function start from the second parameter, and they will also be received by the event callback and `force` function respectively. - -```javascript -const { onSuccess, fetch } = useFetcher(); -onSuccess(({ sendArgs }) => { - //The value of sendArgs is ['test arg'] -}); - -fetch(getTodoList(), 'test arg'); -``` - -For details, please read [send function parameter passing rules](/tutorial/combine-framework/receive-params). - -## Comparison between useRequest and useFetcher - -1. useFetcher does not return the `data` field, the pre-fetched data will be saved in the cache, and the status data of the corresponding location will be updated; -2. Renamed `loading` to `fetching`; -3. There is no `send` function, but there is a `fetch` function. You can reuse the fetch function to fetch data from different interfaces. At this time, you can use the `fetching` and `error` states to uniformly render the view to achieve unified processing. the goal of; +--- +title: Fetch Data +sidebar_position: 30 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info 策略类型 + +use hook + +::: + +When you have the following needs: + +1. Preload the data that will be used in subsequent processes and store it in the cache, so that users no longer have to wait for the data loading process; +2. Conveniently implement cross-page data update (similar to global state), for example, modify an item in the todo list and then re-fetch the latest data, and the interface will be refreshed after the response. + +`useFetcher` is the hook used to implement the above scenario. The response data obtained through it cannot be received directly, but the data fetched through it will not only update the cache, but also update the corresponding state, thereby re-rendering the view. + +## Preload data + +Let's implement a paging list to automatically preload the next page of data. Before preloading data, please make sure that the Method instance used has enabled caching. + + + + +```html + + + +``` + + + + +```jsx +import { useState } from 'react'; +import { useFetcher } from 'alova/client'; + +//method instance creation function +const getTodoList = currentPage => { + return alovaInstance.Get('/todo/list', { + localCache: 60000, + params: { + currentPage, + pageSize: 10 + } + }); +}; + +const App = () => { + const { + // the loading is the status of data fetching + loading, + error, + onSuccess, + onError, + onComplete, + + // Only after calling fetch will a request be sent to fetch data. You can call fetch repeatedly to fetch data from different interfaces. + fetch + } = useFetcher({ + updateState: false + }); + const [currentPage, setCurrentPage] = useState(1); + const { data, onSuccess } = useWatcher(() => getTodoList(currentPage), [currentPage], { + immediate: true + }); + + // After the current page is loaded successfully, pass in the method instance of the next page to pre-fetch the data of the next page. + onSuccess(() => { + fetch(getTodoList(currentPage + 1)); + }); + + return ( + <> + {loading ?
Fetching...
: null} + {/* list view */} + + ); +}; +``` + +
+ + +```html + + +{#if loading} +
Fetching...
+{/if} + +``` + +
+
+ +:::warning + +The above example is set `updateState` to false when calling `useFetcher`. This is because the data fetching will automatically trigger a states cross-component updating by default, causing the view to be re-rendered. When preload data is the same as the currently requested data. You can set it to false to avoid affecting view errors. + +::: + +## Update views across modules/components + +Next, we will modify a todo data and re-fetch the latest todo list data to update the view. We may not know which page the todo list is currently on. In this case, when using the `fetch` function, we can use [Method snapshots matcher](/tutorial/advanced/method-matcher) to dynamically fetch the data of the current page. + +> The Method snapshots matcher is used to find method instances that meet the conditions among the requested method instances. + +First, set a name for the method instance in the todo list, which is used to filter out the required Method instance when the Method instance cannot be specified directly. + +```javascript title="api/todoList.js" +const getTodoList = currentPage => { + return alovaInstance.Get('/todo/list', { + // highlight-start + name: 'todoList', + // highlight-end + params: { + currentPage, + pageSize: 10 + } + }); +}; +``` + +Then in the `EditTodo` component, use the `fetch` function to dynamically find the last name of `todoList` in the requested Method instance to fetch data. + +```javascript title="EditTodo Component" +const { fetch } = useFetcher(); + +// Trigger data fetch in event +const handleSubmit = () => { + // submit data... + // highlight-start + const lastMethod = alovaInstance.snapshots.match({ + name: 'todoList', + filter: (method, index, ary) => { + // Return true to specify the Method instance that needs to be fetched + return index === ary.length - 1; + } + }, true); + if (lastMethod) { + await fetch(lastMethod); + } + // highlight-end +}; +``` + +:::warning Notes + +useFetcher only updates the cache after the request is completed, and if this Method is foundIf the instance has been requested using useHook before, the `data` state created by this useHook will also be updated to ensure that the page data is consistent. This is the guarantee that `useFetcher` is used to update views across modules/components. + +::: + +> For more methods of using `Method` instance matcher, see [Method instance matcher](/tutorial/advanced/method-matcher). + +## Force sending request + +Same as `useRequest` and `useWatcher`, please read [Force Request](/tutorial/cache/force-request) for more information. diff --git a/docs/tutorial/03-client/03-use-pagination.md b/docs/tutorial/03-client/04-use-pagination.md similarity index 97% rename from docs/tutorial/03-client/03-use-pagination.md rename to docs/tutorial/03-client/04-use-pagination.md index 5b2f812d2..380797923 100644 --- a/docs/tutorial/03-client/03-use-pagination.md +++ b/docs/tutorial/03-client/04-use-pagination.md @@ -1,6 +1,6 @@ --- title: Pagination request strategy -sidebar_position: 30 +sidebar_position: 40 --- import Tabs from '@theme/Tabs'; @@ -24,15 +24,15 @@ A hook designed for paging scenarios, which can help you automatically manage pa ## Features -- ✨ Rich and comprehensive paging status; -- ✨ Rich and comprehensive pagination events; -- ✨ Change page, pageSize to automatically get specified paging data; -- ✨Data caching, no need to repeatedly request list data of the same parameters; -- ✨ Front and back pages are preloaded, no waiting for page turning; -- ✨Search condition monitoring automatically reacquires pages; -- ✨ Support adding, editing and deleting list data; -- ✨ Support refreshing the data of the specified page without reset; -- ✨ Request-level search anti-shake, no need to maintain by yourself; +- Rich and comprehensive paging status; +- Rich and comprehensive pagination events; +- Change page, pageSize to automatically get specified paging data; +- Data caching, no need to repeatedly request list data of the same parameters; +- Front and back pages are preloaded, no waiting for page turning; +- Search condition monitoring automatically reacquires pages; +- Support adding, editing and deleting list data; +- Support refreshing the data of the specified page without reset; +- Request-level search anti-shake, no need to maintain by yourself; ## Usage diff --git a/docs/tutorial/03-client/04-use-form.md b/docs/tutorial/03-client/05-use-form.md similarity index 99% rename from docs/tutorial/03-client/04-use-form.md rename to docs/tutorial/03-client/05-use-form.md index adca77a56..5baa7eb2f 100644 --- a/docs/tutorial/03-client/04-use-form.md +++ b/docs/tutorial/03-client/05-use-form.md @@ -1,6 +1,6 @@ --- title: Form submiting strategy -sidebar_position: 40 +sidebar_position: 50 --- import Tabs from '@theme/Tabs'; @@ -22,10 +22,10 @@ A hook designed for form submission. Through this hook, you can easily implement ## Features -- ✨ draft form; -- ✨ Multi-page (multi-step) forms; -- ✨ Form submission automatically resets data; -- ✨Reset form data manually; +- draft form; +- Multi-page (multi-step) forms; +- Form submission automatically resets data; +- Reset form data manually; ## Usage diff --git a/docs/tutorial/03-client/05-token-authentication.md b/docs/tutorial/03-client/06-token-authentication.md similarity index 86% rename from docs/tutorial/03-client/05-token-authentication.md rename to docs/tutorial/03-client/06-token-authentication.md index f8099aa73..922ff7bf0 100644 --- a/docs/tutorial/03-client/05-token-authentication.md +++ b/docs/tutorial/03-client/06-token-authentication.md @@ -1,6 +1,6 @@ --- title: Token authentication interceptor -sidebar_position: 50 +sidebar_position: 60 --- import Tabs from '@theme/Tabs'; @@ -20,11 +20,11 @@ Token authentication interceptor provides unified management of token-based logi ## Features -- ✨ Unified maintenance of all codes for token identity authentication, including login, logout, token assignment, token refresh, etc.; -- ✨ Supports verification of token expiration on the client and server, and refreshes the token without any warning; -- ✨ Requests that rely on tokens automatically wait for the token refresh to complete before requesting; -- ✨ Set request ID with metadata; -- ✨ Automatically release visitor requests that do not rely on tokens; +- Unified maintenance of all codes for token identity authentication, including login, logout, token assignment, token refresh, etc.; +- Supports verification of token expiration on the client and server, and refreshes the token without any warning; +- Requests that rely on tokens automatically wait for the token refresh to complete before requesting; +- Set request ID with metadata; +- Automatically release visitor requests that do not rely on tokens; ## Bind Token authentication interceptor @@ -132,15 +132,27 @@ createClientTokenAuthentication({ // Triggered when the token expires, trigger the refresh token in this function handler: async method => { - const { token, refresh_token } = await refreshToken(); - localStorage.setItem('token', token); - localStorage.setItem('refresh_token', refresh_token); + try { + const { token, refresh_token } = await refreshToken(); + localStorage.setItem('token', token); + localStorage.setItem('refresh_token', refresh_token); + } catch (error) { + // redirect to login page once token refresh failed. + location.href = '/login'; + // and must throw error. + throw error; + } } } }); ``` -In order for the `refreshToken` request to pass smoothly, the `authRole` needs to be identified as `refreshToken` through metadata. +:::warning Attention + +1. In order for the `refreshToken` request to pass smoothly, the `authRole` needs to be marked as `refreshToken` through metadata. +2. If the token refresh fails, an error must be thrown to prevent the failed API from retrying and the waiting APIs from continuing the request. + +::: > For more information about metadata, go to [method metadata](/tutorial/getting-started/method-metadata). @@ -173,9 +185,16 @@ createServerTokenAuthentication({ // Triggered when the token expires, trigger the refresh token in this function handler: async (response, method) => { - const { token, refresh_token } = await refreshToken(); - localStorage.setItem('token', token); - localStorage.setItem('refresh_token', refresh_token); + try { + const { token, refresh_token } = await refreshToken(); + localStorage.setItem('token', token); + localStorage.setItem('refresh_token', refresh_token); + } catch (error) { + // redirect to login page once token refresh failed. + location.href = '/login'; + // and must throw error. + throw error; + } } } }); @@ -196,15 +215,27 @@ createServerTokenAuthentication({ // Triggered when the token expires, trigger the refresh token in this function handler: async (error, method) => { - const { token, refresh_token } = await refreshToken(); - localStorage.setItem('token', token); - localStorage.setItem('refresh_token', refresh_token); + try { + const { token, refresh_token } = await refreshToken(); + localStorage.setItem('token', token); + localStorage.setItem('refresh_token', refresh_token); + } catch (error) { + // redirect to login page once token refresh failed. + location.href = '/login'; + // and must throw error. + throw error; + } } } }); ``` -In order for the `refreshToken` request to pass smoothly, the `authRole` needs to be identified as `refreshToken` through metadata. +:::warning Attention + +1. In order for the `refreshToken` request to pass smoothly, the `authRole` needs to be marked as `refreshToken` through metadata. +2. If the token refresh fails, an error must be thrown to prevent the failed API from retrying and the waiting APIs from continuing the request. + +::: > For more information about metadata, please go to [method metadata](/tutorial/getting-started/method-metadata). diff --git a/docs/tutorial/03-client/06-use-auto-request.md b/docs/tutorial/03-client/07-use-auto-request.md similarity index 90% rename from docs/tutorial/03-client/06-use-auto-request.md rename to docs/tutorial/03-client/07-use-auto-request.md index fedd67de7..5db16896b 100644 --- a/docs/tutorial/03-client/06-use-auto-request.md +++ b/docs/tutorial/03-client/07-use-auto-request.md @@ -1,6 +1,6 @@ --- title: Automatically refetch data -sidebar_position: 60 +sidebar_position: 70 --- import Tabs from '@theme/Tabs'; @@ -18,9 +18,9 @@ Automatically fetch data through browser events or polling, allowing the interfa ## Features -- ✨ Supports refetching the newest data in scenarios such as browser focus, tab switching, network reconnection, polling requests, etc; -- ✨ Supports request throttling, only one request will be sent if triggered multiple times in a short period of time; -- ✨ Support custom event listening functions to adapt to usage scenarios in non-browser environments; +- Supports refetching the newest data in scenarios such as browser focus, tab switching, network reconnection, polling requests, etc; +- Supports request throttling, only one request will be sent if triggered multiple times in a short period of time; +- Support custom event listening functions to adapt to usage scenarios in non-browser environments; ## Basic usage diff --git a/docs/tutorial/03-client/07-action-delegation-middleware.md b/docs/tutorial/03-client/08-action-delegation-middleware.md similarity index 98% rename from docs/tutorial/03-client/07-action-delegation-middleware.md rename to docs/tutorial/03-client/08-action-delegation-middleware.md index 94b4b02e6..294f7163f 100644 --- a/docs/tutorial/03-client/07-action-delegation-middleware.md +++ b/docs/tutorial/03-client/08-action-delegation-middleware.md @@ -1,6 +1,6 @@ --- title: Cross components to trigger request -sidebar_position: 70 +sidebar_position: 80 --- import Tabs from '@theme/Tabs'; @@ -24,8 +24,8 @@ For example, after updating the menu data in a component, you can re-trigger the ## Features -- ✨Delegate the action function of any use hook in alova; -- ✨ Trigger the delegated action function anywhere; +- Delegate the action function of any use hook in alova; +- Trigger the delegated action function anywhere; ## Usage diff --git a/docs/tutorial/03-client/08-sensorless-data-interaction/02-virtual-data.md b/docs/tutorial/03-client/09-sensorless-data-interaction/02-virtual-data.md similarity index 100% rename from docs/tutorial/03-client/08-sensorless-data-interaction/02-virtual-data.md rename to docs/tutorial/03-client/09-sensorless-data-interaction/02-virtual-data.md diff --git a/docs/tutorial/03-client/08-sensorless-data-interaction/03-start-silent-factory.md b/docs/tutorial/03-client/09-sensorless-data-interaction/03-start-silent-factory.md similarity index 100% rename from docs/tutorial/03-client/08-sensorless-data-interaction/03-start-silent-factory.md rename to docs/tutorial/03-client/09-sensorless-data-interaction/03-start-silent-factory.md diff --git a/docs/tutorial/03-client/08-sensorless-data-interaction/04-conservative-request.md b/docs/tutorial/03-client/09-sensorless-data-interaction/04-conservative-request.md similarity index 100% rename from docs/tutorial/03-client/08-sensorless-data-interaction/04-conservative-request.md rename to docs/tutorial/03-client/09-sensorless-data-interaction/04-conservative-request.md diff --git a/docs/tutorial/03-client/08-sensorless-data-interaction/05-modify-response.md b/docs/tutorial/03-client/09-sensorless-data-interaction/05-modify-response.md similarity index 100% rename from docs/tutorial/03-client/08-sensorless-data-interaction/05-modify-response.md rename to docs/tutorial/03-client/09-sensorless-data-interaction/05-modify-response.md diff --git a/docs/tutorial/03-client/08-sensorless-data-interaction/06-request-retry.md b/docs/tutorial/03-client/09-sensorless-data-interaction/06-request-retry.md similarity index 100% rename from docs/tutorial/03-client/08-sensorless-data-interaction/06-request-retry.md rename to docs/tutorial/03-client/09-sensorless-data-interaction/06-request-retry.md diff --git a/docs/tutorial/03-client/08-sensorless-data-interaction/07-data-compensation.md b/docs/tutorial/03-client/09-sensorless-data-interaction/07-data-compensation.md similarity index 100% rename from docs/tutorial/03-client/08-sensorless-data-interaction/07-data-compensation.md rename to docs/tutorial/03-client/09-sensorless-data-interaction/07-data-compensation.md diff --git a/docs/tutorial/03-client/08-sensorless-data-interaction/08-edit-item.md b/docs/tutorial/03-client/09-sensorless-data-interaction/08-edit-item.md similarity index 100% rename from docs/tutorial/03-client/08-sensorless-data-interaction/08-edit-item.md rename to docs/tutorial/03-client/09-sensorless-data-interaction/08-edit-item.md diff --git a/docs/tutorial/03-client/08-sensorless-data-interaction/09-what-more.md b/docs/tutorial/03-client/09-sensorless-data-interaction/09-what-more.md similarity index 100% rename from docs/tutorial/03-client/08-sensorless-data-interaction/09-what-more.md rename to docs/tutorial/03-client/09-sensorless-data-interaction/09-what-more.md diff --git a/docs/tutorial/03-client/08-sensorless-data-interaction/README.md b/docs/tutorial/03-client/09-sensorless-data-interaction/README.md similarity index 100% rename from docs/tutorial/03-client/08-sensorless-data-interaction/README.md rename to docs/tutorial/03-client/09-sensorless-data-interaction/README.md diff --git a/docs/tutorial/03-client/08-sensorless-data-interaction/_category_.json b/docs/tutorial/03-client/09-sensorless-data-interaction/_category_.json similarity index 100% rename from docs/tutorial/03-client/08-sensorless-data-interaction/_category_.json rename to docs/tutorial/03-client/09-sensorless-data-interaction/_category_.json diff --git a/docs/tutorial/03-client/09-use-captcha.md b/docs/tutorial/03-client/10-use-captcha.md similarity index 96% rename from docs/tutorial/03-client/09-use-captcha.md rename to docs/tutorial/03-client/10-use-captcha.md index 8213d8401..b12225afc 100644 --- a/docs/tutorial/03-client/09-use-captcha.md +++ b/docs/tutorial/03-client/10-use-captcha.md @@ -1,6 +1,6 @@ --- title: send captcha -sidebar_position: 90 +sidebar_position: 100 --- import Tabs from '@theme/Tabs'; @@ -22,9 +22,9 @@ The verification code sending hook saves you the trouble of developing the verif ## Features -- ✨ The countdown will start automatically after the verification code is sent; -- ✨ Custom countdown seconds; -- ✨ Verification code sending limit; +- The countdown will start automatically after the verification code is sent; +- Custom countdown seconds; +- Verification code sending limit; ## Usage diff --git a/docs/tutorial/03-client/10-use-serial-request.md b/docs/tutorial/03-client/11-use-serial-request.md similarity index 93% rename from docs/tutorial/03-client/10-use-serial-request.md rename to docs/tutorial/03-client/11-use-serial-request.md index 6950827b6..baedb257c 100644 --- a/docs/tutorial/03-client/10-use-serial-request.md +++ b/docs/tutorial/03-client/11-use-serial-request.md @@ -1,6 +1,6 @@ --- title: useRequest with serial -sidebar_position: 100 +sidebar_position: 110 --- import Tabs from '@theme/Tabs'; @@ -18,9 +18,9 @@ This use hook is more concise and easy to use than [serial request in best pract ## Features -- ✨ A more concise and easy-to-use serial method; -- ✨Unified request status and callback function; -- ✨ send function can trigger serial execution of multiple requests; +- A more concise and easy-to-use serial method; +- Unified request status and callback function; +- send function can trigger serial execution of multiple requests; ## Example diff --git a/docs/tutorial/03-client/11-use-serial-watcher.md b/docs/tutorial/03-client/12-use-serial-watcher.md similarity index 93% rename from docs/tutorial/03-client/11-use-serial-watcher.md rename to docs/tutorial/03-client/12-use-serial-watcher.md index 3141d55b0..267af9261 100644 --- a/docs/tutorial/03-client/11-use-serial-watcher.md +++ b/docs/tutorial/03-client/12-use-serial-watcher.md @@ -1,6 +1,6 @@ --- title: useWatcher with serial -sidebar_position: 110 +sidebar_position: 120 --- import Tabs from '@theme/Tabs'; @@ -18,9 +18,9 @@ Status update triggers a set of serial requests, which is more concise and easy ## Features -- ✨ A more concise and easy-to-use serial method; -- ✨Unified request status and callback function; -- ✨Status update triggers serial execution of multiple requests; +- A more concise and easy-to-use serial method; +- Unified request status and callback function; +- Status update triggers serial execution of multiple requests; ## Example diff --git a/docs/tutorial/03-client/12-use-retriable-request.md b/docs/tutorial/03-client/13-use-retriable-request.md similarity index 98% rename from docs/tutorial/03-client/12-use-retriable-request.md rename to docs/tutorial/03-client/13-use-retriable-request.md index a2b7fa10b..62e27e02f 100644 --- a/docs/tutorial/03-client/12-use-retriable-request.md +++ b/docs/tutorial/03-client/13-use-retriable-request.md @@ -1,6 +1,6 @@ --- title: retriable request -sidebar_position: 120 +sidebar_position: 130 --- import Tabs from '@theme/Tabs'; @@ -22,9 +22,9 @@ A use hook that can automatically retry a request failure, you can use it for im ## Features -- ✨Customize the number of retries or judge whether retry is required according to the conditions; -- ✨ Retry delay mechanism; -- ✨ Manually stop retrying; +- Customize the number of retries or judge whether retry is required according to the conditions; +- Retry delay mechanism; +- Manually stop retrying; ## Usage diff --git a/docs/tutorial/03-client/13-use-sse.md b/docs/tutorial/03-client/14-use-sse.md similarity index 98% rename from docs/tutorial/03-client/13-use-sse.md rename to docs/tutorial/03-client/14-use-sse.md index 12053d850..96259cfa8 100644 --- a/docs/tutorial/03-client/13-use-sse.md +++ b/docs/tutorial/03-client/14-use-sse.md @@ -1,6 +1,6 @@ --- title: request by server-send events -sidebar_position: 130 +sidebar_position: 140 --- import Tabs from '@theme/Tabs'; @@ -18,8 +18,8 @@ A use hook that can automatically retry a request failure, you can use it for im ## Features -- ✨ Simpler and easier-to-use usage. -- ✨ Automatic connection management. +- Simpler and easier-to-use usage. +- Automatic connection management. ## Usage diff --git a/docs/tutorial/03-client/14-use-breakpoint-uploader.md b/docs/tutorial/03-client/15-use-breakpoint-uploader.md similarity index 69% rename from docs/tutorial/03-client/14-use-breakpoint-uploader.md rename to docs/tutorial/03-client/15-use-breakpoint-uploader.md index 3bd88eda8..494bd2a56 100644 --- a/docs/tutorial/03-client/14-use-breakpoint-uploader.md +++ b/docs/tutorial/03-client/15-use-breakpoint-uploader.md @@ -1,6 +1,6 @@ --- title: Breakpoint upload -sidebar_position: 140 +sidebar_position: 150 --- coming soon... diff --git a/docs/tutorial/03-client/15-use-uploader.md b/docs/tutorial/03-client/16-use-uploader.md similarity index 72% rename from docs/tutorial/03-client/15-use-uploader.md rename to docs/tutorial/03-client/16-use-uploader.md index 3d646cd51..382005157 100644 --- a/docs/tutorial/03-client/15-use-uploader.md +++ b/docs/tutorial/03-client/16-use-uploader.md @@ -1,6 +1,6 @@ --- title: Universal upload strategy -sidebar_position: 150 +sidebar_position: 160 --- coming soon... diff --git a/docs/tutorial/03-client/16-rate-limit-middleware.md b/docs/tutorial/03-client/17-rate-limit-middleware.md similarity index 89% rename from docs/tutorial/03-client/16-rate-limit-middleware.md rename to docs/tutorial/03-client/17-rate-limit-middleware.md index dbe5bea83..7f6348b34 100644 --- a/docs/tutorial/03-client/16-rate-limit-middleware.md +++ b/docs/tutorial/03-client/17-rate-limit-middleware.md @@ -1,6 +1,6 @@ --- title: Request rate limit -sidebar_position: 160 +sidebar_position: 170 --- Set the number of requests that should be executed immediately for each interval, and other requests will be automatically delayed. diff --git a/docs/tutorial/04-server/01-retry.md b/docs/tutorial/04-server/01-retry.md index 221edfbd9..4aa9d8528 100644 --- a/docs/tutorial/04-server/01-retry.md +++ b/docs/tutorial/04-server/01-retry.md @@ -13,9 +13,9 @@ Request retry strategy, you can use it for important requests. ## Features -- ✨ Customize the number of retries or determine whether to retry based on conditions; +- Customize the number of retries or determine whether to retry based on conditions; -- ✨ Retry delay mechanism; +- Retry delay mechanism; ## Usage diff --git a/docs/tutorial/05-cache/01-mode.md b/docs/tutorial/05-cache/01-mode.md new file mode 100644 index 000000000..ba3197dca --- /dev/null +++ b/docs/tutorial/05-cache/01-mode.md @@ -0,0 +1,331 @@ +--- +title: Cache Mode +sidebar_position: 10 +--- + +import MemoryCache from '@site/example-links/MemoryCache'; +import StoragePlaceholder from '@site/example-links/StoragePlaceholder'; +import StorageRestore from '@site/example-links/StorageRestore'; + +Cache mode can be set at different granularities, such as global or request level. When set globally, all method instances created by the same alova instance will inherit the setting. + +:::info Note + +Whether to use cache mode and which cache mode to use depends on the scenario. The following will mention their usage scenarios when introducing different cache modes separately. + +::: + +## Memory Mode (Default) + +Memory mode belongs to the single-level cache (L1 cache) mode. By default, the cache is placed in memory. It is the most commonly used cache mode. + +```mermaid +flowchart LR +A[User request] --> B{Check L1 cache} +B -->|Hit| C[Return data] +B -->|Miss| F[Request API interface] +F --> G[Update L1 cache] +G --> C +C --> H[End] + +style F stroke-width:8px +``` + +By default, GET request has a memory cache time of 300000ms (5 minutes). Developers can also customize the cache setting. Please continue reading. + +### Client + +In the client, refreshing the page cache will invalidate it. The memory mode is generally used to solve the performance consumption caused by frequent requests for the same data in a short period of time (several minutes or seconds). For example, when you are writing a todo detail page, you may think that users will frequently click to view details in the todo list. If the user repeatedly views a certain detail, the interface will no longer be requested repeatedly, and the data can be returned immediately, which improves the response speed and reduces the server pressure. At this time, we can set the response data cache for a todo detail method instance. + +```javascript +alovaInstance.GET('/todo/list', { + // ... + // highlight-start + cacheFor: { + // Set the cache mode to memory mode + mode: 'memory', + + // Unit is milliseconds + // When set to `Infinity`, it means that the data will never expire. When set to 0 or a negative number, it means that it will not be cached + expire: 60 * 10 * 1000 + } + // highlight-end +}); +``` + +Memory mode is the default mode. You can shorten it like this + +```javascript +alovaInstance.GET('/todo/list', { + // ... + // highlight-start + cacheFor: 60 * 10 * 1000 + // highlight-end +}); +``` + +### Server + +In the service, it is usually used in scenarios where the interface requires high access frequency and low latency to reduce the pressure on the downstream server. The usage method is the same as the client. + +Note when using it on the server: + +1. Too much cache will always consume server memory. You can use [lru-cache](https://www.npmjs.com/package/lru-cache) to control memory consumption. + +2. If you need to share cache in a single-machine nodejs cluster environment, you can use [@alova/psc](https://www.npmjs.com/package/@alova/psc) to create a cache solution for shared memory between processes. + +```js +const { createPSCAdapter, NodeSyncAdapter } = require('@alova/psc'); +const { LRUCache } = require('lru-cache'); + +function lRUCache(options = {}) { + const cache = new LRUCache(options); + return { + set(key, value) { + return cache.set(key, value); + }, + + get(key) { + return cache.get(key); + }, + + remove(key) { + return cache.delete(key); + }, + + clear() { + return cache.clear(); + } + }; +} + +const alovaInstance = createAlova({ + baseURL: 'https://api.alovajs.dev', + + // Inter-process shared cache adapter + l1Cache: createPSCAdapter( + NodeSyncAdapter(), + lRUCache({ + max: 1000, + ttl: 1000 * 60 * 10 + }) + ) +}); +``` + +Memory cache mode corresponds to l1 cache. Here we replace the cache adapter with lru-cache shared between processes. You can also [customize the storage adapter](/tutorial/custom/custom-storage-adapter). For example, when you only need a single-level cache, you can also directly set the l1 cache to the redis adapter. + +## Restore mode + +Restore mode corresponds to multi-level cache, namely L1 and L2 cache. After the restore mode is turned on, the response data will be stored in both L1 and L2 caches. When the L1 cache fails, the data will be read from the L2 cache and then the L1 cache will be updated. The interface will be requested again when the L2 cache also fails. + +```mermaid +flowchart LR +A[User request] --> B{Check L1 cache} +B -->|Hit| C[Return data] +B -->|Miss| D{Check L2 cache} +D -->|Hit| E[Update L1 cache] +E --> C +D -->|Miss| F[Request API interface] +F --> G[Update L2 cache] +G --> E +C --> H[End] + +style F stroke-width:8px +``` + +### Client + +In the client, when the cache has not expired, it will not be invalidated even if the page cache is refreshed. It is generally used for some data that requires server-side management but is basically unchanged, such as the specific dates of holidays each year are different, but will not change again. In this scenario, we only need to set the cache expiration time to the last minute of this year. + +When using alova in the client, `localStorage` is used as the L2 storage adapter by default. You can also [customize the storage adapter](/tutorial/custom/custom-storage-adapter). + +Set on the method instance: + +```javascript +const todoListGetter = alovaInstance.Get('/todo/list', { + // ... + // highlight-start + cacheFor: { + // Set cache mode to persistence mode + mode: 'restore', + // Cache time + expire: 60 * 10 * 1000 + } + // highlight-end +}); +``` + +#### What to do if data changes? + +When the method instance is set to restore mode, it may be necessary to allow users to re-cache the changed data after publishing the application due to changes in interface data or changes in the logic of front-end processing response data. At this time, you can set the cache tag through the `tag` attribute. Each piece of persistent data contains a `tag` identifier. When the `tag` changes, the original persistent data will become invalid, and new data will be obtained again and identified with a new `tag`. + +```javascript +const todoListGetter = alovaInstance.Get('/todo/list', { + // ... + cacheFor: { + mode: 'restore', + expire: 60 * 10 * 1000, + + // highlight-start + // Add or modify tag parameters, cached data will become invalid + // It is recommended to use version number management + tag: 'v1' + // highlight-end + } +}); +``` + +### Server + +Generally used for multi-level cache, using L1 as memory cache and L2 as persistent cache, such as redis and memcached. + +Some application scenarios are as follows: + +1. High access frequency and low latency requirements: such as hot news and product details, which can further reduce network overhead and maintain faster response when the network is unstable. + +2. Reduce the pressure on downstream servers, such as services with peak access periods, and the upper-level cache can effectively reduce the pressure on the backend database and microservices. +3. Integrate data merging and processing of multiple downstream servers. Multiple serial requests may lead to longer response time, and may also consume performance due to complex data conversion. The converted data can be cached. +4. API rate limit and billing, weather forecast service API updates weather information once an hour, geographic location data API, etc. + +When using alova on the server, there is no L2 storage adapter by default. The implementation of file storage adapter and redis adapter are provided in [Server-side L2 storage practice](/tutorial/best-practice/l2-storage). You can also [customize storage adapter](/tutorial/custom/custom-storage-adapter), for example, use MongoDB, MySQL and other databases as storage adapters for response data. + +:::warning Note + +When the request body is special data such as **FormData**, **Blob**, **ArrayBuffer**, **URLSearchParams**, **ReadableStream**, it will be considered that you have the intention to communicate with the server, and no caching will be performed in this case. + +::: + +## Set alova id + +Each cache key contains the namespace of the alova instance, in the following format: + +``` +cacheKey = [prefix][alova-id][method-json-string] +``` + +By default, [alova-id] increments in the order in which alova is created. When using persistent cache in a server environment, it is strongly recommended to set the namespace of the alova id fixed cache key. **This is particularly important**, otherwise the order in which multiple alova instances are created may change, resulting in the inability to match the corresponding cache. + +```js +const userAlova = createAlova({ + // ... + id: 'user' +}); + +const orderAlova = createAlova({ + // ... + id: 'order' +}); +``` + +## Set cache mode globally + +If you need to set the cache mode globally, you can do it as follows: + +```javascript +const alovaInstance = createAlova({ + // ... + // highlight-start + cacheFor: { + // Set the cache mode for POST uniformly + POST: { + mode: 'restore', + expire: 60 * 10 * 1000 + }, + // Set the cache mode for HEAD requests uniformly + HEAD: 60 * 10 * 1000 + } + // highlight-end +}); +``` + +After this, method instances created through the `alovaInstance` instance will use this cache setting by default, and you can also override it in the method instance. + +## Globally turn off cache mode + +If you don't want to use anyIf you want to use any request cache, you can turn it off globally. If you want to use it only for a few specific requests, you can also turn it off globally and set it in the specified method instance. + +```javascript +const alovaInstance = createAlova({ + // ... + // highlight-start + // Set to null to globally turn off all request caches + cacheFor: null + // highlight-end +}); +``` + +## Set different expiration times + +In restore mode, you can also set different expiration times for L1 and L2 caches. Set `expire` to a function, which will be called when setting L1 and L2 caches respectively. + +The following is an example of setting different expiration times for a single request. + +```js +alovaInst.Get('/user/profile', { + // ... + cacheFor: { + mode: 'restore', + expire: ({ method, mode }) => { + // Set 5 minutes cache in l1 cache and 1 day cache in l2 cache + return mode === 'memory' ? 5 * 60 : 24 * 60 * 60; + } + } +}); +``` + +The following is an example of setting different expiration times for GET requests globally, when the method metadata has the `setDiffExpire` flag. + +```js +const alovaInst = createAlova({ + // ... + cacheFor: { + GET: { + mode: 'restore', + expire: ({ method, mode }) => { + if (method.meta.setDiffExpire) { + // Set 5 minutes cache in l1 cache and 1 day cache in l2 cache + return mode === 'memory' ? 5 * 60 : 24 * 60 * 60; + } + return 5 * 60; + } + } + } +}); +``` + +## Expiration time type + +There are two types of expiration time to choose from, namely **relative time** and **absolute time** + +### Relative time + +That is, the expiration time starts when the cache data is saved, in **milliseconds**. The above examples are all of this type. + +```javascript +cacheFor: 60 * 10 * 1000; +``` + +```javascript +cacheFor: { + expire: 60 * 10 * 1000, +} +``` + +### Absolute time + +With a specific time point as the expiration time, the cache will expire at the set time point + +```javascript +cacheFor: new Date('2030-01-01'); +``` + +```javascript +cacheFor: { + expire: new Date('2030-01-01'); +} +``` + +## Automatic response maintenance instructions + +The key of the response data cache is a combination of the request method (method), request address (url), request header parameters (headers), url parameters (params), and request body parameters (requestBody) of the method instance as a unique identifier. Any information or location will be treated as a different key. If you want to customize the cache key, you can refer to [Custom method key](/tutorial/advanced/custom-method-key). diff --git a/docs/tutorial/05-cache/02-auto-invalidate.md b/docs/tutorial/05-cache/02-auto-invalidate.md new file mode 100644 index 000000000..2b9b65e2d --- /dev/null +++ b/docs/tutorial/05-cache/02-auto-invalidate.md @@ -0,0 +1,86 @@ +--- +title: Auto Invalidate +sidebar_position: 20 +--- + +Automatic invalidation cache is to set the matching rules of the invalidation source method in the target cache. When the source method is successfully requested, the target cache will be automatically matched and invalidated, and there is no need to manually clear the cache. When the target cache and the invalidation source are one-to-one or one-to-many, it is very convenient to set the automatic invalidation rule. + +```mermaid +flowchart + M1[method1 invalidation source pointing] --> T1[target cache] + M11[method1 invalidation source pointing] --> T2[target cache] + M2[method2 invalidation source pointing] --> T2[target cache] + MN[methodN invalidation source pointing] --> T2[target cache] +``` + +## Usage scenarios + +1. After editing a list item and submitting it successfully, the cache data of the list item is automatically invalidated. +2. On the server side, for example, the current cache needs to be invalidated after the user's personal information, configuration data, etc. are updated. + +## Set automatic invalidation rules + +It is very simple to set this rule. You can set the `hitSource` parameter for it when creating a Method instance with cache. + +### Set the invalidation source to the method instance + +Use a fixed method instance as the invalidation source. As long as the request of this method instance or its cloned instance succeeds, the target cache will be automatically cleared. + +```javascript +alova.Get('/todo/1', { + // ... + hitSource: alova.Post('/todo', {}) +}); +``` + +### Match the invalidation source by method name + +Like the method matcher, you can specify the method name in hitSource to match the invalidation source. Multiple invalidation sources can be set to the same name. When the method instance with this name is requested successfully, the target cache will be automatically cleared. + +```javascript +const methodSubmitTodo = data => + alova.Post('/todo', data, { + name: 'submitTodo' + }); + +alova.Get('/todo/1', { + // ... + // Match the invalidation source whose method instance name is submitTodo + hitSource: 'submitTodo' +}); +``` + +### Match invalidation source by method name regular expression + +If the method instance name is not fixed, you can specify a regular expression in hitSource to match the method name. When the matched method instance is requested successfully, the target cache will be automatically cleared. + +```javascript +const methodSubmitTodo = data => + alova.Post('/todo', data, { + name: 'prefix-submitTodo' + }); + +alova.Get('/todo/1', { + // ... + // Match all instances of method instance name starting with prefix + hitSource: /^prefix/ +}); +``` + +### Combining failure sources + +If you want to use multiple rules above to match failure sources, you can specify hitSource as an array, where the array item is any of the above 3 rules, and method instances that meet any of the rules in the array will be matched. + +```javascript +alova.Get('/todo/1', { + // ... + // When the method instance request that satisfies any matching rule in the array is successful, this cache will be invalidated + hitSource: [alova.Post('/todo', {}), 'submitTodo', /^prefix/] +}); +``` + +## hitSource data type + +```typescript +type hitSource = string | RegExp | Method | (string | RegExp | Method)[]; +``` diff --git a/docs/tutorial/05-cache/03-manually-invalidate.md b/docs/tutorial/05-cache/03-manually-invalidate.md new file mode 100644 index 000000000..a97a2ce0e --- /dev/null +++ b/docs/tutorial/05-cache/03-manually-invalidate.md @@ -0,0 +1,99 @@ +--- +title: Manual Invalidate +sidebar_position: 30 +--- + +Generally, automatic cache invalidation is more concise and is recommended to be used first to invalidate the cache. When automatic cache invalidation does not meet the needs, you can also invalidate the cache by calling `invalidateCache`. + +## Invalidate a single cache + +Pass a method instance to the `invalidateCache` function, and it will find the cache under this instance for invalidation. + +In the following example, when the submission is successful, the cache of this todo detail data will be invalidated. + +```javascript +// Get the todo detail data with id 1 +const getTodoDetail = id => + alovaInstance.Get(`/todo/${id}`, { + cacheFor: 600 * 1000 + }); +const { loading, data } = useRequest(getTodoDetail(1)); +``` + +```javascript +// Submit the data and invalidate the todo detail data with id 1. +const { + // ... + send, + onSuccess +} = useRequest(createTodoPoster, { immediate: false }); + +// highlight-start +// Invalidate cache after successful submission +onSuccess(() => { + invalidateCache(getTodoDetail(1)); +}); +// highlight-end + +const handleSubmit = () => { + send({ + title: 'new todo', + content: 'new todo content' + }); +}; +``` + +## Invalidate multiple caches + +You can also pass in an array of method instances to invalidate multiple caches. + +```javascript +invalidateCache([method1, method2, ...]); +``` + +## Dynamically invalidate cache + +Sometimes you may not be sure which cache data needs to be invalidated. We can use [method snapshot matcher](/tutorial/advanced/method-matcher) to dynamically find the corresponding method instance. The following example shows how to invalidate the cache of the first five instances of a method called todoList. + +```javascript +const getTodoList = currentPage => { + return alovaInstance.Get('/todo/list', { + // highlight-start + // Set the name for the method instance first, which is used to filter out the required Method instance when the Method instance cannot be directly specified + name: 'todoList', + // highlight-end + params: { + currentPage, + pageSize: 10 + } + }); +}; + +const { + // ... + send, + onSuccess +} = useRequest(createTodoPoster, { immediate: false }); +// After successful submission, the todo data cache of the first page is fixedly invalidated +onSuccess(() => { + // highlight-start + // Invalidate the cache of the first 5 Method instances named todoList + const matchedMethods = alovaInstance.snapshots.match({ + name: 'todoList', + filter: (method, index, ary) => { + return index < 5; + } + }); + invalidateCache(matchedMethods); + // highlight-end +}); +``` + +> For more usage of method matchers, see [method snapshot matcher](/tutorial/advanced/method-matcher) + +## Invalidate all caches + +```javascript +// When no parameters are passed, invalidate all response caches +invalidateCache(); +``` diff --git a/docs/tutorial/05-cache/04-force-request.md b/docs/tutorial/05-cache/04-force-request.md new file mode 100644 index 000000000..2b52a9b84 --- /dev/null +++ b/docs/tutorial/05-cache/04-force-request.md @@ -0,0 +1,18 @@ +--- +title: Forced Request +sidebar_position: 40 +--- + +Forced request is a mechanism that bypasses cache checks to trigger request sending. It is useful when you need to get the latest data under certain conditions. + +## Forced request in method + +Forced request by calling the send function of the method instance and passing true. + +```javascript +const response = await alovaInstance.Get('/api/user').send(true); +``` + +## Forced request in useHook + +Please go to [Automatically manage request status-Forced request](/tutorial/client/use-request) for details. diff --git a/docs/tutorial/05-cache/05-set-and-query.md b/docs/tutorial/05-cache/05-set-and-query.md new file mode 100644 index 000000000..ce8579804 --- /dev/null +++ b/docs/tutorial/05-cache/05-set-and-query.md @@ -0,0 +1,89 @@ +--- +title: Set & Query Cache +sidebar_position: 50 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +Cache also supports update and search. In [cache mode](/tutorial/cache/mode), we mentioned that each cache data is saved with the method instance that sent the request as the key, so when manually updating the cache, the method instance will also be used to search for the corresponding cache data. + +## Update cache + +### Update static values + +Use `setCache` to update cache data. Its first parameter is the method instance, the second is the new cache data, and returns a Promise instance to indicate whether the execution is completed. + +```js +import { setCache } from 'alova'; +await setCache(todoDetail(id), detailedData); +``` + +### Dynamic cache update + +You can also pass a callback function in `setCache` to dynamically calculate cache data and return the cache data to be updated. If `undefined` is returned in the function, the cache update will be aborted. + +```javascript +await setCache(todoDetail(id), oldCache => { + if (!oldCache.allowUpdate) { + return; // Return undefined to abort cache update + } + + // Return the data to be cached + return { + ...oldCache, + name: 'new name' + }; +}); +``` + +### Update strategy + +When the passed method sets multi-level cache, the L1 cache and L2 cache will be updated by default. You can control the update of the specified cache separately through `policy`. + +```js +await setCache(todoDetail(id), detailedData, { + /** + * Cache policy. + * - l1: Update only l1 cache. + * - l2: Update only l2 cache. + * - all: Update l1 cache and update l2 cache (method cache mode needs to be 'restore'). + * @default 'all' + */ + policy: 'l1' +}); +``` + +## Query cache + +Query cached data through the `queryCache` method, which receives a method instance. + +```javascript +import { queryCache } from 'alova'; + +const cachedData = await queryCache(getTodoListByDate('2022-10-01')); +``` + +You can also dynamically find method instances through [method snapshot matchers](/tutorial/advanced/method-matcher). + +```javascript +const lastMethod = alovaInstance.snapshots.match('todoList', true); +const cachedData = lastMethod ? await queryCache(lastMethod) : undefined; +``` + +### Query strategy + +When the incoming method is set with multi-level cache, the default is to query the L1 cache first, then the L2 cache. You can use `policy` to control only the specified cache. + +```js +const cachedData = await queryCache(getTodoListByDate('2022-10-01'), { + /** + * Cache strategy. + * - l1: query only the l1 cache. + * - l2: query only the l2 cache. + * - all: query the l1 cache and query the l2 cache (the method cache mode needs to be 'restore'). + * @default 'all' + */ + policy: 'l1' +}); +``` diff --git a/docs/tutorial/05-cache/06-controlled-cache.md b/docs/tutorial/05-cache/06-controlled-cache.md new file mode 100644 index 000000000..c75effe38 --- /dev/null +++ b/docs/tutorial/05-cache/06-controlled-cache.md @@ -0,0 +1,56 @@ +--- +title: Controlled Cache +sidebar_position: 60 +--- + +When sending a request, the default response cache will be matched first. In some cases, you may need to use `IndexedDB` as a cache management solution and use a custom `IndexedDB` adapter, but this will make all requests use it as a storage solution, while controlled cache allows you to control the custom cache from a single request. + +Using controlled cache is also very simple. You can set `cacheFor` in method to an asynchronous or synchronous function. The data returned in this function will be used as cache data. For example, read data in `IndexedDB`. + +```javascript +const getFile = fileName => + alovaInstance.GET(`/file/${fileName}`, { + // Controlled cache functions support asynchronous and synchronous functions + cacheFor() { + return new Promise((resolve, reject) => { + const tx = db.transaction(['files']); + const getRequest = tx.objectStore('files').get(fileName); + getRequest.onsuccess = resolve; + getRequest.onerror = reject; + }); + } + }); +``` + +You can also return `undefined` or no data in `cacheFor` and continue to send the request, which is useful in the case of cache misses when customizing cache management. + +## Use transformData to set cache + +Because the `transformData` function has the following two features: + +- It is triggered only when responding, and will not be triggered when hitting the response cache; + +- It supports asynchronous functions; + +Therefore, you can also use it to save custom caches. For example, in the scenario where files are used as response data for caching, you can use IndexedDB to cache file data. + +```javascript +const fileGetter = alovaInstance.Get('/file/file_name', { + // Use IndexedDB to cache files + async transformData(fileBlob) { + await new Promise((resolve, reject) => { + const tx = db.transaction(['files'], 'readwrite'); + const putRequest = tx.objectStore('files').put({ + file: fileBlob + }); + putRequest.onsuccess = resolve; + putRequest.onerror = reject; + }); + return fileBlob; + } +}); +``` + +## Notes + +When used in usehooks, throwing an error in the `cacheFor` function will trigger `onError`. When using the method instance to directly initiate a request, the promise will be rejected. diff --git a/docs/tutorial/05-cache/README.md b/docs/tutorial/05-cache/README.md new file mode 100644 index 000000000..5180903b6 --- /dev/null +++ b/docs/tutorial/05-cache/README.md @@ -0,0 +1,11 @@ +--- +title: Response Cache Details +--- + +Response caching allows us to reuse response data and avoid repeated requests, thereby reducing response time and saving server resources. Alova supports multi-level caching and two caching modes: **memory mode and restore mode**. Just choose the one that suits your needs. + +On the client side, response caching allows users to see data immediately without any delay. On the server side, response data can be cached for a short time to solve the hot key problem. + +In addition, using the cache operation API, you can also freely add, modify and delete caches, as well as customize cache matching rules. + +Next, let's start with the cache mode! diff --git a/docs/tutorial/05-cache/_category_.json b/docs/tutorial/05-cache/_category_.json new file mode 100644 index 000000000..f8845fd87 --- /dev/null +++ b/docs/tutorial/05-cache/_category_.json @@ -0,0 +1,3 @@ +{ + "label": "Cache Details" +} diff --git a/docs/tutorial/06-advanced/02-update-across-components.md b/docs/tutorial/06-advanced/01-update-across-components.md similarity index 98% rename from docs/tutorial/06-advanced/02-update-across-components.md rename to docs/tutorial/06-advanced/01-update-across-components.md index a8a814256..6d083b436 100644 --- a/docs/tutorial/06-advanced/02-update-across-components.md +++ b/docs/tutorial/06-advanced/01-update-across-components.md @@ -1,102 +1,108 @@ ---- -title: Update states across components -sidebar_position: 20 ---- - -There is a scenario where when the user clicks on an item in the todo list, enters the todo details page and edits it, at this time we hope that the todo list data on the previous page will also be updated without resetting the situation. For edited content, `useFetcher` is no longer applicable. - -At this time, you can use `updateState` to update the existing responsive state under any module/page. It can find and modify the responsive state in other modules. - -[Here is an example of `updateState`](/tutorial/example/update-state) - -## Use method instance to find response states - -When determining the method instance corresponding to the updated response state, you can pass in this method instance in `updateState`. It will find whether there is a corresponding response state under this instance and provide it to you for modification in the callback function. Finally Just return the modified data. - -```javascript -import { updateState } from 'alova'; - -//Todo item being edited -const editingTodo = { - id: 1, - title: 'todo1', - time: '09:00' -}; - -const { send, onSuccess } = useRequest(createTodoPoster, { immediate: false }); -onSuccess(() => { - // highlight-start - // Fixed modification of todo data on the first page - // updateState will return whether the update is successful - const updated = updateState(getTodoList(1), todoList => { - return todoList.map(item => { - if (item.id === editingTodo.id) { - return { - ...item, - ...editingTodo - }; - } - return item; - }); - }); - // highlight-end -}); -``` - -:::warning note - -1. When updating the state through `updateState`, if the cache (memory cache and persistent cache) is detected, the new data update cache will also be updated. -2. Only when a request has been initiated using useRequest or useWatcher, alova will manage the states returned by the hook. The reason is that the response states is generated and saved through a Method instance, but when no request is initiated, the url and URL in the Method instance are Parameters such as params, query, and headers are still uncertain. - -::: - -## Dynamically update response states - -Maybe sometimes you are not sure that you need to update the response states under the method, but you know how to find the cached data that needs to be invalidated. We can use [Method instance matcher](/tutorial/advanced/method-matcher) to dynamically Find the corresponding method instance. The following example shows adding a piece of data to the list corresponding to the method instance named todoList. - -```javascript -updateState('todoList', todoListRaw => { - todoListRaw.push({ - title: 'new todo', - time: '10:00' - }); - return todoListRaw; -}); -``` - -The [Method instance matcher](/tutorial/advanced/method-matcher) will be introduced in detail in subsequent chapters. - -## Listen for matching events - -When dynamically updating the response state, sometimes you may want to do some processing when a method instance is matched, or you may want to obtain the matching method instance. `updateState` can also pass in a third parameter to set a matching event to achieve these purposes. . - -```javascript -updateState( - 'todoList', - todoListRaw => { - // ... - }, - { - // Called when a method instance is matched, and the parameter is the matched method instance. - onMatch: method => { - // ... - } - } -); -``` - -:::warning ⚠️ Please make sure the component is not destroyed - -By default, `updateState` will look for the response state created by alova's useHooks when sending a request. However, to prevent memory overflow, the destruction of a component will also recycle all the states created internally, so please make sure you use `updateState` It is hoped that the container component corresponding to the updated response states has not been destroyed, otherwise the corresponding response states will not be found and the update will fail. - -This problem often occurs when updating states across pages. What we tend to overlook is that by default, the previous page has been destroyed when the page jumps. Therefore, if you want to update states across pages, here are two suggestions: - -1. Persist the page components to ensure that the updated states can still be found; -2. Use [setCache](/tutorial/cache/set-and-query) instead of `updateState`. The principle is that when the request for the previous page exists in the cache, update its cache to ensure that when the page is created again, the The request can hit the updated cache to achieve the same effect. - -::: - -## Notes - -1. In actual use, whether you use `useRequest` or `useWatcher` to send a request, you can call the `send` function to specify different parameters to send the request repeatedly. The response states returned by these use hooks will be used by multiple method instances. Reference, so you can choose any method instance to match the same response states value; -2. When dynamically searching and updating the response states, the method instance matcher finds multiple method instances, and the first instance will prevail; +--- +title: Update states across components +sidebar_position: 20 +--- + +:::info usage scope + +client useHook + +::: + +There is a scenario where when the user clicks on an item in the todo list, enters the todo details page and edits it, at this time we hope that the todo list data on the previous page will also be updated without resetting the situation. For edited content, `useFetcher` is no longer applicable. + +At this time, you can use `updateState` to update the existing responsive state under any module/page. It can find and modify the responsive state in other modules. + +[Here is an example of `updateState`](/tutorial/example/update-state) + +## Use method instance to find response states + +When determining the method instance corresponding to the updated response state, you can pass in this method instance in `updateState`. It will find whether there is a corresponding response state under this instance and provide it to you for modification in the callback function. Finally Just return the modified data. + +```javascript +import { updateState } from 'alova'; + +//Todo item being edited +const editingTodo = { + id: 1, + title: 'todo1', + time: '09:00' +}; + +const { send, onSuccess } = useRequest(createTodoPoster, { immediate: false }); +onSuccess(() => { + // highlight-start + // Fixed modification of todo data on the first page + // updateState will return whether the update is successful + const updated = updateState(getTodoList(1), todoList => { + return todoList.map(item => { + if (item.id === editingTodo.id) { + return { + ...item, + ...editingTodo + }; + } + return item; + }); + }); + // highlight-end +}); +``` + +:::warning note + +1. When updating the state through `updateState`, if the cache (memory cache and persistent cache) is detected, the new data update cache will also be updated. +2. Only when a request has been initiated using useRequest or useWatcher, alova will manage the states returned by the hook. The reason is that the response states is generated and saved through a Method instance, but when no request is initiated, the url and URL in the Method instance are Parameters such as params, query, and headers are still uncertain. + +::: + +## Dynamically update response states + +Maybe sometimes you are not sure that you need to update the response states under the method, but you know how to find the cached data that needs to be invalidated. We can use [Method instance matcher](/tutorial/advanced/method-matcher) to dynamically Find the corresponding method instance. The following example shows adding a piece of data to the list corresponding to the method instance named todoList. + +```javascript +updateState('todoList', todoListRaw => { + todoListRaw.push({ + title: 'new todo', + time: '10:00' + }); + return todoListRaw; +}); +``` + +The [Method instance matcher](/tutorial/advanced/method-matcher) will be introduced in detail in subsequent chapters. + +## Listen for matching events + +When dynamically updating the response state, sometimes you may want to do some processing when a method instance is matched, or you may want to obtain the matching method instance. `updateState` can also pass in a third parameter to set a matching event to achieve these purposes. . + +```javascript +updateState( + 'todoList', + todoListRaw => { + // ... + }, + { + // Called when a method instance is matched, and the parameter is the matched method instance. + onMatch: method => { + // ... + } + } +); +``` + +:::warning ⚠️ Please make sure the component is not destroyed + +By default, `updateState` will look for the response state created by alova's useHooks when sending a request. However, to prevent memory overflow, the destruction of a component will also recycle all the states created internally, so please make sure you use `updateState` It is hoped that the container component corresponding to the updated response states has not been destroyed, otherwise the corresponding response states will not be found and the update will fail. + +This problem often occurs when updating states across pages. What we tend to overlook is that by default, the previous page has been destroyed when the page jumps. Therefore, if you want to update states across pages, here are two suggestions: + +1. Persist the page components to ensure that the updated states can still be found; +2. Use [setCache](/tutorial/cache/set-and-query) instead of `updateState`. The principle is that when the request for the previous page exists in the cache, update its cache to ensure that when the page is created again, the The request can hit the updated cache to achieve the same effect. + +::: + +## Notes + +1. In actual use, whether you use `useRequest` or `useWatcher` to send a request, you can call the `send` function to specify different parameters to send the request repeatedly. The response states returned by these use hooks will be used by multiple method instances. Reference, so you can choose any method instance to match the same response states value; +2. When dynamically searching and updating the response states, the method instance matcher finds multiple method instances, and the first instance will prevail; diff --git a/docs/tutorial/06-advanced/02-method-matcher.md b/docs/tutorial/06-advanced/02-method-matcher.md new file mode 100644 index 000000000..ca275afa0 --- /dev/null +++ b/docs/tutorial/06-advanced/02-method-matcher.md @@ -0,0 +1,88 @@ +--- +title: Method Matcher +sidebar_position: 30 +--- + +:::info Usage scope + +Fully + +::: + +The method snapshot matcher is a method that dynamically searches for method instances in the requested method snapshot list. Each alova instance has an independent snapshot space. It is generally used when developers are not sure which method to use. The method snapshot matcher can be used to search according to certain rules. + +It is generally used with the following 5 functions that need to use method instances. + +1. [setCache](/tutorial/cache/set-and-query) +2. [queryCache](/tutorial/cache/set-and-query) +3. [invalidateCache](/tutorial/cache/manually-invalidate) +4. [updateState](/tutorial/advanced/update-across-components) +5. [useFetcher.fetch](/tutorial/client/use-fetcher) + +## Matching rules + +When a method instance is requested, it will be saved as a snapshot. The method snapshot matcher searches for the method snapshots based on the `name` property set by the method instance. Multiple matchers are allowed to set the same `name`. + +## Matching by name property + +Match by passing in the full instance name. By default, a matching array is returned. + +```javascript +// Each time getTodoList is called, a new method instance is generated, and their names are the same +const getTodoList = currentPage => + alova.Get('/todo/list', { + name: 'todoList' + // ... + }); + +// Match all Method instances with the name `todoList` +const matchedMethods = alova.snaptshots.match('todoList'); +``` + +## Match by regular expression + +By passing in a regular expression for matching, all method instance names that match the regular expression will be matched, and the result is also an array. + +```javascript +// Match all Method instances whose name starts with `todo` +const matchedMethods = alova.snaptshots.match(/^todo/); +``` + +## Filter matching results + +By specifying `filter`, you can further filter method instances that do not meet the conditions. The filter function is used in the same way as Array.prototype.filter. Return true to indicate a successful match and false to indicate a failure. See the type declaration above for details. + +Let's look at a few examples. + +**Invalidate the cache of the last method instance of a specific name** + +```javascript +const matchedMethods = alova.snaptshots.match({ + name: 'todoList', + filter: (method, index, methods) => index === methods.length - 1 +}); +``` + +## Match a single method instance + +You can also set the second function of the `match` function to `true` to return the first item of the matching result, and return `undefined` if no match is found. + +```js +const matchedSingleMethod = alova.snaptshots.match(/^todo/, true); +``` + +## Limit instance snapshots + +By default, 1000 method instance snapshots are saved, otherwise frequent requests may cause memory overflow. You can also adjust the limit as needed. + +```js +import { globalConfig } from 'alova'; + +const alovaInstance = createAlova({ + // ... + // Limit to 500 instance snapshots + snapshots: 500 +}); +``` + +When set to 0, instance snapshots will no longer be saved, and the method snapshot matcher will not be used. diff --git a/docs/tutorial/06-advanced/03-method-matcher.md b/docs/tutorial/06-advanced/03-method-matcher.md deleted file mode 100644 index 1666b3b22..000000000 --- a/docs/tutorial/06-advanced/03-method-matcher.md +++ /dev/null @@ -1,141 +0,0 @@ ---- -title: Method Matcher -sidebar_position: 30 ---- - -A method matcher is a method that dynamically finds a method instance in a list of requested method snapshots. It is generally used. When developers are not sure which method to use, they can use the method matcher to search according to certain rules. - -## Matching rules - -When a request is made using a method instance, it will be saved as a snapshot. The method matcher looks in these method snapshots based on the `name` attribute set by the method instance. Multiple matchers are allowed to set the same `name`. - -Method instance matching types are as follows: - -```typescript -type MethodFilter = - | string - | RegExp - | { - name: string | RegExp; - filter: (method: Method, index: number, methods: Method[]) => boolean; - - // Optional parameter, if the alova object is passed in, it will only match the Method instance created by this alova, otherwise it will match the Method instances of all alova instances. - alova?: Alova; - }; -``` - -The method instance matcher can be used in the following functions. - -- [setCache](/tutorial/cache/set-and-query) -- [queryCache](/tutorial/cache/set-and-query) -- [invalidateCache](/tutorial/cache/manually-invalidate) -- [updateState](/tutorial/advanced/update-across-components) -- [useFetcher.fetch](/tutorial/advanced/use-fetcher) - -## Match by name attribute - -Matching is performed by passing in the complete instance name, and its matching result is an array. - -```javascript -// Each time getTodoList is called, a new method instance will be generated, and their names are the same. -const getTodoList = currentPage => - alova.Get('/todo/list', { - // highlight-start - name: 'todoList' - // highlight-end - // ... - }); - -//The following means invalidating the cache of all Method instances with name 'todoList' -invalidateCache('todoList'); -``` - -## Match by regular expression - -By passing in a regular expression for matching, any method instance whose name matches the regular expression will be matched, and its result is also an array. - -```javascript -// The following means invalidating the cache of all Method instances whose names start with 'todo' -invalidateCache(/^todo/); -``` - -## Filter matching results - -Further filter method instances that do not meet the conditions by specifying `filter`. The filter function is used in the same way as Array.prototype.filter. Returning true indicates successful matching, and returning false indicates failure. See the type declaration above for details. - -Let's look at a few examples. - -**Invalidate the cache of the last method instance with a specific name** - -```javascript -invalidateCache({ - name: 'todoList', - filter: (method, index, methods) => index === methods.length - 1 -}); -``` - -**Set the cache of the last method instance of a specific name created by `alovaInst`** - -```javascript -setCache( - { - name: /^todo/, - filter: (method, index, methods) => index === methods.length - 1, - - // If the alova parameter is passed, only the Method instances created by this alova instance will be matched, otherwise it will be matched in all Method instances. - alova: alovaInst - }, - newCache -); -``` - -**Repulse the last requested data from the todo list** - -```javascript -const { fetch } = useFetcher(); -fetch({ - name: 'todoList', - filter: (method, index, methods) => index === methods.length - 1 -}); -``` - -> The alova parameter can further narrow the matching scope. - -## Differences in use in different functions - -### invalidateCache - -Apply the set of all matching Method instances, that is, invalidate the cache corresponding to all matching Method instances. - -### setCache - -All matching Method instance collections are applied. When static data is passed in, all Method instance caches are set to the same value. When the callback function is passed in, this function will be called cyclically and the return value will be used as cache data. - -### updateState - -The first matching Method instance is applied. - -### fetch - -The first matching Method instance is applied, i.e. the data will only be pulled once. - -## Limit instance snapshots - -`[v2.20.0+]` By default, 1000 method instance snapshots will be saved, otherwise memory overflow may occur in frequent request scenarios. You can also adjust the limit as needed. - -```js -import { globalConfig } from 'alova'; - -globalConfig({ - // Limit saving to 500 instance snapshots - limitSnapshots: 500 -}); -``` - -When set to 0, instance snapshots are no longer saved and the method matcher cannot be used. - -```js -globalConfig({ - limitSnapshots: 0 -}); -``` diff --git a/docs/tutorial/06-advanced/06-error-logger.md b/docs/tutorial/06-advanced/06-error-logger.md deleted file mode 100644 index 7e96a6998..000000000 --- a/docs/tutorial/06-advanced/06-error-logger.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -title: Error logger -sidebar_position: 60 ---- - -:::info version required - -v2.6.0+ - -::: - -For the convenience of debugging, when using use hooks to request or respond to an error, the error logger will be printed on the console by default. If you do not want to print an error message or customize the control to print an error message in some cases (such as a production environment), alova also Support for them is provided. - -## Disable error logger - -Log printing can be turned off by setting `errorLogger` to `false or null` when creating an alova instance. - -```javascript -const alovaInstance = createAlova({ - //... - errorLogger: false -}); -``` - -## Custom print error logger - -The error logger is printed by `console.error` by default. If `console.error` is not supported in your project environment, or if you want to collect error information, you can specify `errorLogger` as a function to customize error logger. - -```javascript -const alovaInstance = createAlova({ - //... - // error is the error instance, methodInstance is the method instance corresponding to the error - errorLogger(error, methodInstance) { - reportError(`${methodInstance.url}: ${error.message}`); - } -}); -``` diff --git a/docs/tutorial/05-best-practice/01-manage-apis.md b/docs/tutorial/07-best-practice/01-manage-apis.md similarity index 100% rename from docs/tutorial/05-best-practice/01-manage-apis.md rename to docs/tutorial/07-best-practice/01-manage-apis.md diff --git a/docs/tutorial/05-best-practice/02-skills.md b/docs/tutorial/07-best-practice/02-skills.md similarity index 100% rename from docs/tutorial/05-best-practice/02-skills.md rename to docs/tutorial/07-best-practice/02-skills.md diff --git a/docs/tutorial/05-best-practice/03-manage-cache-by-indexeddb.md b/docs/tutorial/07-best-practice/03-manage-cache-by-indexeddb.md similarity index 100% rename from docs/tutorial/05-best-practice/03-manage-cache-by-indexeddb.md rename to docs/tutorial/07-best-practice/03-manage-cache-by-indexeddb.md diff --git a/docs/tutorial/05-best-practice/04-multiple-servers.md b/docs/tutorial/07-best-practice/04-multiple-servers.md similarity index 100% rename from docs/tutorial/05-best-practice/04-multiple-servers.md rename to docs/tutorial/07-best-practice/04-multiple-servers.md diff --git a/docs/tutorial/05-best-practice/05-middleware.md b/docs/tutorial/07-best-practice/05-middleware.md similarity index 100% rename from docs/tutorial/05-best-practice/05-middleware.md rename to docs/tutorial/07-best-practice/05-middleware.md diff --git a/docs/tutorial/07-best-practice/06-parallel-request.md b/docs/tutorial/07-best-practice/06-parallel-request.md new file mode 100644 index 000000000..7aa5fe8d0 --- /dev/null +++ b/docs/tutorial/07-best-practice/06-parallel-request.md @@ -0,0 +1,68 @@ +--- +title: Parallel Request +sidebar_position: 60 +--- + +## Use method + +Since method is a PromiseLike instance, to send parallel requests through method, you only need to use `Promise.all` to wait. + +```javascript +const [todoList, todoCounter] = await Promise.all[(todoListGetter, todoCountGetter)]; +``` + +## Use useRequest + +For simple parallel requests, you only need to call multiple useRequests at the same time. + +```javascript +const { data: todoList } = useRequest(todoListGetter); +const { data: todoCounter } = useRequest(todoCountGetter); +``` + +However, such requests are only applicable to simple parallel requests. If you need to perform some operations after all parallel requests are completed, there are two ways to achieve it: + +### Method 1 + +Manually create a promise object and use `Promise.all` to complete the effect. + +```javascript +const { + data: todoList, + onSuccess: onListSuccess, + onError: onListError +} = useRequest(todoListGetter); +const { + data: todoCounter, + onSuccess: onCountSuccess, + onError: onCountError +} = useRequest(todoCountGetter); + +// Manually create a promise object +const listPromise = new Promise((resolve, reject) => { + onListSuccess(resolve); + onListError(reject); +}); +const countPromise = new Promise((resolve, reject) => { + onCountSuccess(resolve); + onCountError(reject); +}); +const [listEvent, countEvent] = await Promise.all([listPromise, countPromise]); +// Parallel requests completed, continue processing business... +``` + +### Method 2 + +Use the `send` function returned by the `useRequest` function. Calling `send` will return a usable promise object. + +```javascript +// First, let them not automatically send requests +const { send: sendList } = useRequest(todoListGetter, { immediate: false }); +const { send: sendCount } = useRequest(todoCountGetter, { immediate: false }); + +// Use the promise object returned by the send function +const parallelRequest = async () => { + const [listResponse, countResponse] = await Promise.all([sendList(), sendCount()]); + // Parallel requests completed, continue processing business... +}; +``` diff --git a/docs/tutorial/07-best-practice/07-serial-request.md b/docs/tutorial/07-best-practice/07-serial-request.md new file mode 100644 index 000000000..f01667727 --- /dev/null +++ b/docs/tutorial/07-best-practice/07-serial-request.md @@ -0,0 +1,54 @@ +--- +title: Serial Request +sidebar_position: 70 +--- + +## Use method + +Since method is a PromiseLike instance, you can use `await` to wait for the request to succeed. + +```javascript +const todoList = await todoListGetter; +const todoDetail = await todoDetailGetter(todoList[0].id); +``` + +## Use useRequest + +Serial requests also have two modes. + +### Method 1 + +Let the first request be automatically sent, and the second request is triggered in the `onSuccess` callback of the first request to complete the serial request. The serial request can be completed by writing as follows: + +```javascript +// +const { data: todoList, onSuccess } = useRequest(todoListGetter); +const { data: todoDetail, send: sendTodoDetail } = useRequest( + todoId => todoDetailGetter(todoId), + { immediate: false } +); + +// Get the list first, then get the details of the first todo +onSuccess(event => { + sendTodoDetail(event.todoList[0].id); +}); +``` + +### Method 2 + +Use the `send` function returned by the `useRequest` function. Calling `send` will return a usable promise object. + +```javascript +// First, let them not automatically send requests +const { send: sendList } = useRequest(todoListGetter, { immediate: false }); +const { send: sendTodoDetail } = useRequest(todoId => todoDetailGetter(todoId), { + immediate: false +}); + +// Use the promise object returned by the send function +const serialRequest = async () => { + const todoList = await sendList(); + const todoDetail = await sendTodoDetail(todoList[0].id); + // Serial request completed, continue processing business... +}; +``` diff --git a/docs/tutorial/07-best-practice/08-l2-storage.md b/docs/tutorial/07-best-practice/08-l2-storage.md new file mode 100644 index 000000000..e69de29bb diff --git a/docs/tutorial/05-best-practice/_category_.json b/docs/tutorial/07-best-practice/_category_.json similarity index 100% rename from docs/tutorial/05-best-practice/_category_.json rename to docs/tutorial/07-best-practice/_category_.json diff --git a/docs/tutorial/07-cache/01-mode.md b/docs/tutorial/07-cache/01-mode.md deleted file mode 100644 index 175ffc3fe..000000000 --- a/docs/tutorial/07-cache/01-mode.md +++ /dev/null @@ -1,215 +0,0 @@ ---- -title: Cache Mode -sidebar_position: 10 ---- - -import MemoryCache from '@site/example-links/MemoryCache'; -import StoragePlaceholder from '@site/example-links/StoragePlaceholder'; -import StorageRestore from '@site/example-links/StorageRestore'; - -The cache mode can be set at different granularities such as global or request level. When set globally, all Method instances created from the same alova instance will inherit the setting. - -:::info note - -Whether to use the cache mode and which cache mode to use depends on the scenario. The usage scenarios of different cache modes will be mentioned below when introducing different cache modes separately. - -::: - -## Memory mode (default) - -The memory mode puts the cache in the memory, which means that the page cache is invalidated when it is refreshed, and is the most commonly used cache mode. - -Memory mode is generally used to solve the performance consumption caused by frequent requests for the same data in a short period of time (minutes or seconds). For example, when you are writing a todo details page, you may think that users will frequently click on the todo list Check the details, if the user does not repeatedly request the interface when repeatedly viewing a certain detail, and can return the data immediately, the colleague who improves the response speed also reduces the pressure on the server. At this point we can set the response data cache for a todo detail `Method` instance. - -```javascript -alovaInstance.GET('/todo/list', { - //... - // highlight-start - localCache: { - // Set cache mode to memory mode - mode: 'memory', - - // unit is milliseconds - // When set to `Infinity`, it means that the data will never expire, and when it is set to 0 or a negative number, it means no caching - expire: 60 * 10 * 1000 - } - // highlight-end -}); -``` - -Memory mode is the default mode, you can abbreviate like this - -```javascript -alovaInstance.GET('/todo/list', { - //... - // highlight-start - localCache: 60 * 10 * 1000 - // highlight-end -}); -``` - -> GET requests will set the memory cache time of 300000ms (5 minutes) by default, and developers can also customize the settings. - -> If you need to set the caching mode globally, see [Global setting cache mode] at the bottom of this section (#Global setting cache mode) - -### Memory mode example - - - -## Cache placeholder mode - -This cache mode is used when you don't want to display the Loading icon every time the application is entered, but you want to use the old data instead, you can use the cache occupancy mode, which has a better experience than Loading. - -In the cache occupancy mode, `data` will be immediately assigned the old data of the last cache. You can judge that if there is old data, use it to replace the Loading display. At the same time, it will send a request to obtain the latest data and update the cache, so as to achieve In order to quickly display the actual data, and obtain the latest data. - -Set on `Method` instances: - -```javascript -const todoListGetter = alovaInstance.Get('/todo/list', { - //... - // highlight-start - localCache: { - // Set the cache mode to persistent placeholder mode - mode: 'placeholder', - // cache time - expire: 60 * 10 * 1000 - } - // highlight-end -}); -``` - -> If you need to set the caching mode globally, see [Global setting cache mode] at the bottom of this section (#Global setting cache mode) - -### Cache placeholder mode example - - - -## Restore mode - -In this mode, the server-side cached data will be persistent. If the expiration time is not reached, even if the page cache is refreshed, it will not be invalidated. It is generally used for some data that requires server-side management but is basically unchanged, such as the specific dates of annual holidays. It is different, but it will not change again. In this scenario, we only need to set the cache expiration time to the last moment of this year. - -Set on `Method` instances: - -```javascript -const todoListGetter = alovaInstance.Get('/todo/list', { - //... - // highlight-start - localCache: { - // Set the cache mode to persistent mode - mode: 'restore', - // cache time - expire: 60 * 10 * 1000 - } - // highlight-end -}); -``` - -:::warning Caution - -When request body is special data such as **FormData**, **Blob**, **ArrayBuffer**, **URLSearchParams**, **ReadableStream**, it will be considered that you intend to communicate with server. In this case would not cache data. - -::: - -> If you need to set the caching mode globally, see [Global setting cache mode] at the bottom of this section (#Global setting cache mode) - -### Restore mode example - - - -### What should I do if the data changes in restore mode? - -When the `Method` instance in restore mode is set, it may be due to the change of the interface data or the logic change of the front-end processing response data. At this time, it is necessary to let the user re-cache the changed data after publishing the application. At this time, you can use `tag` The attribute sets the cache tag. Each piece of persistent data contains a `tag` identifier. When the `tag` changes, the original persistent data will become invalid, and new data will be obtained again, and the new `tag` will be used for identification . - -```javascript -const todoListGetter = alovaInstance.Get('/todo/list', { - //... - localCache: { - mode: 'restore', - expire: 60 * 10 * 1000, - - // highlight-start - // Add or modify the tag parameter, the cached data will be invalid - // It is recommended to use version number management - tag: 'v1' - // highlight-end - } -}); -``` - -## Global setting cache mode - -:::info version required -v1.3.0+ -::: -The above settings are all set separately on `Method`. If you need to set the cache mode globally, you can do it as follows: - -```javascript -const alovaInstance = createAlova({ - //... - // highlight-start - localCache: { - // Uniformly set the cache mode of POST - POST: { - mode: 'placeholder', - expire: 60 * 10 * 1000 - }, - // Uniformly set the cache mode of the HEAD request - HEAD: 60 * 10 * 1000 - } - // highlight-end -}); -``` - -Henceforth, the `Method` instance created by `alovaInstance` instance will use this cache setting by default, and it can also be overridden in the `Method` instance. - -> Note: When the cache mode is set globally, the original 5-minute GET cache mode will be overwritten. - -## Disable caching mode globally - -If you don't want to use any request cache in your project, you can turn it off globally. If you want to use it only in a few specific requests, you can also turn it off globally and set it in the specified `Method` instance . - -```javascript -const alovaInstance = createAlova({ - //... - // highlight-start - // Set to null to disable all request caching globally - localCache: null - // highlight-end -}); -``` - -## Expiration time type - -There are two types of expiration time to choose from, namely **relative time** and **absolute time** - -### Relative Time - -That is, the time to expire when the cached data is saved, in **milliseconds**, the above examples are all of this type. - -```javascript -localCache: 60 * 10 * 1000; -``` - -```javascript -localCache: { -expire: 60 * 10 * 1000, -} -``` - -### absolute time - -With a specific time point as the expiration time, the cache will expire at the set time point - -```javascript -localCache: new Date('2030-01-01'); -``` - -```javascript -localCache: { - expire: new Date('2030-01-01'); -} -``` - -## Instruction for response automatic maintenance - -The key of the response data cache is uniquely identified by the combination of the request method (method), request address (url), request header parameters (headers), url parameters (params), and request body parameters (requestBody) of the method instance. Any information or Different positions will be treated as different keys. diff --git a/docs/tutorial/07-cache/02-auto-invalidate.md b/docs/tutorial/07-cache/02-auto-invalidate.md deleted file mode 100644 index 9bbdfe62c..000000000 --- a/docs/tutorial/07-cache/02-auto-invalidate.md +++ /dev/null @@ -1,91 +0,0 @@ ---- -title: Auto Invalidate -sidebar_position: 20 ---- - -There is a scenario where when the user clicks on an item in the todo list, enters the todo details page and edits it, at this time we hope that the todo list data on the previous page will also be updated with the edited content. Usually The approach is to trigger content updates on the previous page through events, which increases maintenance costs. And `alova` provides 3 ways to achieve this goal very elegantly: - -1. Use `useFetcher` to immediately re-request the latest data, which will be explained in the [Data Fetching](/tutorial/advanced/use-fetcher) chapter; -2. Update the cache. This method will be explained in detail in the [Cache set and query](/tutorial/cache/set-and-query) chapter later; -3. Invalidate the response cache. When requested again, the data will be requested again due to cache invalidation. This is also what this chapter will explain. - -Automatic cache invalidation is to set invalidation source rules in the target cache. As long as the rules match, the target cache can be automatically invalidated. This saves the trouble of manually clearing the cache in many cases. - -## scenes to be used - -Setting up automatic invalidation rules is convenient when the target cache is one-to-one or one-to-many with the invalidation source. - -```mermaid -flowchart - M1[method1 invalidation source points to] --> T1[target cache] - M11[method1 invalidation source points to] --> T2[target cache] - M2[method2 invalidation source points to] --> T2[target cache] - MN[methodN invalidation source points to] --> T2[target cache] -``` - -## Set automatic invalidation rules - -Setting this rule is very simple, you can set the `hitSource` parameter for it when creating a Method instance with caching. - -### Invalidation source is set to method instance - -With a fixed method instance as the invalidation source, as long as the method instance or its clone instance succeeds, the target cache will be cleared automatically. - -```javascript -alova.Get('/todo/1', { - //... - hitSource: alova.Post('/todo', {}) -}); -``` - -### Match invalidating source by method name - -Like the method instance matcher, you can specify the name of the method in hitSource to match the failure source. Multiple failure sources can be set to the same name. When the method instance request with this name succeeds, the target cache will be automatically cleared. - -```javascript -const methodSubmitTodo = data => - alova.Post('/todo', data, { - name: 'submitTodo' - }); - -alova.Get('/todo/1', { - //... - // Match the failure source whose method instance name is submitTodo - hitSource: 'submitTodo' -}); -``` - -### Match invalidating source by regexp of method name - -If the method instance name is not fixed, you can specify a regular expression in hitSource to match the method name, and the target cache will be automatically cleared when the matched method instance succeeds in the request. - -```javascript -const methodSubmitTodo = data => - alova.Post('/todo', data, { - name: 'prefix-submitTodo' - }); - -alova.Get('/todo/1', { - //... - // Match all instances whose method instance name starts with prefix - hitSource: /^prefix/ -}); -``` - -### Combination setting validating source - -If you want to use the above multiple rules to match failure sources, you can specify hitSource as an array, and the array item is any one of the above three rules, and the method instance that meets any one of the rules in the array will be matched. - -```javascript -alova.Get('/todo/1', { - //... - // When the method instance request that satisfies any matching rule in the array succeeds, the cache will be invalidated - hitSource: [alova.Post('/todo', {}), 'submitTodo', /^prefix/] -}); -``` - -## hitSource data type - -```typescript -type hitSource = string | RegExp | Method | (string | RegExp | Method)[]; -``` diff --git a/docs/tutorial/07-cache/03-manually-invalidate.md b/docs/tutorial/07-cache/03-manually-invalidate.md deleted file mode 100644 index bd8dd546b..000000000 --- a/docs/tutorial/07-cache/03-manually-invalidate.md +++ /dev/null @@ -1,102 +0,0 @@ ---- -title: Manually Invalidate -sidebar_position: 30 ---- - -Generally, automatic invalidate cache is more concise, and it is recommended to use it first to invalidate the cache. When the automatic invalidate cache does not meet the needs, you can also invalidate the cache by calling `invalidateCache`. - -## Use method instance invalidate cache - -Pass in a method instance in the `invalidateCache` function, it will look for the cache under this instance to invalidate it. - -In the following example, when the submission is successful, the todo details data cache will be invalidated. - -```javascript -// Get the todo details data with id 1 -const getTodoDetail = id => - alovaInstance.Get(`/todo/${id}`, { - localCache: 1000000 - }); -const { loading, data } = useRequest(getTodoDetail(1)); -``` - -```javascript -// Submit the data and invalidate the todo details with id 1. -const { - // ... - send, - onSuccess -} = useRequest(createTodoPoster, { immediate: false }); - -// highlight-start -//Invalid cache after successful submission -onSuccess(() => { - invalidateCache(getTodoDetail(1)); -}); -// highlight-end - -const handleSubmit = () => { - send({ - title: 'new todo', - content: 'new todo content' - }); -}; -``` - -## Batch invalidate cache - -In the following example, we invalidate caches in batches by specifying the name of the cache or a regular expression of the name. - -```javascript -//The cache of the method named todoList will be invalidated -invalidateCache('todoList'); - -// The cache of methods whose names match the following regular expression will be invalidated -invalidateCache(/^todoList/); -``` - -## Dynamic invalidate cache - -Maybe sometimes you are not sure which cached data needs to be invalidated. We can use [Method instance matcher](/tutorial/advanced/method-matcher) to dynamically find the corresponding method instance. The following example shows how to invalidate the cache of the first 5 method instances named todoList. - -```javascript -const getTodoList = currentPage => { - return alovaInstance.Get('/todo/list', { - // highlight-start - // First set the name for the method instance, which is used to filter out the required Method instance when the Method instance cannot be specified directly. - name: 'todoList', - // highlight-end - params: { - currentPage, - pageSize: 10 - } - }); -}; - -const { - // ... - send, - onSuccess -} = useRequest(createTodoPoster, { immediate: false }); -// After successful submission, the todo data cache of the first page will be invalidated. -onSuccess(() => { - // highlight-start - //The cache of the first 5 Method instances whose invalid name is todoList - invalidateCache({ - name: 'todoList', - filter: (method, index, ary) => { - return index < 5; - } - }); - // highlight-end -}); -``` - -> For more methods of using method instance matcher, see [Method instance matcher](/tutorial/advanced/method-matcher) - -## Invalidate all caches - -```javascript -// When no parameters are passed, all response caches are invalidated -invalidateCache(); -``` diff --git a/docs/tutorial/07-cache/04-force-request.md b/docs/tutorial/07-cache/04-force-request.md deleted file mode 100644 index 3337a47a8..000000000 --- a/docs/tutorial/07-cache/04-force-request.md +++ /dev/null @@ -1,75 +0,0 @@ ---- -title: Force Request -sidebar_position: 40 ---- - -Forced request refers to a mechanism that bypasses the cache check and triggers a request. It is useful when the latest data needs to be obtained under certain conditions. - -## Force request with method - -Force request by calling the send function of the method instance, and passing `true`. - -```javascript -const response = await alovaInstance.Get('/api/user').send(true); -``` - -## Force request in useHook - -Among the three core hooks of `useRequest/useWatcher/useFetcher`, force request parameters are supported. - -```javascript -// useRequest -useRequest(todoListGetter, { - // highlight-start - force: true - // highlight-end -}); - -// useWatcher -useWatcher(todoListGetter, [page], { - // highlight-start - force: true - // highlight-end -}); - -// useFetcher -useFetcher({ - // highlight-start - force: true - // highlight-end -}); -``` - -### Dynamically set force value - -In actual situations, we often need to set whether to force the request to be sent based on different situations. In this case, force can be set to a function, which will also receive parameters passed in from the `send` function. Please read the [receive params](/tutorial/combine-framework/receive-params) for details - -```javascript -useRequest(todoListGetter, { - // highlight-start - force: isForce => { - return isForce; - } - // highlight-end -}); - -// useWatcher -useWatcher(todoListGetter, [page], { - // highlight-start - force: isForce => { - return isForce; - } - // highlight-end -}); - -// useFetcher -useFetcher({ - // highlight-start - force: isForce => { - return isForce; - } - // highlight-end -}); -``` - -`useFetcher` is a useHook for data fetching, which will be discussed in the [Data Fetching](/tutorial/advanced/use-fetcher) chapter later. diff --git a/docs/tutorial/07-cache/05-set-and-query.md b/docs/tutorial/07-cache/05-set-and-query.md deleted file mode 100644 index c11798832..000000000 --- a/docs/tutorial/07-cache/05-set-and-query.md +++ /dev/null @@ -1,284 +0,0 @@ ---- -title: Set & Query Cache -sidebar_position: 50 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -The cache also supports updating and querying, As we mentioned in [cache mode](/tutorial/cache/mode), each cached data is saved with the method instance that sends the request as the key, so the method instance will also be used when updating the cache manually to find the corresponding cached data. - -## Update static cache data - - - - -```html - - -``` - - - - -```jsx -import { setCache } from 'alova'; -import { useState } from 'react'; - -const getTodoListByDate = dateList => - alovaInstance.Get('/todo/list/dates', { - params: { dateList } - }); - -const App = () => { - // Get 5 days of data in batches during initialization - const [dates, setDates] = useState(['2022-05-01', '2022-05-02', '2022-05-03', '2022-05-04', '2022-05-05']); - const { - //... - onSuccess - } = useWatcher(() => getTodoListByDate(dates.join()), [dates], { - immediate: true - }); - onSuccess(todoListDates => { - if (todoListDates.length <= 1) { - return; - } - - // highlight-start - // By default, the data of these 5 days will be cached together in a key - // In order to make subsequent requests for data of a certain day also hit the cache, we can disassemble the data of 5 days into days, and manually set the response cache one by one through setCache - // The first parameter of setCache is the method instance object, which is used to specify the key of the cache - // The second parameter is the cached data - todoListDates.forEach(todoDate => { - setCache(getTodoListByDate(todoDate.date), [todoDate]); - }); - // highlight-end - }); - - // highlight-start - const handleTodolistToggle = () => { - // At this time, when the switching date is May 1, it will hit the response cache we manually set. - // The dates value is being monitored by useWatcher, so changing it can automatically trigger the request - setDates(['2022-05-01']); - }; - // highlight-end - - return ; -}; -``` - - - - -```html - - -``` - - - - -```html - - -``` - - - - -## Dynamically set cache data - -You can also pass in a callback function in `setCache` to dynamically calculate the cache data and return the cache data that needs to be updated. - -```javascript -setCache(getTodoListByDate('2022-10-01'), oldCache => { - // Return the data that needs to be cached - return { - ...oldCache, - expire: isAfter('2022-10-01', new Date()) - }; -}); -``` - -Similarly, you can also dynamically find method instances through [method instance matcher](/tutorial/advanced/method-matcher). - -```javascript -setCache( - { - name: 'todoList', - filter: (method, index, ary) => { - return index < 5; - } - }, - 'newCache' -); -``` - -## Abort to set cache - -Sometimes you need to dynamically determine whether to update the cache. If no data is returned in the callback function of `setCache`, or `undefined` is returned, the original cache data will not be updated at this time - -```javascript -setCache(getTodoListByDate('2022-10-01'), oldCache => { - const isExpired = isAfter('2022-10-01', new Date()); - if (!isExpired) { - return; // abort cache updating when return the undefined - } - return null; // update the cache to null -}); -``` - -## Cache query - -At the same time, we also provide a cache query method. - -```javascript -import { queryCache } from 'alova'; - -const cacheData = queryCache(getTodoListByDate('2022-10-01')); -``` - -You can also dynamically find method instances via [method instance matcher](/tutorial/advanced/method-matcher). - -```javascript -const cacheData = queryCache({ - name: 'todoList', - filter: (method, index, ary) => { - return index < 5; - } -}); -``` diff --git a/docs/tutorial/07-cache/06-controlled-cache.md b/docs/tutorial/07-cache/06-controlled-cache.md deleted file mode 100644 index 89dec2a39..000000000 --- a/docs/tutorial/07-cache/06-controlled-cache.md +++ /dev/null @@ -1,83 +0,0 @@ ---- -title: Controlled Cache -sidebar_position: 60 ---- - -:::info version requirements - -v2.1.0+ - -::: - -When sending a request, by default it will first check whether there is matching cache data. If it matches, it will use it as the response data to return. If in some scenarios, the user needs to use a custom cache, they must first use `setCache` to synchronize Setting up cached data can only work, which undoubtedly increases the burden on users. This is an uncontrolled cache. - -If you want to use **IndexedDB** to custom manage cached data under uncontrolled caching, you may first pre-set the hit cache for the upcoming request, like this: - -```javascript -const getFile = fileName => { - const fileGetter = alovaInstance.GET(`/file/${fileName}`); - const tx = db.transaction(['files']); - const getRequest = tx.objectStore('files').get(fileName); - getRequest.onsuccess = ({ result }) => { - setCache(fileGetter, result); - }; - return fileGetter; -}; -``` - -**❌ However, the above writing method is not recommended** for the following reasons: - -1. Each time `getFile` is called, a cache will be set, but fileGetter is not necessarily used to send requests; -2. IndexedDB is an asynchronous interface. If the cache matching step occurs before IndexedDB triggers onsuccess, then the cached data will not be matched, and their order is unpredictable; -3. Custom cache management tasks and methods are separated, but in fact they should be brought together; - -In this case, you can use controlled caching to solve the above problem. Using controlled caching is also very simple. You can set the localCache in the method to an asynchronous or synchronous function, and return custom data as a hit response. - -```javascript -const getFile = fileName => - alovaInstance.GET(`/file/${fileName}`, { - // Controlled cache functions support asynchronous and synchronous functions - localCache() { - return new Promise((resolve, reject) => { - const tx = db.transaction(['files']); - const getRequest = tx.objectStore('files').get(fileName); - getRequest.onsuccess = resolve; - getRequest.onerror = reject; - }); - } - }); -``` - -## Do not use caching - -If you wish to continue sending the request, you can return `undefined` or `void` in `localCache`, which is useful in the case of cache misses when customizing the cache. - -## Use transformData to set up cache - -Because the `transformData` function has the following two properties: - -- It is only triggered when responding, but will not be triggered when the response cache is hit; -- Support asynchronous functions; - -Therefore, you can also use it to save customized caches. For example, in a cache scenario where files are used as response data, you can use IndexedDB to cache file data. - -```javascript -const fileGetter = alovaInstance.Get('/file/file_name', { - // Use IndexedDB to cache files - async transformData(fileBlob) { - await new Promise((resolve, reject) => { - const tx = db.transaction(['files'], 'readwrite'); - const putRequest = tx.objectStore('files').put({ - file: fileBlob - }); - putRequest.onsuccess = resolve; - putRequest.onerror = reject; - }); - return fileBlob; - } -}); -``` - -## Notes - -When used in usehooks, throwing an error in the `localCache` function will trigger `onError`. When a request is made directly using a method instance, a promise instance with a reject status will be returned. diff --git a/docs/tutorial/07-cache/07-server-cache.md b/docs/tutorial/07-cache/07-server-cache.md deleted file mode 100644 index 2d809326f..000000000 --- a/docs/tutorial/07-cache/07-server-cache.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: 服务端缓存 -sidebar_position: 70 ---- - -## alova id 设置 - -可以设置 alova id,用于固定请求,当没有设置时将按 alova 创建顺序递增。 diff --git a/docs/tutorial/07-cache/README.md b/docs/tutorial/07-cache/README.md deleted file mode 100644 index be3949357..000000000 --- a/docs/tutorial/07-cache/README.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -title: Response Cache ---- - -Response cache is a technology that caches the data returned by server to the client. It avoids sending repeated requests when the server data can be reused. It can not only respond to user requests immdediately, but also save server resources. According to different caching scenarios, alova provides 3 modes named **memory mode, cache placeholder mode, and restore mode**. Just choose the one that suits you. - -In addition, using the cache operation API, you can freely add, modify, and delete caches, as well as customize cache matching rules. - -Next, let us start to understand from the cache mode! diff --git a/docs/tutorial/07-cache/_category_.json b/docs/tutorial/07-cache/_category_.json deleted file mode 100644 index 2360c70d8..000000000 --- a/docs/tutorial/07-cache/_category_.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "label": "Response Cache" -} diff --git a/docs/tutorial/08-request-adapter/01-alova-mock.md b/docs/tutorial/08-request-adapter/01-alova-mock.md index e668d17c3..612823839 100644 --- a/docs/tutorial/08-request-adapter/01-alova-mock.md +++ b/docs/tutorial/08-request-adapter/01-alova-mock.md @@ -1,331 +1,331 @@ ---- -title: Mock data -sidebar_position: 10 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -This mock plug-in is an alova request adapter. Different from the traditional Proxy form, you can control the scope of use of mock data. You can control the global scope, a group of interface scopes, and even the enabling and use of a certain interface. Disabled, which is very useful in our actual business scenarios. Each iteration will add or modify a set of interfaces. We hope that the previous functions will still follow the developed interfaces, and let the new or modified interfaces Taking the simulation data, at this time, each developer can group the interfaces involved in this iteration into a group, and turn them on or off. - -## Features - -- ✨Works seamlessly with alova -- ✨Arbitrary grouping of simulation requests to control global, group, and individual simulation interface enable and disable -- ✨Works with mockjs -- ✨Do not pollute the production environment - -## Install - - - - -```bash -npm install @alova/mock --save -``` - - - - -```bash -yarn add @alova/mock -``` - - - - -The following is the usage flow. - -## Usage - -### Define the mock interface - -Use `defineMock` to define a set of mock interfaces. You can directly specify the return response data in each mock interface, or specify the response data to be dynamically calculated for the callback function. - -```javascript title=mockGrou1.js -import { defineMock } from '@alova/mock'; - -export default defineMock( - { - // capture get request - '/todo': [1, 2, 3, 4], - - // rest style request - '/todo/{id}': ({ params }) => { - const id = params.id; - // ... - return { - title: '...', - time: '10:00' - }; - }, - - // capture post request - '[POST]/todo': ({ query, data }) => { - // ... - return { success: true }; - }, - - // return more detailed information - '[POST]/todo': ({ query, data }) => { - //... - return { - status: 403, - statusText: 'unknown error', - responseHeaders: { - //... - }, - body: { - success: true - } - }; - }, - - // simulate network error - '[POST]/todo': ({ query, data }) => { - throw new Error('network error'); - }, - - // Add `-` before the key to disable this mock interface - '-[DELETE]/todo/{id}': ({ params }) => { - // ... - return { success: true }; - } - }, - true -); // The second parameter indicates whether to enable this group of mock interfaces, the default is true, and can be specified as false to close -``` - -### Create mock request adapter - -Create a mock request adapter when calling `createAlova`, and pass in the mock interface to complete. - -```javascript -import GlobalFetch from 'alova/GlobalFetch'; -import { createAlovaMockAdapter } from '@alova/mock'; -import mockGroup1 from './mockGroup1'; - -// highlight-start -const mockAdapter = createAlovaMockAdapter([mockGroup1, /** ... */], { - // Global control whether the mock interface is enabled, the default is true - enable: true, - - // Non-mock request adapter, used to send requests when the mock interface is not matched - httpAdapter: GlobalFetch(), - - // mock interface response delay, in milliseconds - delay: 1000, - - // Whether to print mock interface request information - mockRequestLogger: true, - - // Simulation interface callback, data is the returned simulation data, you can use it to construct any object you want and return it to alova - // The following is the default callback function, which is suitable for requesting the adapter using GlobalFetch - // If you are using other request adapters, please customize the return data structure suitable for the adapter in the mock interface callback - onMockResponse: data => new Response(JSON.stringify(data)) -}); -// highlight-end - -export const alovaInst = createAlova({ - baseURL: 'http://xxx', - - // Use the mock request adapter, if you need to switch adapters, please see the following practical suggestions - requestAdapter: mockAdapter, - - statesHook: /** ... */ -}); -``` - -### Paths match mode - -:::info version required - -1.5.0+ - -::: - -By default, the path defined in `defineMock` is the full pathname of a url, see the following code snippet. - -```javascript -const alovaInst = createAlova({ - baseURL: 'https://api.alovajs.org' - //... -}); -alovaInst.Get('/user?id=1').send(); -``` - -When the request path in the example is `https://api.alovajs.org/user?id=1`, its full pathname is `/user`, which can match `/user` in `defineMock`. - -Usually this is enough, but when your baseURL is not just a domain name. - -```javascript -const alovaInst = createAlova({ - baseURL: 'https://api.alovajs.org/v1/subname' - //... -}); -alovaInst.Get('/user?id=1').send(); -``` - -In this example, the request path is `https://api.alovajs.org/v1/subname/user?id=1`, the matching path of the mock is `/v1/subname/user`, and `/ in the baseURL needs to be v1/subname` is also written together, which is slightly redundant when the number of interfaces is large. - -At this point, you can set `matchMode` to `methodurl` in `createAlovaMockAdapter`, it will only match the url defined in the method instance, for example, the above instance will match `/user?id=1` instead of The part in baseURL needs to be written. On the contrary, if the url in the method instance has a get parameter, it also needs to be written in the matching path of `defineMock`, just like `?id=1` here. - -```javascript -createAlovaMockAdapter([mockGroup1 /** ... */], { - //... - // highlight-start - matchMode: 'methodurl' - // highlight-end -}); -``` - -## Practical advice - -### Group interfaces per developer per version - -In the team development scenario, we often only need to simulate some undeveloped interfaces for each version development, and use the test environment interface for the interface of the previous version. At this time, in order to achieve better simulation interface management, you can use The two dimensions, development version and developer, group interfaces. - -For example, there are two developers named _August_, _kevin_, they are developing v1.1 product features, they can manage the mock interface like this. - -```javascript title=August-v1.1.js -import { defineMock } from '@alova/mock'; - -export default defineMock({ - '/todo': [ - /** */ - ], - '[POST]/todo': ({ data }) => { - // ... - // return... - } - // ... -}); -``` - -```javascript title=kevin-v1.1.js -import { defineMock } from '@alova/mock'; - -export default defineMock({ - '[PUT]/todo/add': ({ data }) => { - // ... - // return... - }, - '[DELETE]/todo/remove': ({ data }) => { - // ... - // return... - } - // ... -}); -``` - -```javascript title=request.js -import Augustv1_1 from './August-v1.1'; -import Keevenv1_1 from './kevin-v1.1'; - -const mockAdapter = createAlovaMockAdapter([Augustv1_1, kevinv1_1], { - httpAdapter: GlobalFetch(), - delay: 1000 -}); -export const alovaInst = createAlova({ - baseURL: 'http://xxx', - requestAdapter: mockAdapter - // ... -}); -``` - -### Exclude mock code in production - -The mock data is generally only used in the development environment, and will be switched to the actual interface in the production environment, so this mock code becomes useless in the production environment. At this time, we can exclude this code by judging the environment variables. , you just need to do: - -```javascript -const globalFetch = GlobalFetch(); -const mockAdapter = createAlovaMockAdapter([mockGroup1, /** ... */], { - httpAdapter: globalFetch, - delay: 1000, -}); - -export const alovaInst = createAlova({ - baseURL: 'http://xxx', - - // highlight-start - // In the production environment controlled by environment variables, the mock-related code will not be packaged in - requestAdapter: process.env.NODE_ENV === 'development' ? mockAdapter : globalFetch, - // highlight-end - - statesHook: /** ... */ -}); -``` - -### Use with mockjs - -If you don't want to write the mock data yourself, but use it with a mock js library (such as mockjs), you can do so. - -```javascript -import { defineMock } from '@alova/mock'; -import Mock from 'mockjs'; - -export default defineMock({ - '/api1': Mock.mock({ - 'id|1-10000': 100 - }) -}); -``` - -## Convert mock data - -**@alova/mock** By default, the response data is packaged as a Response instance, and the response header is packaged as a Headers instance by default, which is adapted for `GlobalFetch`, but if you use other request adapters, you need to mock the data Convert to the corresponding format. - -### Convert response data - -You can intercept the mock response data in the `onMockResponse` field and return the transformed response data and response headers. - -> You can also throw an ERROR in onMockResponse to indicate a failure request. - -```javascript -const mockAdapter = createAlovaMockAdapter( - [ - /* mock data */ - ], - { - //... - // highlight-start - onMockResponse(response, request, currentMethod) { - // response is the corresponding data set, which contains status, statusText, responseHeaders, body - // request is the request data, which contains query, params, headers, data - // currentMethod is the method instance of the current request - //... - // Return converted response data and response headers - return { - response: /** response data */, - headers: /** Response headers */ - }; - } - // highlight-end - } -); -``` - -### Convert Error Instance - -You can intercept the error instance in the `onMockError` field and return the converted error message. - -> You can also throw an ERROR in onMockResponse to indicate failure request. - -```javascript -const mockAdapter = createAlovaMockAdapter( - [ - /* mock data */ - ], - { - //... - // highlight-start - onMockError(error, currentMethod) { - // error is an error instance - // currentMethod is the method instance of the current request - //... - // Return the converted error message collection - } - // highlight-end - } -); -``` +--- +title: Mock data +sidebar_position: 10 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +This mock plug-in is an alova request adapter. Different from the traditional Proxy form, you can control the scope of use of mock data. You can control the global scope, a group of interface scopes, and even the enabling and use of a certain interface. Disabled, which is very useful in our actual business scenarios. Each iteration will add or modify a set of interfaces. We hope that the previous functions will still follow the developed interfaces, and let the new or modified interfaces Taking the simulation data, at this time, each developer can group the interfaces involved in this iteration into a group, and turn them on or off. + +## Features + +- Works seamlessly with alova +- Arbitrary grouping of simulation requests to control global, group, and individual simulation interface enable and disable +- Works with mockjs +- Do not pollute the production environment + +## Install + + + + +```bash +npm install @alova/mock --save +``` + + + + +```bash +yarn add @alova/mock +``` + + + + +The following is the usage flow. + +## Usage + +### Define the mock interface + +Use `defineMock` to define a set of mock interfaces. You can directly specify the return response data in each mock interface, or specify the response data to be dynamically calculated for the callback function. + +```javascript title=mockGrou1.js +import { defineMock } from '@alova/mock'; + +export default defineMock( + { + // capture get request + '/todo': [1, 2, 3, 4], + + // rest style request + '/todo/{id}': ({ params }) => { + const id = params.id; + // ... + return { + title: '...', + time: '10:00' + }; + }, + + // capture post request + '[POST]/todo': ({ query, data }) => { + // ... + return { success: true }; + }, + + // return more detailed information + '[POST]/todo': ({ query, data }) => { + //... + return { + status: 403, + statusText: 'unknown error', + responseHeaders: { + //... + }, + body: { + success: true + } + }; + }, + + // simulate network error + '[POST]/todo': ({ query, data }) => { + throw new Error('network error'); + }, + + // Add `-` before the key to disable this mock interface + '-[DELETE]/todo/{id}': ({ params }) => { + // ... + return { success: true }; + } + }, + true +); // The second parameter indicates whether to enable this group of mock interfaces, the default is true, and can be specified as false to close +``` + +### Create mock request adapter + +Create a mock request adapter when calling `createAlova`, and pass in the mock interface to complete. + +```javascript +import GlobalFetch from 'alova/GlobalFetch'; +import { createAlovaMockAdapter } from '@alova/mock'; +import mockGroup1 from './mockGroup1'; + +// highlight-start +const mockAdapter = createAlovaMockAdapter([mockGroup1, /** ... */], { + // Global control whether the mock interface is enabled, the default is true + enable: true, + + // Non-mock request adapter, used to send requests when the mock interface is not matched + httpAdapter: GlobalFetch(), + + // mock interface response delay, in milliseconds + delay: 1000, + + // Whether to print mock interface request information + mockRequestLogger: true, + + // Simulation interface callback, data is the returned simulation data, you can use it to construct any object you want and return it to alova + // The following is the default callback function, which is suitable for requesting the adapter using GlobalFetch + // If you are using other request adapters, please customize the return data structure suitable for the adapter in the mock interface callback + onMockResponse: data => new Response(JSON.stringify(data)) +}); +// highlight-end + +export const alovaInst = createAlova({ + baseURL: 'http://xxx', + + // Use the mock request adapter, if you need to switch adapters, please see the following practical suggestions + requestAdapter: mockAdapter, + + statesHook: /** ... */ +}); +``` + +### Paths match mode + +:::info version required + +1.5.0+ + +::: + +By default, the path defined in `defineMock` is the full pathname of a url, see the following code snippet. + +```javascript +const alovaInst = createAlova({ + baseURL: 'https://api.alovajs.org' + //... +}); +alovaInst.Get('/user?id=1').send(); +``` + +When the request path in the example is `https://api.alovajs.org/user?id=1`, its full pathname is `/user`, which can match `/user` in `defineMock`. + +Usually this is enough, but when your baseURL is not just a domain name. + +```javascript +const alovaInst = createAlova({ + baseURL: 'https://api.alovajs.org/v1/subname' + //... +}); +alovaInst.Get('/user?id=1').send(); +``` + +In this example, the request path is `https://api.alovajs.org/v1/subname/user?id=1`, the matching path of the mock is `/v1/subname/user`, and `/ in the baseURL needs to be v1/subname` is also written together, which is slightly redundant when the number of interfaces is large. + +At this point, you can set `matchMode` to `methodurl` in `createAlovaMockAdapter`, it will only match the url defined in the method instance, for example, the above instance will match `/user?id=1` instead of The part in baseURL needs to be written. On the contrary, if the url in the method instance has a get parameter, it also needs to be written in the matching path of `defineMock`, just like `?id=1` here. + +```javascript +createAlovaMockAdapter([mockGroup1 /** ... */], { + //... + // highlight-start + matchMode: 'methodurl' + // highlight-end +}); +``` + +## Practical advice + +### Group interfaces per developer per version + +In the team development scenario, we often only need to simulate some undeveloped interfaces for each version development, and use the test environment interface for the interface of the previous version. At this time, in order to achieve better simulation interface management, you can use The two dimensions, development version and developer, group interfaces. + +For example, there are two developers named _August_, _kevin_, they are developing v1.1 product features, they can manage the mock interface like this. + +```javascript title=August-v1.1.js +import { defineMock } from '@alova/mock'; + +export default defineMock({ + '/todo': [ + /** */ + ], + '[POST]/todo': ({ data }) => { + // ... + // return... + } + // ... +}); +``` + +```javascript title=kevin-v1.1.js +import { defineMock } from '@alova/mock'; + +export default defineMock({ + '[PUT]/todo/add': ({ data }) => { + // ... + // return... + }, + '[DELETE]/todo/remove': ({ data }) => { + // ... + // return... + } + // ... +}); +``` + +```javascript title=request.js +import Augustv1_1 from './August-v1.1'; +import Keevenv1_1 from './kevin-v1.1'; + +const mockAdapter = createAlovaMockAdapter([Augustv1_1, kevinv1_1], { + httpAdapter: GlobalFetch(), + delay: 1000 +}); +export const alovaInst = createAlova({ + baseURL: 'http://xxx', + requestAdapter: mockAdapter + // ... +}); +``` + +### Exclude mock code in production + +The mock data is generally only used in the development environment, and will be switched to the actual interface in the production environment, so this mock code becomes useless in the production environment. At this time, we can exclude this code by judging the environment variables. , you just need to do: + +```javascript +const globalFetch = GlobalFetch(); +const mockAdapter = createAlovaMockAdapter([mockGroup1, /** ... */], { + httpAdapter: globalFetch, + delay: 1000, +}); + +export const alovaInst = createAlova({ + baseURL: 'http://xxx', + + // highlight-start + // In the production environment controlled by environment variables, the mock-related code will not be packaged in + requestAdapter: process.env.NODE_ENV === 'development' ? mockAdapter : globalFetch, + // highlight-end + + statesHook: /** ... */ +}); +``` + +### Use with mockjs + +If you don't want to write the mock data yourself, but use it with a mock js library (such as mockjs), you can do so. + +```javascript +import { defineMock } from '@alova/mock'; +import Mock from 'mockjs'; + +export default defineMock({ + '/api1': Mock.mock({ + 'id|1-10000': 100 + }) +}); +``` + +## Convert mock data + +**@alova/mock** By default, the response data is packaged as a Response instance, and the response header is packaged as a Headers instance by default, which is adapted for `GlobalFetch`, but if you use other request adapters, you need to mock the data Convert to the corresponding format. + +### Convert response data + +You can intercept the mock response data in the `onMockResponse` field and return the transformed response data and response headers. + +> You can also throw an ERROR in onMockResponse to indicate a failure request. + +```javascript +const mockAdapter = createAlovaMockAdapter( + [ + /* mock data */ + ], + { + //... + // highlight-start + onMockResponse(response, request, currentMethod) { + // response is the corresponding data set, which contains status, statusText, responseHeaders, body + // request is the request data, which contains query, params, headers, data + // currentMethod is the method instance of the current request + //... + // Return converted response data and response headers + return { + response: /** response data */, + headers: /** Response headers */ + }; + } + // highlight-end + } +); +``` + +### Convert Error Instance + +You can intercept the error instance in the `onMockError` field and return the converted error message. + +> You can also throw an ERROR in onMockResponse to indicate failure request. + +```javascript +const mockAdapter = createAlovaMockAdapter( + [ + /* mock data */ + ], + { + //... + // highlight-start + onMockError(error, currentMethod) { + // error is an error instance + // currentMethod is the method instance of the current request + //... + // Return the converted error message collection + } + // highlight-end + } +); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current.json b/i18n/zh-CN/docusaurus-plugin-content-docs/current.json index 18bd79697..8599723a1 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current.json +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current.json @@ -19,9 +19,9 @@ "message": "开始", "description": "The label for category Get Started in sidebar tutorialSidebar" }, - "sidebar.tutorialSidebar.category.Response Cache": { - "message": "响应缓存", - "description": "The label for category Learn in sidebar tutorialSidebar" + "sidebar.tutorialSidebar.category.Cache Details": { + "message": "缓存详解", + "description": "The label for category Cache Details in sidebar tutorialSidebar" }, "sidebar.tutorialSidebar.category.Combine Framework": { "message": "结合框架", diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/08-server.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/08-server.md index ed462008e..db20b5c83 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/08-server.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/08-server.md @@ -147,4 +147,4 @@ const alovaInstance = createAlova({ }); ``` -> 深入了解响应缓存,请移步[深入响应缓存](/xxx)。 +> 深入了解响应缓存,请移步[缓存详解](/turorial/cache/mode)。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/README.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/README.md index 8ec8f7efc..5de4870e2 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/README.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/README.md @@ -1,134 +1,132 @@ ---- -title: alova 是什么 ---- - -import Link from '@docusaurus/Link'; -import NavCard from '@site/src/components/NavCard'; -import SupportList from '@site/src/components/SupportList'; - -alova.js 是一个创新的下一代请求工具,它可以帮你在请求方面省去大部分的工作,解决前后端协作问题,让网络请求变得非常简单。我们来看看 alova 是如何帮你的简化工作的。 - -![](/img/overview_flow_cn.png) - -它可以将 API 的消费从 7 步简化为 1 步,你只需要选择使用的 API 就可以了。 - -## 如何做的? - -### 请求策略 - -在实际项目中,前端请求总是需要根据不同场景考虑应该什么时候发出请求、什么时候不能发出请求、如何处理响应数据等才能满足项目的表现、性能的提高,这将导致开发人员时间成本和代码维护成本的增加,在 alova 中提供了一套完整的应对复杂请求场景的方案,我们称之为**请求策略**,只需一行代码就能快速实现各种复杂的请求逻辑,不仅能帮你提升开发效率,还能帮你提升 App 的运行效率,降低服务端压力。 - -例如,`useRequest`可以自动管理请求状态,**`loading/error/data` 是响应式的数据**,在 react、vue、svelte 等 UI 框架中可以直接在视图中绑定它们,而且会根据请求状态自动维护它这些响应式数据。 - -```javascript -const { loading, error, data } = useRequest(alova.Get('/api/user')); -``` - -再来一个分页请求策略,**当`page/pageSize`等发生变化时会自动以不同参数触发请求**。 - -```javascript -const { loading, error, data, page, pageSize, total } = usePagination((page, size) => - alova.Get('/api/user/list', { - params: { page, size } - }) -); -``` - -alova 提供了 10+个基于[RSM](/tutorial/others/RSM)规范的请求策略模块,它们以 useHook 的形式实现。 - -### alova 编辑器扩展 - -在 vscode 中使用 alova 扩展可以帮你自动生成包含完整的 API 文档标注,响应类型的请求代码。在过去,你需要先查询 API 文档,并不断地在 API 文档与编辑器切换来编写请求代码,使用 alova 插件后,你可以不再需要离开编辑器,直接在编辑器中边查边使用 API,感受不一样的 API 使用体验。 - -> 关于 alova 插件的详细介绍,请参考 [集成 IDE 插件](/tutorial/getting-started/plugin-integration)。 - -## 有什么不同吗? - -与其他请求库不同的是,alova 的目标是让请求变得非常简单,并且保持更高效的数据交互。 - -我们为开发者和 App 使用者双方考虑,对于开发者来说,alova 为他们提供了极致的使用体验,对于应用的用户来说,他们可以享受到 alova 的高性能数据交互带来的流畅体验。 - -此外,再从具体的特性来看看: - -- 与 axios 相似的 api 设计,让使用者学习成本更低; -- 高性能的客户端和服务端请求策略,让应用更流畅; -- 灵活性高,alova 的适配器可以让 alova 在任何 js 环境下,与任何 UI 框架协作使用,并且提供了统一的使用体验和完美的代码迁移; -- 2 种缓存模式和请求共享机制,提升请求性能并降低服务端压力; -- api 代码的高聚合组织,每个 api 的请求参数、缓存行为、响应数据转换等都将聚集在相同的代码块中,这对于管理大量的 api 有很大的优势; - -:::info 对比 - -你还可以查看请[与其他请求库比较](/tutorial/others/comparison)详细了解 alova 的不同之处。 - -::: - -## 在任何 JS 环境下运行 - -不仅如此,alova 的灵活性非常高,你可以在以下任意的 JS 环境下,配合不同的请求工具使用(灰色部分将在未来逐渐支持)。 - - - -## 在线试用 - -你可以通过 Codesandbox [在线可编辑示例尝试 alovajs](/category/examples)直接在浏览器中运行项目,因此它与本地开发几乎无差别,同时无需在你的机器上安装任何东西。 - -## 脚手架推荐 - -, -title: 'Uniapp 脚手架 - unibest', -desc: '集成了最新前端技术栈的跨端解决方案', -link: 'https://codercup.github.io/unibest-docs/', -target: '__blank' -} -]}> - -## 加入 alova 社区 - -import ImgDiscord from '@site/static/img/discord.svg'; -import ImgX from '@site/static/img/x.svg'; -import ImgWechat from '@site/static/img/wechat.svg'; -import wechatQrcode from '@site/static/img/wechat_qrcode.jpg'; - -, -title: 'Discord', -desc: '社区的 GPT 机器人为你解答', -link: 'https://discord.gg/S47QGJgkVb', -target: '__blank' -}, -{ -Image: , -title: '微信', -desc: '在群聊交流,更快获得回应', -link: wechatQrcode, -target: '__blank' -}, -{ -Image: , -title: 'X', -desc: '关注我们,持续获得最新动态', -link: 'https://x.com/alovajs', -target: '__blank' -} -]}> - -## 欢迎参与贡献 - -在参与贡献前,请务必详细阅读 [贡献指南](/contributing/overview),以保证你的有效贡献。 - -## 开始 - -接下来,我们将从最简单的请求开始,再到请求策略的讲解,了解 alova 如何简化你的工作,再深入到进阶指南,以及在实际项目中总结的最佳实践。 - -让我们开始学习发送第一个请求吧! - - +--- +title: alova 是什么 +--- + +import Link from '@docusaurus/Link'; +import NavCard from '@site/src/components/NavCard'; +import SupportList from '@site/src/components/SupportList'; + +alova 是一个创新的下一代请求工具,从前后端协作和 API 消费作为出发点,将 API 的消费从 7 步简化为只有 1 步,帮你在请求方面省去大部分的工作,让网络请求变得非常简单。我们来看看 alova 是如何帮你的简化工作的。 + +![](/img/overview_flow_cn.png) + +## 如何做的? + +### 请求策略 + +在实际项目中,前端请求总是需要根据不同场景考虑应该什么时候发出请求、什么时候不能发出请求、如何处理响应数据等才能满足项目的表现、性能的提高,这将导致开发人员时间成本和代码维护成本的增加,在 alova 中提供了一套完整的应对复杂请求场景的方案,我们称之为**请求策略**,只需一行代码就能快速实现各种复杂的请求逻辑,不仅能帮你提升开发效率,还能帮你提升 App 的运行效率,降低服务端压力。 + +例如,`useRequest`可以自动管理请求状态,**`loading/error/data` 是响应式的数据**,在 react、vue、svelte 等 UI 框架中可以直接在视图中绑定它们,而且会根据请求状态自动维护它这些响应式数据。 + +```javascript +const { loading, error, data } = useRequest(alova.Get('/api/user')); +``` + +再来一个分页请求策略,**当`page/pageSize`等发生变化时会自动以不同参数触发请求**。 + +```javascript +const { loading, error, data, page, pageSize, total } = usePagination((page, size) => + alova.Get('/api/user/list', { + params: { page, size } + }) +); +``` + +alova 提供了 10+个基于[RSM](/tutorial/others/RSM)规范的请求策略模块,它们以 useHook 的形式实现。 + +### alova 编辑器扩展 + +在 vscode 中使用 alova 扩展可以帮你自动生成包含完整的 API 文档标注,响应类型的请求代码。在过去,你需要先查询 API 文档,并不断地在 API 文档与编辑器切换来编写请求代码,使用 alova 插件后,你可以不再需要离开编辑器,直接在编辑器中边查边使用 API,感受不一样的 API 使用体验。 + +> 关于 alova 插件的详细介绍,请参考 [集成 IDE 插件](/tutorial/getting-started/plugin-integration)。 + +## 有什么不同吗? + +与其他请求库不同的是,alova 的目标是让请求变得非常简单,并且保持更高效的数据交互。 + +我们为开发者和 App 使用者双方考虑,对于开发者来说,alova 为他们提供了极致的使用体验,对于应用的用户来说,他们可以享受到 alova 的高性能数据交互带来的流畅体验。 + +此外,再从具体的特性来看看: + +- 与 axios 相似的 api 设计,让使用者学习成本更低; +- 高性能的客户端和服务端请求策略,让应用更流畅; +- 灵活性高,alova 的适配器可以让 alova 在任何 js 环境下,与任何 UI 框架协作使用,并且提供了统一的使用体验和完美的代码迁移; +- 2 种缓存模式和请求共享机制,提升请求性能并降低服务端压力; +- api 代码的高聚合组织,每个 api 的请求参数、缓存行为、响应数据转换等都将聚集在相同的代码块中,这对于管理大量的 api 有很大的优势; + +:::info 对比 + +你还可以查看请[与其他请求库比较](/tutorial/others/comparison)详细了解 alova 的不同之处。 + +::: + +## 在任何 JS 环境下运行 + +不仅如此,alova 的灵活性非常高,你可以在以下任意的 JS 环境下,配合不同的请求工具使用(灰色部分将在未来逐渐支持)。 + + + +## 在线试用 + +你可以通过 Codesandbox [在线可编辑示例尝试 alovajs](/category/examples)直接在浏览器中运行项目,因此它与本地开发几乎无差别,同时无需在你的机器上安装任何东西。 + +## 脚手架推荐 + +, +title: 'Uniapp 脚手架 - unibest', +desc: '集成了最新前端技术栈的跨端解决方案', +link: 'https://codercup.github.io/unibest-docs/', +target: '__blank' +} +]}> + +## 加入 alova 社区 + +import ImgDiscord from '@site/static/img/discord.svg'; +import ImgX from '@site/static/img/x.svg'; +import ImgWechat from '@site/static/img/wechat.svg'; +import wechatQrcode from '@site/static/img/wechat_qrcode.jpg'; + +, +title: 'Discord', +desc: '社区的 GPT 机器人为你解答', +link: 'https://discord.gg/S47QGJgkVb', +target: '__blank' +}, +{ +Image: , +title: '微信', +desc: '在群聊交流,更快获得回应', +link: wechatQrcode, +target: '__blank' +}, +{ +Image: , +title: 'X', +desc: '关注我们,持续获得最新动态', +link: 'https://x.com/alovajs', +target: '__blank' +} +]}> + +## 欢迎参与贡献 + +在参与贡献前,请务必详细阅读 [贡献指南](/contributing/overview),以保证你的有效贡献。 + +## 开始 + +接下来,我们将从最简单的请求开始,再到请求策略的讲解,了解 alova 如何简化你的工作,再深入到进阶指南,以及在实际项目中总结的最佳实践。 + +让我们开始学习发送第一个请求吧! + + diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/01-use-fetcher.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/03-use-fetcher.md similarity index 57% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/01-use-fetcher.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/03-use-fetcher.md index 696148d51..e83727ee9 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/01-use-fetcher.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/03-use-fetcher.md @@ -1,307 +1,241 @@ ---- -title: 数据拉取 -sidebar_position: 10 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -当你有以下需求时: - -1. 预加载后续流程中将会使用到的数据并存放在缓存中,让用户不再等待数据加载的过程; -2. 便捷地实现跨页面更新数据(类似全局状态),例如修改 todo 列表的某一项后重新拉取最新数据,响应后将刷新界面。 - -`useFetcher`就是用于实现以上场景的 hook,通过它获取的响应数据不能直接接收到,但通过它拉取的数据除了会更新缓存外还会更新对应的状态,从而重新渲染视图。 - -## 预加载数据 - -我们来实现一个分页列表中,自动预加载下一页数据,在预加载数据前请确保使用的 Method 实例已开启了缓存。 - - - - -```html - - - -``` - - - - -```jsx -import { useState } from 'react'; - -// method实例创建函数 -const getTodoList = currentPage => { - return alovaInstance.Get('/todo/list', { - localCache: 60000, - params: { - currentPage, - pageSize: 10 - } - }); -}; - -const App = () => { - const { - // fetching属性与loading相同,发送拉取请求时为true,请求结束后为false - fetching, - error, - onSuccess, - onError, - onComplete, - - // 调用fetch后才会发送请求拉取数据,可以重复调用fetch多次拉取不同接口的数据 - fetch - } = useFetcher({ - updateState: false - }); - const [currentPage, setCurrentPage] = useState(1); - const { data, onSuccess } = useWatcher(() => getTodoList(currentPage), [currentPage], { - immediate: true - }); - - // 在当前页加载成功后,传入下一页的method实例,即可预拉取下一页的数据 - onSuccess(() => { - fetch(getTodoList(currentPage + 1)); - }); - - return ( - <> - {fetching ?
Fetching...
: null} - {/* 列表视图 */} - - ); -}; -``` - -
- - -```html - - -{#if fetching} -
Fetching...
-{/if} - -``` - -
- - -```html - - - -``` - - -
- -:::warning - -以上示例在调用`useFetcher`时设置了`updateState`为 false,这是因为默认情况下 fetch 时会自动触发跨组件更新状态,导致视图重新渲染,在预拉取的数据与当前请求的数据为同一份`data`时可以关闭它,以免影响视图错误。 - -::: - -## 跨模块/组件更新视图 - -下面我们来实现修改一条 todo 数据,并重新拉取最新的 todo 列表数据,让视图更新。我们可能并不知道 todo 列表当前位于第几页,此时在使用`fetch`函数时可以使用[method 匹配器](/tutorial/advanced/method-matcher)来动态拉取当前页的数据。 - -> method 匹配器是用于,在已请求过的 method 实例中,查找符合条件的 method 实例。 - -首先,为 todo 列表的 method 实例设置名称,用于在无法直接指定 Method 实例时,过滤出需要的 Method 实例。 - -```javascript title="api/todoList.js" -const getTodoList = currentPage => { - return alovaInstance.Get('/todo/list', { - // highlight-start - name: 'todoList', - // highlight-end - params: { - currentPage, - pageSize: 10 - } - }); -}; -``` - -然后在`EditTodo`组件中,通过`fetch`函数在已请求过的 Method 实例中,动态查找最后一个 name 为`todoList`进行数据拉取。 - -```javascript title="EditTodo Component" -const { fetch } = useFetcher(); - -// 在事件中触发数据拉取 -const handleSubmit = () => { - // 提交数据... - await fetch({ - name: 'todoList', - filter: (method, index, ary) => { - // 返回true来指定需要拉取的Method实例 - return index === ary.length - 1; - } - }); -}; -``` - -:::warning 注意事项 - -useFetcher 请求完成后只更新缓存,且如果发现这个 Method 实例在之前使用过 useHook 请求过,那么也会更新这个 useHook 创建的`data`状态,从而保证页面数据一致,这是`useFetcher`用于跨模块/组件更新视图的保证。 - -::: - -> 更多 method 匹配器的使用方法见 [method 匹配器](/tutorial/advanced/method-matcher)。 - -## 强制发送请求 - -和`useRequest`和`useWatcher`相同,更多请阅读[强制请求](/tutorial/cache/force-request)。 - -## 绑定响应回调 - -useFetcher 也支持绑定`onSuccess/onError/onComplete`回调函数。 - -```javascript -const { onSuccess, onError, onComplete } = useFetcher(); -``` - -具体请阅读[响应处理](/tutorial/combine-framework/response)。 - -## send 函数参数传递规则 - -与`useRequest`和`useWatcher`不同的是,fetch 函数的自定义参数是从第二个参数开始的,它们也将分别被事件回调和`force`函数接收。 - -```javascript -const { onSuccess, fetch } = useFetcher(); -onSuccess(({ sendArgs }) => { - // sendArgs的值为['test arg'] -}); - -fetch(getTodoList(), 'test arg'); -``` - -具体请阅读[send 函数参数传递规则](/tutorial/combine-framework/receive-params)。 - -## useRequest 与 useFetcher 对比 - -1. useFetcher 不返回`data`字段,预拉取的数据将保存在缓存中,以及更新对应位置的状态数据; -2. 将`loading`改名为了`fetching`; -3. 没有`send`函数,但多了一个`fetch`函数,可以重复利用 fetch 函数拉取不同接口的数据,此时你可以使用 `fetching` 和 `error` 状态统一渲染视图,从而达到统一处理的目的; +--- +title: 数据拉取 +sidebar_position: 30 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info 策略类型 + +use hook + +::: + +当你有以下需求时: + +1. 预加载后续流程中将会使用到的数据并存放在缓存中,让用户不再等待数据加载的过程; +2. 便捷地实现跨页面更新数据(类似全局状态),例如修改 todo 列表的某一项后重新拉取最新数据,响应后将刷新界面。 + +`useFetcher`就是用于实现以上场景的 hook,通过它获取的响应数据不能直接接收到,但通过它拉取的数据除了会更新缓存外还会更新对应的状态,从而重新渲染视图。 + +## 预加载数据 + +我们来实现一个分页列表中,自动预加载下一页数据,在预加载数据前请确保使用的 Method 实例已开启了缓存。 + + + + +```html + + + +``` + + + + +```jsx +import { useState } from 'react'; +import { useFetcher } from 'alova/client'; + +// method实例创建函数 +const getTodoList = currentPage => { + return alovaInstance.Get('/todo/list', { + localCache: 60000, + params: { + currentPage, + pageSize: 10 + } + }); +}; + +const App = () => { + const { + // loading表示发送拉取请求的状态 + loading, + error, + onSuccess, + onError, + onComplete, + + // 调用fetch后才会发送请求拉取数据,可以重复调用fetch多次拉取不同接口的数据 + fetch + } = useFetcher({ + updateState: false + }); + const [currentPage, setCurrentPage] = useState(1); + const { data, onSuccess } = useWatcher(() => getTodoList(currentPage), [currentPage], { + immediate: true + }); + + // 在当前页加载成功后,传入下一页的method实例,即可预拉取下一页的数据 + onSuccess(() => { + fetch(getTodoList(currentPage + 1)); + }); + + return ( + <> + {loading ?
Fetching...
: null} + {/* 列表视图 */} + + ); +}; +``` + +
+ + +```html + + +{#if loading} +
Fetching...
+{/if} + +``` + +
+
+ +可以重复利用 fetch 函数拉取不同接口的数据,此时你可以使用 `loading` 和 `error` 状态统一渲染视图,从而达到统一处理的目的; + +:::warning + +以上示例在调用`useFetcher`时设置了`updateState`为 false,这是因为默认情况下 fetch 时会自动触发跨组件更新状态,导致视图重新渲染,在预拉取的数据与当前请求的数据为同一份`data`时可以关闭它,以免影响视图错误。 + +::: + +## 跨模块/组件更新视图 + +下面我们来实现修改一条 todo 数据,并重新拉取最新的 todo 列表数据,让视图更新。我们可能并不知道 todo 列表当前位于第几页,此时在使用`fetch`函数时可以使用[method 快照匹配器](/tutorial/advanced/method-matcher)来动态拉取当前页的数据。 + +> method 快照匹配器可以在已请求过的 method 实例中,查找符合条件的 method 实例。 + +首先,为 todo 列表的 method 实例设置名称,用于在无法直接指定 Method 实例时,过滤出需要的 Method 实例。 + +```javascript title="api/todoList.js" +const getTodoList = currentPage => { + return alovaInstance.Get('/todo/list', { + // highlight-start + name: 'todoList', + // highlight-end + params: { + currentPage, + pageSize: 10 + } + }); +}; +``` + +然后在`EditTodo`组件中,动态查找最后一个 name 为`todoList`进行数据拉取。 + +```javascript title="EditTodo Component" +const { fetch } = useFetcher(); + +// 在事件中触发数据拉取 +const handleSubmit = () => { + // 提交数据... + // highlight-start + const lastMethod = alovaInstance.snapshots.match({ + name: 'todoList', + filter: (method, index, ary) => { + // 返回true来指定需要拉取的Method实例 + return index === ary.length - 1; + } + }, true); + if (lastMethod) { + await fetch(lastMethod); + } + // highlight-end +}; +``` + +:::warning 注意事项 + +useFetcher 只会在请求完成后更新缓存,并且如果此方法发现该实例之前已经使用 useHook 请求过,则此 useHook 创建的 `data` 状态也会更新,以保证页面数据一致。这是使用 `useFetcher` 跨模块/组件更新视图的保证。 + +::: + +> 更多 method 快照匹配器的使用方法见 [method 快照匹配器](/tutorial/advanced/method-matcher)。 + +## 强制发送请求 + +和`useRequest`和`useWatcher`相同,更多请阅读[强制请求](/tutorial/cache/force-request)。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/03-use-pagination.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/04-use-pagination.md similarity index 98% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/03-use-pagination.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/04-use-pagination.md index 94878e909..d80142489 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/03-use-pagination.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/04-use-pagination.md @@ -1,6 +1,6 @@ --- title: 分页请求策略 -sidebar_position: 30 +sidebar_position: 40 --- import Tabs from '@theme/Tabs'; @@ -24,15 +24,15 @@ use hook ## 特性 -- ✨ 丰富全面的分页状态; -- ✨ 丰富全面的分页事件; -- ✨ 更改 page、pageSize 自动获取指定分页数据; -- ✨ 数据缓存,无需重复请求相同参数的列表数据; -- ✨ 前后页预加载,翻页不再等待; -- ✨ 搜索条件监听自动重新获取页数; -- ✨ 支持列表数据的新增、编辑、删除; -- ✨ 支持刷新指定页的数据,无需重置; -- ✨ 请求级搜索防抖,无需自行维护; +- 丰富全面的分页状态; +- 丰富全面的分页事件; +- 更改 page、pageSize 自动获取指定分页数据; +- 数据缓存,无需重复请求相同参数的列表数据; +- 前后页预加载,翻页不再等待; +- 搜索条件监听自动重新获取页数; +- 支持列表数据的新增、编辑、删除; +- 支持刷新指定页的数据,无需重置; +- 请求级搜索防抖,无需自行维护; ## 使用 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/04-use-form.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/05-use-form.md similarity index 98% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/04-use-form.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/05-use-form.md index b66b03707..14987e5ef 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/04-use-form.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/05-use-form.md @@ -1,6 +1,6 @@ --- title: 表单提交策略 -sidebar_position: 40 +sidebar_position: 50 --- import Tabs from '@theme/Tabs'; @@ -22,10 +22,10 @@ use hook ## 特性 -- ✨ 表单草稿; -- ✨ 多页面(多步骤)表单; -- ✨ 表单提交自动重置数据; -- ✨ 手动重置表单数据; +- 表单草稿; +- 多页面(多步骤)表单; +- 表单提交自动重置数据; +- 手动重置表单数据; ## 使用 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/05-token-authentication.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/06-token-authentication.md similarity index 86% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/05-token-authentication.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/06-token-authentication.md index 138d28fc4..75f5e768f 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/05-token-authentication.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/06-token-authentication.md @@ -1,6 +1,6 @@ --- title: Token认证拦截器 -sidebar_position: 50 +sidebar_position: 60 --- import Tabs from '@theme/Tabs'; @@ -10,8 +10,6 @@ import TabItem from '@theme/TabItem'; 策略类型:拦截器 -版本要求:v1.3.0+ - ::: > 在使用扩展 hooks 前,确保你已熟悉了 alova 的基本使用。 @@ -20,11 +18,11 @@ Token 认证拦截器,对基于 token 的登录、登出、token 附带、toke ## 特性 -- ✨ 统一维护 Token 身份认证的所有代码,包括登录、登出、token 附带、token 刷新等; -- ✨ 支持在客户端和服务端验证 token 过期,并无感刷新 token; -- ✨ 依赖 token 的请求自动等待 token 刷新完成再请求; -- ✨ 使用元数据设置请求身份; -- ✨ 自动放行不依赖 token 的访客请求; +- 统一维护 Token 身份认证的所有代码,包括登录、登出、token 附带、token 刷新等; +- 支持在客户端和服务端验证 token 过期,并无感刷新 token; +- 依赖 token 的请求自动等待 token 刷新完成再请求; +- 使用元数据设置请求身份; +- 自动放行不依赖 token 的访客请求; ## 绑定 Token 认证拦截器 @@ -132,15 +130,27 @@ createClientTokenAuthentication({ // 当token过期时触发,在此函数中触发刷新token handler: async method => { - const { token, refresh_token } = await refreshToken(); - localStorage.setItem('token', token); - localStorage.setItem('refresh_token', refresh_token); + try { + const { token, refresh_token } = await refreshToken(); + localStorage.setItem('token', token); + localStorage.setItem('refresh_token', refresh_token); + } catch (error) { + // token刷新失败,跳转回登录页 + location.href = '/login'; + // 并抛出错误 + throw error; + } } } }); ``` -为了让`refreshToken`请求顺利通过,需要通过元数据标识`authRole`为`refreshToken`。 +:::warning 注意 + +1. 为了让`refreshToken`请求顺利通过,需要通过元数据标识`authRole`为`refreshToken`。 +2. 如果 token 刷新失败必须抛出错误,阻止失败接口重试和等待接口继续请求。 + +::: > 了解更多元数据的信息,请前往[method 元数据](/tutorial/getting-started/method-metadata)。 @@ -173,9 +183,16 @@ createServerTokenAuthentication({ // 当token过期时触发,在此函数中触发刷新token handler: async (response, method) => { - const { token, refresh_token } = await refreshToken(); - localStorage.setItem('token', token); - localStorage.setItem('refresh_token', refresh_token); + try { + const { token, refresh_token } = await refreshToken(); + localStorage.setItem('token', token); + localStorage.setItem('refresh_token', refresh_token); + } catch (error) { + // token刷新失败,跳转回登录页 + location.href = '/login'; + // 并抛出错误 + throw error; + } } } }); @@ -196,15 +213,27 @@ createServerTokenAuthentication({ // 当token过期时触发,在此函数中触发刷新token handler: async (error, method) => { - const { token, refresh_token } = await refreshToken(); - localStorage.setItem('token', token); - localStorage.setItem('refresh_token', refresh_token); + try { + const { token, refresh_token } = await refreshToken(); + localStorage.setItem('token', token); + localStorage.setItem('refresh_token', refresh_token); + } catch (error) { + // token刷新失败,跳转回登录页 + location.href = '/login'; + // 并抛出错误 + throw error; + } } } }); ``` -为了让`refreshToken`请求顺利通过,需要通过元数据标识`authRole`为`refreshToken`。 +:::warning 注意 + +1. 为了让`refreshToken`请求顺利通过,需要通过元数据标识`authRole`为`refreshToken`。 +2. 如果 token 刷新失败必须抛出错误,阻止失败接口重试和等待接口继续请求。 + +::: > 了解更多元数据的信息,请前往[method 元数据](/tutorial/getting-started/method-metadata)。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/06-use-auto-request.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/07-use-auto-request.md similarity index 90% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/06-use-auto-request.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/07-use-auto-request.md index 380cabae6..a72c09412 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/06-use-auto-request.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/07-use-auto-request.md @@ -1,6 +1,6 @@ --- title: 自动拉取数据 -sidebar_position: 60 +sidebar_position: 70 --- import Tabs from '@theme/Tabs'; @@ -18,9 +18,9 @@ use hook ## 特性 -- ✨ 支持浏览器聚焦、tab 切换、网络重连、轮询请求等场景下拉取最新数据,可自定义配置监听类型; -- ✨ 支持请求节流,在短时间内多次触发只会发送 1 次请求; -- ✨ 支持自定义事件的监听函数,以适应非浏览器环境下的使用场景; +- 支持浏览器聚焦、tab 切换、网络重连、轮询请求等场景下拉取最新数据,可自定义配置监听类型; +- 支持请求节流,在短时间内多次触发只会发送 1 次请求; +- 支持自定义事件的监听函数,以适应非浏览器环境下的使用场景; ## 使用 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/07-action-delegation-middleware.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-action-delegation-middleware.md similarity index 98% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/07-action-delegation-middleware.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-action-delegation-middleware.md index d6bbf88e3..ec292250f 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/07-action-delegation-middleware.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-action-delegation-middleware.md @@ -1,6 +1,6 @@ --- title: 跨组件触发请求 -sidebar_position: 70 +sidebar_position: 80 --- import Tabs from '@theme/Tabs'; @@ -24,8 +24,8 @@ import TabItem from '@theme/TabItem'; ## 特性 -- ✨ 委托任意 alova 中的 use hook 的操作函数; -- ✨ 任意位置触发已委托的操作函数; +- 委托任意 alova 中的 use hook 的操作函数; +- 任意位置触发已委托的操作函数; ## 使用 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/02-virtual-data.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/02-virtual-data.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/02-virtual-data.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/02-virtual-data.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/03-start-silent-factory.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/03-start-silent-factory.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/03-start-silent-factory.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/03-start-silent-factory.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/04-conservative-request.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/04-conservative-request.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/04-conservative-request.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/04-conservative-request.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/05-modify-response.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/05-modify-response.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/05-modify-response.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/05-modify-response.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/06-request-retry.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/06-request-retry.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/06-request-retry.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/06-request-retry.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/07-data-compensation.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/07-data-compensation.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/07-data-compensation.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/07-data-compensation.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/08-edit-item.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/08-edit-item.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/08-edit-item.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/08-edit-item.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/09-what-more.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/09-what-more.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/09-what-more.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/09-what-more.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/README.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/README.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/README.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/README.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/_category_.json b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/_category_.json similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-sensorless-data-interaction/_category_.json rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/_category_.json diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-use-captcha.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/10-use-captcha.md similarity index 97% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-use-captcha.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/10-use-captcha.md index 8e7f06bf9..3b9b1852f 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-use-captcha.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/10-use-captcha.md @@ -1,6 +1,6 @@ --- title: 发送验证码 -sidebar_position: 90 +sidebar_position: 100 --- import Tabs from '@theme/Tabs'; @@ -22,9 +22,9 @@ use hook ## 特性 -- ✨ 验证码发送后自动开始倒计时; -- ✨ 自定义倒计时秒数; -- ✨ 验证码发送限制; +- 验证码发送后自动开始倒计时; +- 自定义倒计时秒数; +- 验证码发送限制; ## 使用 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/10-use-serial-request.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/11-use-serial-request.md similarity index 93% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/10-use-serial-request.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/11-use-serial-request.md index c9691fc0d..21166fe9b 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/10-use-serial-request.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/11-use-serial-request.md @@ -1,6 +1,6 @@ --- title: 串行请求的useRequest -sidebar_position: 100 +sidebar_position: 110 --- import Tabs from '@theme/Tabs'; @@ -18,9 +18,9 @@ use hook ## 特性 -- ✨ 更加简洁易用的串行方式; -- ✨ 统一的请求状态和回调函数; -- ✨ send 函数可触发多个请求串行执行; +- 更加简洁易用的串行方式; +- 统一的请求状态和回调函数; +- send 函数可触发多个请求串行执行; ## 示例 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/11-use-serial-watcher.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/12-use-serial-watcher.md similarity index 93% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/11-use-serial-watcher.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/12-use-serial-watcher.md index e1020519a..4a2d6a01d 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/11-use-serial-watcher.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/12-use-serial-watcher.md @@ -1,6 +1,6 @@ --- title: 串行请求的useWatcher -sidebar_position: 110 +sidebar_position: 120 --- import Tabs from '@theme/Tabs'; @@ -18,9 +18,9 @@ use hook ## 特性 -- ✨ 更加简洁易用的串行方式; -- ✨ 统一的请求状态和回调函数; -- ✨ 状态更新触发多个请求串行执行; +- 更加简洁易用的串行方式; +- 统一的请求状态和回调函数; +- 状态更新触发多个请求串行执行; ## 示例 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/12-use-retriable-request.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/13-use-retriable-request.md similarity index 98% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/12-use-retriable-request.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/13-use-retriable-request.md index c4b468d6c..3263875ba 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/12-use-retriable-request.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/13-use-retriable-request.md @@ -1,6 +1,6 @@ --- title: 请求重试策略 -sidebar_position: 120 +sidebar_position: 130 --- import Tabs from '@theme/Tabs'; @@ -22,9 +22,9 @@ use hook ## 特性 -- ✨ 自定义重试次数或按条件判断是否需要重试; -- ✨ 重试延迟机制; -- ✨ 手动停止重试; +- 自定义重试次数或按条件判断是否需要重试; +- 重试延迟机制; +- 手动停止重试; ## 使用 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/13-use-sse.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/14-use-sse.md similarity index 98% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/13-use-sse.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/14-use-sse.md index 3d780360f..defff9c79 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/13-use-sse.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/14-use-sse.md @@ -1,6 +1,6 @@ --- title: Server-sent events发送请求 -sidebar_position: 130 +sidebar_position: 140 --- import Tabs from '@theme/Tabs'; @@ -18,8 +18,8 @@ use hook ## 特性 -- ✨ 更加简洁易用的使用方式; -- ✨ 自动管理连接; +- 更加简洁易用的使用方式; +- 自动管理连接; ## 用法 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/14-use-breakpoint-uploader.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/15-use-breakpoint-uploader.md similarity index 69% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/14-use-breakpoint-uploader.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/15-use-breakpoint-uploader.md index 6b3821453..453a98a5a 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/14-use-breakpoint-uploader.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/15-use-breakpoint-uploader.md @@ -1,6 +1,6 @@ --- title: 断点续传策略 -sidebar_position: 140 +sidebar_position: 150 --- 敬请期待... diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/15-use-uploader.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/16-use-uploader.md similarity index 99% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/15-use-uploader.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/16-use-uploader.md index 99b66586e..70bf48f1d 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/15-use-uploader.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/16-use-uploader.md @@ -1,6 +1,6 @@ --- title: 通用的上传策略 -sidebar_position: 150 +sidebar_position: 160 --- :::warning diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/16-rate-limit-middleware.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/17-rate-limit-middleware.md similarity index 85% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/16-rate-limit-middleware.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/17-rate-limit-middleware.md index 637055c7a..b8c2486f6 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/16-rate-limit-middleware.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/17-rate-limit-middleware.md @@ -1,6 +1,6 @@ --- title: 请求速率限制 -sidebar_position: 160 +sidebar_position: 170 --- 设置每个间隔应立即执行的请求数,其他请求将自动延迟。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/01-retry.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/01-retry.md index 3961178f6..e3bfea830 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/01-retry.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/01-retry.md @@ -13,8 +13,8 @@ server hook ## 特性 -- ✨ 自定义重试次数或按条件判断是否需要重试; -- ✨ 重试延迟机制; +- 自定义重试次数或按条件判断是否需要重试; +- 重试延迟机制; ## 使用 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/01-mode.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/01-mode.md new file mode 100644 index 000000000..f759085c7 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/01-mode.md @@ -0,0 +1,329 @@ +--- +title: 缓存模式 +sidebar_position: 10 +--- + +import MemoryCache from '@site/example-links/MemoryCache'; +import StoragePlaceholder from '@site/example-links/StoragePlaceholder'; +import StorageRestore from '@site/example-links/StorageRestore'; + +缓存模式可在全局或请求级等不同粒度下设置。全局设置时,所有由相同 alova 实例创建的 method 实例都会继承该设置。 + +:::info 注意 + +是否使用缓存模式,以及使用哪种缓存模式需要根据场景而定,下面在单独介绍不同缓存模式时将会提及它们的使用场景。 + +::: + +## 内存模式(默认) + +内存模式属于单级缓存(L1 缓存)模式,默认将缓存放在内存中,是最常用的缓存模式。 + +```mermaid +flowchart LR + A[用户请求] --> B{检查L1缓存} + B -->|命中| C[返回数据] + B -->|未命中| F[请求API接口] + F --> G[更新L1缓存] + G --> C + C --> H[结束] + + style F stroke-width:8px +``` + +在默认情况下,GET 请求有 300000ms(5 分钟)的内存缓存时间,开发者也可以自定义设置缓存,请继续往下看。 + +### 客户端 + +在客户端中,刷新页面缓存即失效,内存模式一般用于解决短时间内(几分钟或几秒钟)频繁请求相同数据带来的性能消耗,例如当你在写 todo 详情页的时候,你可能会想到用户会频繁在 todo 列表中点击查看详情,如果用户重复查看某条详情时不再重复请求接口,并且能立即返回数据,提升了响应速度的同时也减小了服务器压力。此时我们就可以为某个 todo 详情 method 实例设置响应数据缓存。 + +```javascript +alovaInstance.GET('/todo/list', { + // ... + // highlight-start + cacheFor: { + // 设置缓存模式为内存模式 + mode: 'memory', + + // 单位为毫秒 + // 当设置为`Infinity`,表示数据永不过期,设置为0或负数时表示不缓存 + expire: 60 * 10 * 1000 + } + // highlight-end +}); +``` + +内存模式为默认模式,你可以这样简写 + +```javascript +alovaInstance.GET('/todo/list', { + // ... + // highlight-start + cacheFor: 60 * 10 * 1000 + // highlight-end +}); +``` + +### 服务端 + +在服务中通常在接口需要高访问频率和低延迟场景中使用,减轻下游服务器压力,使用方法与客户端相同。 + +在服务端中使用时需要注意: + +1. 过多的缓存会一直消耗服务器内存,你可以改用[lru-cache](https://www.npmjs.com/package/lru-cache)控制内存消耗。 +2. 如果需要在单机的 nodejs 集群环境中共享缓存,可以使用[@alova/psc](https://www.npmjs.com/package/@alova/psc)创建进程间共享内存的缓存方案。 + +```js +const { createPSCAdapter, NodeSyncAdapter } = require('@alova/psc'); +const { LRUCache } = require('lru-cache'); + +function lRUCache(options = {}) { + const cache = new LRUCache(options); + return { + set(key, value) { + return cache.set(key, value); + }, + + get(key) { + return cache.get(key); + }, + + remove(key) { + return cache.delete(key); + }, + + clear() { + return cache.clear(); + } + }; +} + +const alovaInstance = createAlova({ + baseURL: 'https://api.alovajs.dev', + + // 进程间共享缓存适配器 + l1Cache: createPSCAdapter( + NodeSyncAdapter(), + lRUCache({ + max: 1000, + ttl: 1000 * 60 * 10 + }) + ) +}); +``` + +内存缓存模式对应 l1 cache,在此我们更换了缓存适配器为进程间共享的 lru-cache,你也可以[自定义存储适配器](/tutorial/custom/custom-storage-adapter),例如当你只需要单级缓存时,你也可以直接将 l1 cache 设置为 redis 适配器。 + +## 恢复模式 + +恢复模式对应多级缓存,分别为 L1 和 L2 缓存。开启恢复模式后,响应数据会同时存储在 L1 和 L2 缓存中,当 L1 缓存失效时,会从 L2 缓存中读取数据,然后更新 L1 缓存,当 L2 缓存也失效时才会再次请求接口。 + +```mermaid +flowchart LR + A[用户请求] --> B{检查L1缓存} + B -->|命中| C[返回数据] + B -->|未命中| D{检查L2缓存} + D -->|命中| E[更新L1缓存] + E --> C + D -->|未命中| F[请求API接口] + F --> G[更新L2缓存] + G --> E + C --> H[结束] + + style F stroke-width:8px +``` + +### 客户端 + +在客户端中,当还未过期的缓存即使刷新页面缓存也不会失效,它一般用于一些需要服务端管理,但基本不变的数据,如每年的节假日具体日期有所不同,但不会再变动,这种场景下我们只需设置缓存过期时间为今年的最后一刻即可。 + +客户端中使用 alova 时默认使用`localStorage`作为 L2 存储适配器,你也可以[自定义存储适配器](/tutorial/custom/custom-storage-adapter)。 + +在 method 实例上设置: + +```javascript +const todoListGetter = alovaInstance.Get('/todo/list', { + // ... + // highlight-start + cacheFor: { + // 设置缓存模式为持久化模式 + mode: 'restore', + // 缓存时间 + expire: 60 * 10 * 1000 + } + // highlight-end +}); +``` + +#### 数据有变怎么办? + +当设置了恢复模式的 method 实例,可能由于接口数据变动,或前端处理响应数据的逻辑变动,此时需要在发布应用后让用户重新缓存变动后的数据,此时你可以通过`tag`属性设置缓存标签,每一份持久化数据都包含一个`tag`标识,当`tag`改变后原有的持久化数据将会失效,并重新获取新的数据,并用新的`tag`进行标识。 + +```javascript +const todoListGetter = alovaInstance.Get('/todo/list', { + // ... + cacheFor: { + mode: 'restore', + expire: 60 * 10 * 1000, + + // highlight-start + // 新增或修改tag参数,已缓存的数据将失效 + // 建议使用版本号的形式管理 + tag: 'v1' + // highlight-end + } +}); +``` + +### 服务端 + +一般用于多级缓存,使用 L1 作为内存缓存,L2 作为持久化缓存,例如 redis、memcached。 + +部分应用场景如下: + +1. 高访问频率和低延迟需求:例如热门新闻、商品详情,可以进一步减少网络开销,在网络不稳定时也保持更快的响应。 +2. 减轻下游服务器压力,例如有访问高峰期的服务,上层缓存可以有效减少对后端数据库和微服务的压力。 +3. 整合多个下游服务器的数据合并和处理,多个串行请求可能导致更长的响应时间,也可能因复杂的数据转换消耗性能,可将转换后的数据进行缓存。 +4. API 速率限制和计费,天气预报服务 API 每小时更新一次天气信息,地理位置数据 API 等。 + +在服务端使用 alova 时默认没有 L2 存储适配器,在[服务端 L2 存储实践](/tutorial/best-practice/l2-storage)中分别提供了文件存储适配器和 redis 适配器的实现。你也可以[自定义存储适配器](/tutorial/custom/custom-storage-adapter),例如将 MongoDB、mysql 等数据库作为响应数据的存储适配器。 + +:::warning 注意 + +当 request body 是**FormData**、**Blob**、**ArrayBuffer**、**URLSearchParams**、**ReadableStream**等特殊数据时,将会被认为你是有意图和服务端通信的,在这种情况下不会进行缓存。 + +::: + +## 设置 alova id + +每个缓存 key 中都带有 alova 实例的命名空间,格式如下: + +``` +cacheKey = [prefix][alova-id][method-json-string] +``` + +默认情况下[alova-id]按 alova 创建顺序递增,在服务端环境下使用了持久化缓存时,强烈建议设置 alova id 固定缓存 key 的命名空间,**这个尤为重要**,否则可能因为多个 alova 实例创建的顺序变动,而导致而无法匹配对应的缓存。 + +```js +const userAlova = createAlova({ + // ... + id: 'user' +}); + +const orderAlova = createAlova({ + // ... + id: 'order' +}); +``` + +## 全局设置缓存模式 + +如果你需要全局设置缓存模式,可以按如下方式做: + +```javascript +const alovaInstance = createAlova({ + // ... + // highlight-start + cacheFor: { + // 统一设置POST的缓存模式 + POST: { + mode: 'restore', + expire: 60 * 10 * 1000 + }, + // 统一设置HEAD请求的缓存模式 + HEAD: 60 * 10 * 1000 + } + // highlight-end +}); +``` + +此后,通过`alovaInstance`实例创建的 method 实例,都将默认使用这份缓存设置,同时也可以在 method 实例中覆盖它。 + +## 全局关闭缓存模式 + +如果在你的项目中不希望使用任何请求缓存,可以在全局将它关闭,如果希望只在特定的几个请求中使用,也可以全局关闭它,并在指定的 method 实例中设置即可。 + +```javascript +const alovaInstance = createAlova({ + // ... + // highlight-start + // 设置为null即可全局关闭全部请求缓存 + cacheFor: null + // highlight-end +}); +``` + +## 设置不同的过期时间 + +在恢复模式下,你还可以对 L1 和 L2 缓存设置不同的过期时间。将`expire`设置为函数,它将会在设置 L1 和 L2 缓存时分别被调用。 + +以下是设置单个请求的不同过期时间的示例。 + +```js +alovaInst.Get('/user/profile', { + // ... + cacheFor: { + mode: 'restore', + expire: ({ method, mode }) => { + // 在l1缓存设置5分钟缓存,在l2缓存设置1天缓存 + return mode === 'memory' ? 5 * 60 : 24 * 60 * 60; + } + } +}); +``` + +以下是全局设置 GET 请求的不同过期时间的示例,当 method 的元数据带有`setDiffExpire`标识时设置不同的过期时间。 + +```js +const alovaInst = createAlova({ + // ... + cacheFor: { + GET: { + mode: 'restore', + expire: ({ method, mode }) => { + if (method.meta.setDiffExpire) { + // 在l1缓存设置5分钟缓存,在l2缓存设置1天缓存 + return mode === 'memory' ? 5 * 60 : 24 * 60 * 60; + } + return 5 * 60; + } + } + } +}); +``` + +## 过期时间类型 + +过期时间有两种类型可供选择,分别为 **相对时间** 和 **绝对时间** + +### 相对时间 + +即在保存缓存数据时开始,过期的时长,以 **毫秒** 为单位,以上示例均为此类型。 + +```javascript +cacheFor: 60 * 10 * 1000; +``` + +```javascript +cacheFor: { + expire: 60 * 10 * 1000, +} +``` + +### 绝对时间 + +以一个具体时间点为过期时间,缓存将在设定的时间点过期 + +```javascript +cacheFor: new Date('2030-01-01'); +``` + +```javascript +cacheFor: { + expire: new Date('2030-01-01'); +} +``` + +## 响应自动维护说明 + +响应数据缓存的 key 是由 method 实例的请求方法(method)、请求地址(url)、请求头参数(headers)、url 参数(params)、请求体参数(requestBody)组合作为唯一标识,任意一个信息或位置不同都将被当做不同的 key,如果要自定义缓存 key,可以参考[自定义 method key](/tutorial/advanced/custom-method-key)。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/02-auto-invalidate.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/02-auto-invalidate.md similarity index 67% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/02-auto-invalidate.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/02-auto-invalidate.md index fbdf57836..4710eba6b 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/02-auto-invalidate.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/02-auto-invalidate.md @@ -1,91 +1,86 @@ ---- -title: 自动失效 -sidebar_position: 20 ---- - -有这样一个场景,当用户点开 todo 列表中的某一项,进入 todo 详情页并对它执行了编辑,此时我们希望上一页中的 todo 列表数据也更新为编辑后的内容,通常的做法是通过事件来触发上一页的内容更新,这样增加了维护成本。而`alova`提供了 3 种方式,可以很优雅地达到这个目的: - -1. 使用`useFetcher`立即重新请求最新的数据,它将在[数据拉取](/tutorial/advanced/use-fetcher)章节中讲解; -2. 更新缓存,这种方式将在后面的[缓存设置与查询](/tutorial/cache/set-and-query)章节中详细讲解; -3. 让这个响应缓存失效,当再次请求时将会因缓存失效而重新请求数据。这也是这个章节要讲解的内容。 - -自动失效缓存是在目标缓存中设置失效源规则,只要匹配规则都可以让目标缓存自动失效,这在很多时候省去了手动清除缓存的麻烦。 - -## 使用场景 - -当目标缓存与失效源是一对一或一对多时,设置自动失效规则会很方便。 - -```mermaid -flowchart - M1[method1失效源指向] --> T1[目标缓存] - M11[method1失效源指向] --> T2[目标缓存] - M2[method2失效源指向] --> T2[目标缓存] - MN[methodN失效源指向] --> T2[目标缓存] -``` - -## 设置自动失效规则 - -设置这个规则很简单,你可以在创建一个带缓存的 Method 实例时,为它设置`hitSource`参数即可。 - -### 失效源设置为 method 实例 - -以一个固定的 method 实例作为失效源,只要此 method 实例或它的克隆实例请求成功,目标缓存将被自动清除。 - -```javascript -alova.Get('/todo/1', { - // ... - hitSource: alova.Post('/todo', {}) -}); -``` - -### 通过 method 名称匹配失效源 - -和 method 匹配器一样,你可以在 hitSource 中指定 method 的名称来匹配失效源,多个失效源可以设置为同一个名称,带有这个名称的 method 实例请求成功时,目标缓存将被自动清除。 - -```javascript -const methodSubmitTodo = data => - alova.Post('/todo', data, { - name: 'submitTodo' - }); - -alova.Get('/todo/1', { - // ... - // 匹配method实例名称为submitTodo的失效源 - hitSource: 'submitTodo' -}); -``` - -### 通过 method 名称正则表达式匹配失效源 - -如果 method 实例名称不固定时,你可以在 hitSource 中指定一个正则表达式来匹配 method 名称,被匹配的 method 实例在请求成功时,目标缓存将被自动清除。 - -```javascript -const methodSubmitTodo = data => - alova.Post('/todo', data, { - name: 'prefix-submitTodo' - }); - -alova.Get('/todo/1', { - // ... - // 匹配method实例名称为prefix开头的所有实例 - hitSource: /^prefix/ -}); -``` - -### 组合设置失效源 - -如果你希望使用以上的多种规则匹配失效源,可以将 hitSource 指定为一个数组,数组项为以上 3 种规则的任意一种,满足数组任意一项规则的 method 实例将被匹配。 - -```javascript -alova.Get('/todo/1', { - // ... - // 满足数组中任意一项匹配规则的method实例请求成功时,此缓存将失效 - hitSource: [alova.Post('/todo', {}), 'submitTodo', /^prefix/] -}); -``` - -## hitSource 数据类型 - -```typescript -type hitSource = string | RegExp | Method | (string | RegExp | Method)[]; -``` +--- +title: 自动失效 +sidebar_position: 20 +--- + +自动失效缓存是在目标缓存中设置失效源 method 的匹配规则,当源 method 在请求成功后,将自动匹配并失效目标缓存,不需要再手动清除缓存。当目标缓存与失效源是一对一或一对多时,设置自动失效规则会很方便。 + +```mermaid +flowchart + M1[method1失效源指向] --> T1[目标缓存] + M11[method1失效源指向] --> T2[目标缓存] + M2[method2失效源指向] --> T2[目标缓存] + MN[methodN失效源指向] --> T2[目标缓存] +``` + +## 使用场景 + +1. 编辑列表项并提交成功后,自动对列表项缓存数据失效。 +2. 在服务端,例如用户个人信息、配置数据等更新后需要失效当前缓存。 + +## 设置自动失效规则 + +设置这个规则很简单,你可以在创建一个带缓存的 Method 实例时,为它设置`hitSource`参数即可。 + +### 失效源设置为 method 实例 + +以一个固定的 method 实例作为失效源,只要此 method 实例或它的克隆实例请求成功,目标缓存将被自动清除。 + +```javascript +alova.Get('/todo/1', { + // ... + hitSource: alova.Post('/todo', {}) +}); +``` + +### 通过 method 名称匹配失效源 + +和 method 匹配器一样,你可以在 hitSource 中指定 method 的名称来匹配失效源,多个失效源可以设置为同一个名称,带有这个名称的 method 实例请求成功时,目标缓存将被自动清除。 + +```javascript +const methodSubmitTodo = data => + alova.Post('/todo', data, { + name: 'submitTodo' + }); + +alova.Get('/todo/1', { + // ... + // 匹配method实例名称为submitTodo的失效源 + hitSource: 'submitTodo' +}); +``` + +### 通过 method 名称正则表达式匹配失效源 + +如果 method 实例名称不固定时,你可以在 hitSource 中指定一个正则表达式来匹配 method 名称,被匹配的 method 实例在请求成功时,目标缓存将被自动清除。 + +```javascript +const methodSubmitTodo = data => + alova.Post('/todo', data, { + name: 'prefix-submitTodo' + }); + +alova.Get('/todo/1', { + // ... + // 匹配method实例名称为prefix开头的所有实例 + hitSource: /^prefix/ +}); +``` + +### 组合设置失效源 + +如果你希望使用以上的多种规则匹配失效源,可以将 hitSource 指定为一个数组,数组项为以上 3 种规则的任意一种,满足数组任意一项规则的 method 实例将被匹配。 + +```javascript +alova.Get('/todo/1', { + // ... + // 满足数组中任意一项匹配规则的method实例请求成功时,此缓存将失效 + hitSource: [alova.Post('/todo', {}), 'submitTodo', /^prefix/] +}); +``` + +## hitSource 数据类型 + +```typescript +type hitSource = string | RegExp | Method | (string | RegExp | Method)[]; +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/03-manually-invalidate.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/03-manually-invalidate.md similarity index 71% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/03-manually-invalidate.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/03-manually-invalidate.md index 512468e41..5bbad031c 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/03-manually-invalidate.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/03-manually-invalidate.md @@ -1,102 +1,99 @@ ---- -title: 手动失效 -sidebar_position: 30 ---- - -通常,自动失效缓存更加简洁,并且推荐优先使用它来失效缓存,当自动失效缓存不满足需求时,你还可以通过调用`invalidateCache`来失效缓存。 - -## 使用 method 实例失效缓存 - -在 `invalidateCache` 函数中传入一个 method 实例,它将查找此实例下的缓存进行失效。 - -在下面的例子中,当提交成功后,将使这条 todo 详情数据缓存失效。 - -```javascript -// 获取 id 为 1 的 todo 详情数据 -const getTodoDetail = id => - alovaInstance.Get(`/todo/${id}`, { - localCache: 1000000 - }); -const { loading, data } = useRequest(getTodoDetail(1)); -``` - -```javascript -// 提交数据并让 id 为 1 的 todo 详情数据失效。 -const { - // ... - send, - onSuccess -} = useRequest(createTodoPoster, { immediate: false }); - -// highlight-start -// 提交成功后失效缓存 -onSuccess(() => { - invalidateCache(getTodoDetail(1)); -}); -// highlight-end - -const handleSubmit = () => { - send({ - title: 'new todo', - content: 'new todo content' - }); -}; -``` - -## 批量失效缓存 - -在下面的例子中,我们通过指定缓存的名称或名称的正则表达式来批量失效缓存。 - -```javascript -// 名称为todoList的method的缓存将失效 -invalidateCache('todoList'); - -// 名称符合以下正则表达式的method的缓存将失效 -invalidateCache(/^todoList/); -``` - -## 动态失效缓存 - -可能有时候你并不确定需要失效哪个缓存数据,我们可以使用 [method 匹配器](/tutorial/advanced/method-matcher) 来动态查找对应的 method 实例。以下例子展示了如何让名称为 todoList 的前 5 个 method 实例的缓存失效。 - -```javascript -const getTodoList = currentPage => { - return alovaInstance.Get('/todo/list', { - // highlight-start - // 先为method实例设置名称,用于在无法直接指定Method实例时,过滤出需要的Method实例 - name: 'todoList', - // highlight-end - params: { - currentPage, - pageSize: 10 - } - }); -}; - -const { - // ... - send, - onSuccess -} = useRequest(createTodoPoster, { immediate: false }); -// 提交成功后,固定使第一页的todo数据缓存失效 -onSuccess(() => { - // highlight-start - // 失效名称为todoList的前5个Method实例的缓存 - invalidateCache({ - name: 'todoList', - filter: (method, index, ary) => { - return index < 5; - } - }); - // highlight-end -}); -``` - -> 更多 method 匹配器的使用方法见 [method 匹配器](/tutorial/advanced/method-matcher) - -## 失效所有缓存 - -```javascript -// 当不传任何参数时,失效所有响应缓存 -invalidateCache(); -``` +--- +title: 手动失效 +sidebar_position: 30 +--- + +通常,自动失效缓存更加简洁,并且推荐优先使用它来失效缓存,当自动失效缓存不满足需求时,你还可以通过调用`invalidateCache`来失效缓存。 + +## 失效单个缓存 + +在 `invalidateCache` 函数中传入一个 method 实例,它将查找此实例下的缓存进行失效。 + +在下面的例子中,当提交成功后,将使这条 todo 详情数据缓存失效。 + +```javascript +// 获取 id 为 1 的 todo 详情数据 +const getTodoDetail = id => + alovaInstance.Get(`/todo/${id}`, { + cacheFor: 600 * 1000 + }); +const { loading, data } = useRequest(getTodoDetail(1)); +``` + +```javascript +// 提交数据并让 id 为 1 的 todo 详情数据失效。 +const { + // ... + send, + onSuccess +} = useRequest(createTodoPoster, { immediate: false }); + +// highlight-start +// 提交成功后失效缓存 +onSuccess(() => { + invalidateCache(getTodoDetail(1)); +}); +// highlight-end + +const handleSubmit = () => { + send({ + title: 'new todo', + content: 'new todo content' + }); +}; +``` + +## 失效多个缓存 + +你还可以传入 method 实例数组来失效多个缓存。 + +```javascript +invalidateCache([method1, method2, ...]); +``` + +## 动态失效缓存 + +可能有时候你并不确定需要失效哪个缓存数据,我们可以使用 [method 快照匹配器](/tutorial/advanced/method-matcher) 来动态查找对应的 method 实例。以下例子展示了如何让名称为 todoList 的前 5 个 method 实例的缓存失效。 + +```javascript +const getTodoList = currentPage => { + return alovaInstance.Get('/todo/list', { + // highlight-start + // 先为method实例设置名称,用于在无法直接指定Method实例时,过滤出需要的Method实例 + name: 'todoList', + // highlight-end + params: { + currentPage, + pageSize: 10 + } + }); +}; + +const { + // ... + send, + onSuccess +} = useRequest(createTodoPoster, { immediate: false }); +// 提交成功后,固定使第一页的todo数据缓存失效 +onSuccess(() => { + // highlight-start + // 失效名称为todoList的前5个Method实例的缓存 + const matchedMethods = alovaInstance.snapshots.match({ + name: 'todoList', + filter: (method, index, ary) => { + return index < 5; + } + }); + invalidateCache(matchedMethods); + // highlight-end +}); +``` + +> 更多 method 匹配器的使用方法见 [method 快照匹配器](/tutorial/advanced/method-matcher) + +## 失效所有缓存 + +```javascript +// 当不传任何参数时,失效所有响应缓存 +invalidateCache(); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/04-force-request.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/04-force-request.md new file mode 100644 index 000000000..7b86c2724 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/04-force-request.md @@ -0,0 +1,18 @@ +--- +title: 强制请求 +sidebar_position: 40 +--- + +强制请求是指绕过缓存的检查触发请求发送的机制,当需要在一定条件下获取最新的数据时很有用。 + +## 在 method 中强制请求 + +通过调用 method 实例的 send 函数,并传入 true 来强制请求。 + +```javascript +const response = await alovaInstance.Get('/api/user').send(true); +``` + +## 在 useHook 中强制请求 + +请前往[自动管理请求状态-强制请求](/tutorial/client/use-request#强制请求)中查看详情。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/05-set-and-query.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/05-set-and-query.md new file mode 100644 index 000000000..06af8d0d5 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/05-set-and-query.md @@ -0,0 +1,89 @@ +--- +title: 更新与查找缓存 +sidebar_position: 50 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +缓存也支持更新和查找,在[缓存模式](/tutorial/cache/mode)中我们提到过,每份缓存数据是以发送请求的 method 实例作为 key 进行保存的,因此在手动更新缓存时也将使用 method 实例来查找对应的缓存数据。 + +## 更新缓存 + +### 更新静态值 + +使用`setCache`更新缓存数据,它的第一个参数为 method 实例,第二个为新的缓存数据,并返回一个 Promise 实例表示是否执行完毕。 + +```js +import { setCache } from 'alova'; +await setCache(todoDetail(id), detailedData); +``` + +### 动态更新缓存 + +你也可以在`setCache`中传入一个回调函数来动态计算缓存数据,并返回需要更新的缓存数据,如果函数中返回了`undefined`则会中止缓存更新。 + +```javascript +await setCache(todoDetail(id), oldCache => { + if (!oldCache.allowUpdate) { + return; // 返回undefined中止缓存更新 + } + + // 返回需要缓存的数据 + return { + ...oldCache, + name: 'new name' + }; +}); +``` + +### 更新策略 + +当传入的 method 设置了多级缓存时,默认会同时更新 L1 缓存和 L2 缓存,你可以通过`policy`控制单独更新指定的缓存。 + +```js +await setCache(todoDetail(id), detailedData, { + /** + * 缓存策略。 + * - l1:仅更新 l1 缓存。 + * - l2:仅更新 l2 缓存。 + * - all:更新 l1 缓存并更新 l2 缓存(方法缓存模式需要为 'restore')。 + * @default 'all' + */ + policy: 'l1' +}); +``` + +## 查询缓存 + +通过`queryCache`方法来查询缓存数据,它接收 method 实例。 + +```javascript +import { queryCache } from 'alova'; + +const cachedData = await queryCache(getTodoListByDate('2022-10-01')); +``` + +你也可以通过 [method 快照匹配器](/tutorial/advanced/method-matcher) 动态查找 method 实例。 + +```javascript +const lastMethod = alovaInstance.snapshots.match('todoList', true); +const cachedData = lastMethod ? await queryCache(lastMethod) : undefined; +``` + +### 查询策略 + +当传入的 method 设置了多级缓存时,默认会先查询 L1 缓存,再查询 L2 缓存,你可以通过`policy`控制只查询指定的缓存。 + +```js +const cachedData = await queryCache(getTodoListByDate('2022-10-01'), { + /** + * 缓存策略。 + * - l1:仅查询 l1 缓存。 + * - l2:仅查询 l2 缓存。 + * - all:查询 l1 缓存并查询 l2 缓存(方法缓存模式需要为 'restore')。 + * @default 'all' + */ + policy: 'l1' +}); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/06-controlled-cache.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/06-controlled-cache.md new file mode 100644 index 000000000..af56baf26 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/06-controlled-cache.md @@ -0,0 +1,55 @@ +--- +title: 受控的缓存 +sidebar_position: 60 +--- + +在发送一个请求时,默认会优先匹配响应缓存,在某些情况下,你可能需要使用`IndexedDB`作为缓存管理方案,使用自定义的`IndexedDB`适配器,但这会让所有请求都使用它作为存储方案,而受控的缓存则可以让你从单个请求上控制自定义的缓存。 + +使用受控缓存也很简单,可以在 method 中的 `cacheFor` 设置为异步或同步函数,在这个函数中返回的数据将作为缓存数据。例如在`IndexedDB`中读取数据。 + +```javascript +const getFile = fileName => + alovaInstance.GET(`/file/${fileName}`, { + // 受控缓存函数支持异步和同步函数 + cacheFor() { + return new Promise((resolve, reject) => { + const tx = db.transaction(['files']); + const getRequest = tx.objectStore('files').get(fileName); + getRequest.onsuccess = resolve; + getRequest.onerror = reject; + }); + } + }); +``` + +你也可以在`cacheFor`中返回 `undefined` 或不返回任何数据,继续发送请求,这在自定义管理缓存时未命中缓存的情况下很有用。 + +## 使用 transformData 设置缓存 + +由于 `transformData` 函数具有以下两个特性: + +- 只有在响应时才被触发,而命中响应缓存时不会触发; +- 支持异步函数; + +因此,你还可以配合它保存自定义的缓存,例如以文件为响应数据的缓存场景下,可以配合 IndexedDB 进行文件数据的缓存。 + +```javascript +const fileGetter = alovaInstance.Get('/file/file_name', { + // 使用IndexedDB缓存文件 + async transformData(fileBlob) { + await new Promise((resolve, reject) => { + const tx = db.transaction(['files'], 'readwrite'); + const putRequest = tx.objectStore('files').put({ + file: fileBlob + }); + putRequest.onsuccess = resolve; + putRequest.onerror = reject; + }); + return fileBlob; + } +}); +``` + +## 注意事项 + +在 usehooks 中使用时,在`cacheFor`函数中抛出错误将会触发`onError`,使用 method 实例直接发起请求时,promise 将会被 reject。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/README.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/README.md new file mode 100644 index 000000000..cdeb5c069 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/README.md @@ -0,0 +1,11 @@ +--- +title: 响应缓存详解 +--- + +响应缓存可以让我们重复利用响应数据,避免重复发送请求,从而减少响应时间和节约服务端资源。alova 支持多级缓存和**内存模式、恢复模式**两种缓存模式,选择适合你的使用即可。 + +在客户端,响应缓存可以让用户立即看到数据,没有任何延迟。在服务端,可以短时缓存响应数据来解决热 key 问题。 + +此外,使用缓存操作 API,你还可以自由添加、修改和删除缓存,以及自定义缓存匹配规则。 + +接下来,让我们从缓存模式开始理解吧! diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/_category_.json b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/_category_.json new file mode 100644 index 000000000..32760dc1b --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/_category_.json @@ -0,0 +1,3 @@ +{ + "label": "缓存详解" +} diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/02-update-across-components.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/01-update-across-components.md similarity index 97% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/02-update-across-components.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/01-update-across-components.md index 005dba6f1..5e2df9454 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/02-update-across-components.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/01-update-across-components.md @@ -1,102 +1,108 @@ ---- -title: 跨组件更新状态 -sidebar_position: 20 ---- - -有这个一个场景,当用户点开 todo 列表中的某一项,进入 todo 详情页并对它执行了编辑,此时我们希望上一页中的 todo 列表数据,在不重新情况的情况下也更新为编辑后的内容,`useFetcher`就不再适用了。 - -此时可以使用`updateState`来更新任意模块/页面下的已存在的响应状态,它可以查找并修改其他模块内的响应式状态。 - -[这里有个`updateState`的 示例](/tutorial/example/update-state) - -## 使用 method 实例查找响应状态 - -当确定更新的响应状态对应的 method 实例时,你可以在`updateState`中传入此 method 实例,它将查找这个实例下是否存在对应的响应状态,并在回调函数中提供给你进行修改,最后将修改后的数据返回即可。 - -```javascript -import { updateState } from 'alova'; - -// 正在编辑的todo项 -const editingTodo = { - id: 1, - title: 'todo1', - time: '09:00' -}; - -const { send, onSuccess } = useRequest(createTodoPoster, { immediate: false }); -onSuccess(() => { - // highlight-start - // 固定修改第一页的todo数据数据 - // updateState将返回是否更新成功 - const updated = updateState(getTodoList(1), todoList => { - return todoList.map(item => { - if (item.id === editingTodo.id) { - return { - ...item, - ...editingTodo - }; - } - return item; - }); - }); - // highlight-end -}); -``` - -:::warning 注意 - -1. 通过`updateState`更新状态时,如果检测到缓存(内存缓存和持久化缓存)也将会更新新的数据更新缓存。 -2. 只有当使用 useRequest、useWatcher 发起过请求时,alova 才会管理 hook 返回的状态,原因是响应状态是通过一个 Method 实例来生成 key 并保存的,但在未发起请求时 Method 实例内的 url、params、query、headers 等参数都还不确定。 - -::: - -## 动态更新响应状态 - -可能有时候你并不确定需要更新 method 下的响应状态,但却知道以什么方式来找到需要失效的缓存数据,我们可以使用 [method 匹配器](/tutorial/advanced/method-matcher) 来动态查找对应的 method 实例。以下例子展示了为名称为 todoList 的 method 实例对应的列表添加一条数据。 - -```javascript -updateState('todoList', todoListRaw => { - todoListRaw.push({ - title: 'new todo', - time: '10:00' - }); - return todoListRaw; -}); -``` - -关于 [method 匹配器](/tutorial/advanced/method-matcher) 将在后面的章节中详细介绍。 - -## 监听匹配事件 - -在动态更新响应状态时,有时候你可能想要匹配到 method 实例时做一些处理,或者想要获取匹配的 method 实例,`updateState`还可以传入第三个参数来设置匹配事件来达到这些目的。 - -```javascript -updateState( - 'todoList', - todoListRaw => { - // ... - }, - { - // 匹配到method实例时调用,参数为匹配到的method实例 - onMatch: method => { - // ... - } - } -); -``` - -:::warning ⚠️ 请确保组件未销毁 - -`updateState`默认会查找由 alova 的 useHooks 发送请求时所创建的响应状态,但由于防止内存溢出,一个组件的销毁同时也会回收它内部创建的所有状态,因此在使用`updateState`时请确保你希望更新的响应状态对应的容器组件未被销毁,否则将无法查找到对应的响应状态而导致更新失败。 - -这个问题常常出现在跨页面更新状态,我们容易忽略的是,在默认情况下,当页面跳转时上一个页面已被销毁,因此,如果你希望跨页面更新状态,这里有两个建议: - -1. 将页面组件持久化,以保证被更新的状态还可以被查找到; -2. 使用 [setCache](/tutorial/cache/set-and-query) 替代`updateState`,其原理是,当上一个页面的请求存在缓存时,更新它的缓存以保证再次创建页面时,所触发的请求可以命中更新后的缓存,达到同样的效果。 - -::: - -## 注意事项 - -1. 在实际使用中,不管是使用`useRequest`还是`useWatcher`发送请求时,你都可以调用`send`函数来指定不同参数重复发送请求,这些 use hook 返回的响应状态会被多个 method 实例引用,因此你可以选择任意一个 method 实例都可以匹配到同一个响应状态值; -2. 当动态查找更新响应状态时,method 匹配器找到了多个 method 实例,将会以第一个实例为准; +--- +title: 跨组件更新状态 +sidebar_position: 20 +--- + +:::info 使用范围 + +客户端 useHook + +::: + +有这个一个场景,当用户点开 todo 列表中的某一项,进入 todo 详情页并对它执行了编辑,此时我们希望上一页中的 todo 列表数据,在不重新情况的情况下也更新为编辑后的内容,`useFetcher`就不再适用了。 + +此时可以使用`updateState`来更新任意模块/页面下的已存在的响应状态,它可以查找并修改其他模块内的响应式状态。 + +[这里有个`updateState`的 示例](/tutorial/example/update-state) + +## 使用 method 实例查找响应状态 + +当确定更新的响应状态对应的 method 实例时,你可以在`updateState`中传入此 method 实例,它将查找这个实例下是否存在对应的响应状态,并在回调函数中提供给你进行修改,最后将修改后的数据返回即可。 + +```javascript +import { updateState } from 'alova'; + +// 正在编辑的todo项 +const editingTodo = { + id: 1, + title: 'todo1', + time: '09:00' +}; + +const { send, onSuccess } = useRequest(createTodoPoster, { immediate: false }); +onSuccess(() => { + // highlight-start + // 固定修改第一页的todo数据数据 + // updateState将返回是否更新成功 + const updated = updateState(getTodoList(1), todoList => { + return todoList.map(item => { + if (item.id === editingTodo.id) { + return { + ...item, + ...editingTodo + }; + } + return item; + }); + }); + // highlight-end +}); +``` + +:::warning 注意 + +1. 通过`updateState`更新状态时,如果检测到缓存(内存缓存和持久化缓存)也将会更新新的数据更新缓存。 +2. 只有当使用 useRequest、useWatcher 发起过请求时,alova 才会管理 hook 返回的状态,原因是响应状态是通过一个 Method 实例来生成 key 并保存的,但在未发起请求时 Method 实例内的 url、params、query、headers 等参数都还不确定。 + +::: + +## 动态更新响应状态 + +可能有时候你并不确定需要更新 method 下的响应状态,但却知道以什么方式来找到需要失效的缓存数据,我们可以使用 [method 匹配器](/tutorial/advanced/method-matcher) 来动态查找对应的 method 实例。以下例子展示了为名称为 todoList 的 method 实例对应的列表添加一条数据。 + +```javascript +updateState('todoList', todoListRaw => { + todoListRaw.push({ + title: 'new todo', + time: '10:00' + }); + return todoListRaw; +}); +``` + +关于 [method 匹配器](/tutorial/advanced/method-matcher) 将在后面的章节中详细介绍。 + +## 监听匹配事件 + +在动态更新响应状态时,有时候你可能想要匹配到 method 实例时做一些处理,或者想要获取匹配的 method 实例,`updateState`还可以传入第三个参数来设置匹配事件来达到这些目的。 + +```javascript +updateState( + 'todoList', + todoListRaw => { + // ... + }, + { + // 匹配到method实例时调用,参数为匹配到的method实例 + onMatch: method => { + // ... + } + } +); +``` + +:::warning ⚠️ 请确保组件未销毁 + +`updateState`默认会查找由 alova 的 useHooks 发送请求时所创建的响应状态,但由于防止内存溢出,一个组件的销毁同时也会回收它内部创建的所有状态,因此在使用`updateState`时请确保你希望更新的响应状态对应的容器组件未被销毁,否则将无法查找到对应的响应状态而导致更新失败。 + +这个问题常常出现在跨页面更新状态,我们容易忽略的是,在默认情况下,当页面跳转时上一个页面已被销毁,因此,如果你希望跨页面更新状态,这里有两个建议: + +1. 将页面组件持久化,以保证被更新的状态还可以被查找到; +2. 使用 [setCache](/tutorial/cache/set-and-query) 替代`updateState`,其原理是,当上一个页面的请求存在缓存时,更新它的缓存以保证再次创建页面时,所触发的请求可以命中更新后的缓存,达到同样的效果。 + +::: + +## 注意事项 + +1. 在实际使用中,不管是使用`useRequest`还是`useWatcher`发送请求时,你都可以调用`send`函数来指定不同参数重复发送请求,这些 use hook 返回的响应状态会被多个 method 实例引用,因此你可以选择任意一个 method 实例都可以匹配到同一个响应状态值; +2. 当动态查找更新响应状态时,method 匹配器找到了多个 method 实例,将会以第一个实例为准; diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/02-method-matcher.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/02-method-matcher.md new file mode 100644 index 000000000..940f0e952 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/02-method-matcher.md @@ -0,0 +1,88 @@ +--- +title: method匹配器 +sidebar_position: 30 +--- + +:::info 使用范围 + +全范围 + +::: + +method 快照匹配器是一个在已请求的 method 快照列表中动态查找 method 实例的方法,每个 alova 实例都有独立的快照空间。它一般用于,开发者不确定具体使用哪个 method 时,可以使用 method 快照匹配器按一定的规则查找。 + +它一般与以下 5 个需要使用 method 实例的函数中使用。 + +1. [setCache](/tutorial/cache/set-and-query) +2. [queryCache](/tutorial/cache/set-and-query) +3. [invalidateCache](/tutorial/cache/manually-invalidate) +4. [updateState](/tutorial/advanced/update-across-components) +5. [useFetcher.fetch](/tutorial/client/use-fetcher) + +## 匹配规则 + +当使用 method 实例请求时将被作为快照保存起来,method 快照匹配器依据 method 实例设置的`name`属性在这些 method 快照中进行查找,多个匹配器允许设置相同的`name`。 + +## 通过 name 属性匹配 + +通过传入完整的实例名称进行匹配,默认返回匹配数组。 + +```javascript +// 每次调用getTodoList时都会生成一个新的method实例,它们的name是相同的 +const getTodoList = currentPage => + alova.Get('/todo/list', { + name: 'todoList' + // ... + }); + +// 匹配name为`todoList`的所有Method实例 +const matchedMethods = alova.snaptshots.match('todoList'); +``` + +## 通过正则表达式匹配 + +通过传入正则表达式进行匹配,method 实例的 name 符合正则表达式的都将匹配,它的结果也是一个数组。 + +```javascript +// 匹配name为以`todo`开头的所有Method实例 +const matchedMethods = alova.snaptshots.match(/^todo/); +``` + +## 过滤匹配结果 + +通过指定`filter`来进一步过滤不满足条件的 method 实例,filter 函数使用与 Array.prototype.filter 相同,返回 true 表示匹配成功,返回 false 表示失败,详见上面的类型声明。 + +让我们来看几个例子。 + +**让特定名称的最后一个 method 实例的缓存失效** + +```javascript +const matchedMethods = alova.snaptshots.match({ + name: 'todoList', + filter: (method, index, methods) => index === methods.length - 1 +}); +``` + +## 匹配单个 method 实例 + +你还可以将`match`函数的第二个函数设置为`true`返回匹配结果的第一项,未匹配到时返回`undefined`。 + +```js +const matchedSingleMethod = alova.snaptshots.match(/^todo/, true); +``` + +## 限制实例快照 + +默认保存 1000 个 method 实例快照,否则在频繁的请求场景下可能导致内存溢出,你也可以根据需要调整限制数量。 + +```js +import { globalConfig } from 'alova'; + +const alovaInstance = createAlova({ + // ... + // 限制保存500个实例快照 + snapshots: 500 +}); +``` + +当设置为 0 时将不再保存实例快照,此时也将无法使用 method 快照匹配器。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/03-method-matcher.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/03-method-matcher.md deleted file mode 100644 index 50ceec7ee..000000000 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/03-method-matcher.md +++ /dev/null @@ -1,141 +0,0 @@ ---- -title: method匹配器 -sidebar_position: 30 ---- - -method 匹配器是一个在已请求的 method 快照列表中动态查找 method 实例的方法。它一般用于,开发者不确定具体使用哪个 method 时,可以使用 method 匹配器按一定的规则查找。 - -## 匹配规则 - -当使用 method 实例请求时,它将被作为快照保存起来,method 匹配器依据 method 实例设置的`name`属性在这些 method 快照中进行查找,多个匹配器允许设置相同的`name`。 - -method 实例匹配类型如下: - -```typescript -type MethodFilter = - | string - | RegExp - | { - name: string | RegExp; - filter: (method: Method, index: number, methods: Method[]) => boolean; - - // 可选参数,如果传入alova对象则只匹配此alova所创建的Method实例,否则匹配所有alova实例的Method实例 - alova?: Alova; - }; -``` - -在以下函数中都可以使用 method 实例匹配器。 - -- [setCache](/tutorial/cache/set-and-query) -- [queryCache](/tutorial/cache/set-and-query) -- [invalidateCache](/tutorial/cache/manually-invalidate) -- [updateState](/tutorial/advanced/update-across-components) -- [useFetcher.fetch](/tutorial/advanced/use-fetcher) - -## 通过 name 属性匹配 - -通过传入完整的实例名称进行匹配,它的匹配结果是一个数组。 - -```javascript -// 每次调用getTodoList时都会生成一个新的method实例,它们的name是相同的 -const getTodoList = currentPage => - alova.Get('/todo/list', { - // highlight-start - name: 'todoList' - // highlight-end - // ... - }); - -// 以下表示让name为'todoList'的所有Method实例的缓存失效 -invalidateCache('todoList'); -``` - -## 通过正则表达式匹配 - -通过传入正则表达式进行匹配,method 实例的 name 符合正则表达式的都将匹配,它的结果也是一个数组。 - -```javascript -// 以下表示让name为以'todo'开头的所有Method实例的缓存失效 -invalidateCache(/^todo/); -``` - -## 过滤匹配结果 - -通过指定`filter`来进一步过滤不满足条件的 method 实例,filter 函数使用与 Array.prototype.filter 相同,返回 true 表示匹配成功,返回 false 表示失败,详见上面的类型声明。 - -让我们来看几个例子。 - -**让特定名称的最后一个 method 实例的缓存失效** - -```javascript -invalidateCache({ - name: 'todoList', - filter: (method, index, methods) => index === methods.length - 1 -}); -``` - -**设置由`alovaInst`创建的,特定名称的最后一个 method 实例的缓存** - -```javascript -setCache( - { - name: /^todo/, - filter: (method, index, methods) => index === methods.length - 1, - - // 如果传了alova参数,那么只匹配由此alova实例创建的Method实例,否则会在所有Method实例中匹配 - alova: alovaInst - }, - newCache -); -``` - -**重新拉取 todo 列表最后一次请求的数据** - -```javascript -const { fetch } = useFetcher(); -fetch({ - name: 'todoList', - filter: (method, index, methods) => index === methods.length - 1 -}); -``` - -> alova 参数可以进一步缩小匹配范围。 - -## 在不同函数中使用的区别 - -### invalidateCache - -应用所有匹配的 Method 实例集合,即失效所有匹配的 Method 实例对应的缓存。 - -### setCache - -应用所有匹配的 Method 实例集合,当传入静态数据时所有的 Method 实例缓存设置为相同值,传入回调函数时将循环调用此函数,并将返回值作为缓存数据。 - -### updateState - -应用第一个匹配的 Method 实例。 - -### fetch - -应用第一个匹配的 Method 实例,即只会拉取一次数据。 - -## 限制实例快照 - -`[v2.20.0+]`默认情况下,会保存 1000 个 method 实例快照,否则在频繁的请求场景下可能导致内存溢出,你也可以根据需要调整限制数量。 - -```js -import { globalConfig } from 'alova'; - -globalConfig({ - // 限制保存500个实例快照 - limitSnapshots: 500 -}); -``` - -当设置为 0 时将不再保存实例快照,此时也将无法使用 method 匹配器。 - -```js -globalConfig({ - limitSnapshots: 0 -}); -``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/04-middleware.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/04-middleware.md index e6870112a..eb4065c25 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/04-middleware.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/04-middleware.md @@ -1,405 +1,269 @@ ---- -title: 请求中间件 -sidebar_position: 40 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -请求中间件是一个异步函数,它提供了强大的,几乎能控制一个请求的所有行为的能力。如果你只是使用 alova,那你应该很可能不需要使用请求中间件,因为它主要用于完成自定义的请求策略,无论简单还是复杂的请求策略,可能你都会用上它,接下来我们看下它到底有什么神通。 - -## 中间件函数 - -请求中间件是一个异步函数,你可以在`useRequest`、`useWatcher`、`useFetcher`中定义请求中间件。以下是一个简单的请求中间件,它在请求前和请求后分别打印了一些信息,没有改变任何请求行为。 - -```javascript -useRequest(todoList, { - async middleware(_, next) { - console.log('before request'); - await next(); - console.log('after requeste'); - } -}); -``` - -这里有几点你需要知道的,有关`next`函数调用的问题,这个函数也是一个异步函数,调用它可以继续发送请求,此时将会把 _loading_ 状态设置为 true,然后发送请求。next 的返回值是带有响应数据的 Promise 实例,你可以在中间件函数中操纵返回值。 - -## 控制响应数据 - -中间件函数的返回值将作为本次请求的响应数据参与后续的处理,如果中间件没有返回任何数据但调用了 `next`,则会将本次请求的响应数据参与后续处理。 - -```javascript -// 将会以修改后的result作为响应数据 -useRequest(todoList, { - async middleware(_, next) { - const result = await next(); - result.code = 500; - return result; - } -}); - -// 将会以本次请求的响应数据参与后续处理 -useRequest(todoList, { - async middleware(_, next) { - await next(); - } -}); - -// 将会以字符串abc作为响应数据 -useRequest(todoList, { - async middleware(_, next) { - await next(); - return 'abc'; - } -}); -``` - -这里还有一个特例,当既没有调用 `next`,又没有返回值时,将不再执行后续的处理,这表示*onSuccess*、_onError_、*onComplete*响应事件不会被触发。 - -```javascript -useRequest(todoList, { - async middleware() {} -}); -``` - -## 更改请求 - -有时候你想要更改请求,此时可以在 `next` 中指定另一个 method 实例,在发送请求时就会将这个 method 中的信息进行请求,同时你还可以通过 `next` 设置是否强制请求来穿透缓存,这也很简单。 - -```javascript -useRequest(todoList, { - async middleware(_, next) { - await next({ - // 更改请求的method实例 - method: newMethodInstance, - - // 本次是否强制请求 - force: true - }); - } -}); -``` - -## 控制错误 - -### 捕获错误 - -在中间件中,可以捕获 `next` 中产生的请求错误,捕获后,全局的`onError`钩子不再触发。 - -```javascript -useRequest(todoList, { - async middleware(_, next) { - try { - await next(); - } catch (e) { - console.error('捕获到错误', e); - } - } -}); -``` - -### 抛出错误 - -当然,也可以在中间件中抛出一个自定义错误,即使请求正常也将会进入请求错误的流程。 - -```javascript -// 未发出请求,同时还会触发全局的以及请求级的onError,如果是通过`method.send`发送的请求将返回reject的promise实例 -useRequest(todoList, { - async middleware(_, next) { - throw new Error('error on before request'); - await next(); - } -}); - -// 请求成功后,将触发全局的以及请求级的onError,如果是通过`method.send`发送的请求将返回reject的promise实例 -useRequest(todoList, { - async middleware(_, next) { - await next(); - throw new Error('error on after request'); - } -}); -``` - -## 控制响应延迟 - -在中间件中我们可以延迟响应,也可以提前响应,在提前的情况下,虽然获取不到响应数据,但可以返回一些其他的数据作为响应数据参与后续的处理。 - -```javascript -// 延迟1秒响应 -useRequest(todoList, { - async middleware(_, next) { - await new Promise(resolve => { - setTimeout(resolve, 1000); - }); - return next(); - } -}); - -// 立即响应,并使用字符串abc作为响应数据 -useRequest(todoList, { - async middleware(_, next) { - return 'abc'; - } -}); -``` - -## 不止于此 - -**至此,我们所提及的都是中间件的第二个参数 `next` 的使用,那第一个参数是做什么的呢?** - -中间件第一个参数中包含了本次请求的一些信息,以及对`loading`、`data`和`onSuccess`等 useHook 中返回的状态和事件的控制函数。我们接着往下看! - -## 包含的请求信息 - - - - -以下为 useRequest 和 useWatcher 的中间件所包含的请求信息 - -```javascript -async function alovaFrontMiddleware(context, next) { - // 本次请求的method实例 - context.method; - - // send函数发送的参数数组,默认为[] - context.sendArgs; - - // 本次请求命中的缓存数据 - context.cachedResponse; - - // useHook的配置集合 - context.config; - - // useHook返回的各项状态,包含以下属性 - // loading、data、error、downloading、uploading,以及通过managedStates管理的额外状态 - context.frontStates; - // ... -} -``` - - - - -以下为 useFetcher 的中间件所包含的请求信息 - -```javascript -async function alovaFetcherMiddleware(context, next) { - // 本次请求的method实例 - context.method; - - // 由useFetcher的fetch传入的参数组,默认为[] - context.fetchArgs; - - // 本次请求命中的缓存数据 - context.cachedResponse; - - // useHook的配置集合 - context.config; - - // useHook返回的各项状态,包含以下属性 - // fetching、error、downloading、uploading - context.fetchStates; - // ... -} -``` - - - - -接下来,我们再来看看有哪些控制能力。 - -## 修改响应式数据 - -使用`context.update`修改响应式数据。 - - - - -```javascript -async function alovaFrontMiddleware(context, next) { - context.update({ - // 提前修改loading状态为true - loading: true, - - // 修改data值,如设置自定义的初始化数据 - data: { - /* ... */ - } - }); - // ... -} -``` - - - - -```javascript -async function alovaFetcherMiddleware(context, next) { - context.update({ - // 提前修改fetching状态为true - fetching: true, - - // 修改error的值 - error: new Error('custom midleware error') - }); - // ... -} -``` - - - - -## 装饰事件 - -你还可以在中间件中装饰*onSuccess*、_onError_、*onComplete*回调函数,让它们变得更丰富,例如改变回调函数的参数,又或者接收回调函数的返回值,实现更多的功能。 - -你可以使用`decorateSuccess`、`decorateError`、`decorateComplete`函数来装饰回调函数。下面将成功回调作为示例,它装饰了 3 处地方: - -1. 为 event 对象新增了`custom`属性; -2. 为成功回调函数新增了第二个参数,值为`extra data`; -3. 接收第二个成功回调函数的值,并打印它; - -```javascript -const { onSuccess } = useRequest(todoList, { - // ... - async middleware(context, next) { - // 装饰成功回调函数,以下函数参数解释: - // handler: 绑定的回调函数 - // event: 回调函数对应的事件对象 - // index: 回调函数下标,表示当前正在执行第几个回调函数 - // length: 回调函数绑定个数 - context.decorateSuccess((handler, event, index, length) => { - event.custom = 1; - const received = handler(event, 'extra data'); - if (index === 1) { - console.log(`接收到第${index + 1}个回调函数的返回值:`, received); - // [打印信息] 接收到第2个回调函数的返回值:I'm second handler - } - }); - // ... - } -}); -onSuccess((event, extra) => { - console.log(event.custom); // 1 - console.log(extra); // extra data -}); -onSuccess((event, extra) => { - return "I'm second handler"; -}); -``` - -`decorateError`、`decorateComplete`的用法与`decorateSuccess`相同。 - -## 中断或重复发送请求 - -在中间件中还可以接收到 use hooks 返回的`abort`和`send`函数(useFetcher 中为`fetch`),你还可以在触发一次请求意图时,发送多个请求。 - -典型的使用例子是请求重试,发送一次请求后如果请求失败将自动按一定策略再次请求,重试成功后再触发`onSuccess`。以下为简单的请求重试示例代码。 - - - - -```javascript -async function alovaFrontMiddleware(context, next) { - return next().catch(error => { - if (needRetry) { - setTimeout(() => { - context.send(...context.sendArgs); - }, retryDelay); - } - return Promise.reject(error); - }); -} -``` - - - - -```javascript -async function alovaFetcherMiddleware(context, next) { - return next().catch(error => { - if (needRetry) { - setTimeout(() => { - context.fetch(context.method, ...context.fetchArgs); - }, retryDelay); - } - return Promise.reject(error); - }); -} -``` - - - - -如果需要在中间件内中断请求,可以调用`context.abort()`。 - -## 受控的加载状态 - -在上面内容中,我们知道了可以通过`context.update`自定义修改响应式数据,不过当你在修改加载状态值(`loading`或`fetching`)时将会有所阻碍,因为在正常情况下,加载状态值会在调用`next`时自动设置为 true,在响应流程中自动设置 false,这将覆盖通过`context.update`修改的加载状态值,此时我们可以开启受控的加载状态,开启后,在`next`函数和响应流程将不再修改加载状态值,而由我们完全控制。 - -我们还是以请求重试为例,我们希望在触发一次请求意图开始,经过请求重试直到请求结束为止,加载状态一直保持为 true。 - - - - -在 useRequest 和 useWatcher 的中间件中,使用`context.controlLoading`开启自定义控制加载状态。 - -```javascript -async function alovaFrontMiddleware(context, next) { - context.controlLoading(); - - // 请求开始时设置为true - context.update({ loading: true }); - return next() - .then(value => { - // 请求成功后设置为false - context.update({ loading: false }); - return value; - }) - .catch(error => { - if (needRetry) { - setTimeout(() => { - context.send(...context.sendArgs); - }, retryDelay); - } else { - // 不再重试时也设置为false - context.update({ loading: false }); - } - return Promise.reject(error); - }); -} -``` - - - - -在 useFetching 的中间件中,使用`context.controlFetching`开启自定义控制加载状态。 - -```javascript -async function alovaFetcherMiddleware(context, next) { - context.controlFetching(); - - // 请求开始时设置为true - context.update({ fetching: true }); - return next() - .then(value => { - // 请求成功后设置为false - context.update({ fetching: false }); - return value; - }) - .catch(error => { - if (needRetry) { - setTimeout(() => { - context.fetch(context.method, ...context.fetchArgs); - }, retryDelay); - } else { - // 不再重试时也设置为false - context.update({ fetching: false }); - } - return Promise.reject(error); - }); -} -``` - - - +--- +title: 请求中间件 +sidebar_position: 40 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info 使用范围 + +客户端 useHook + +::: + +你可以为所有的 useHook 设置请求中间件来自由控制请求行为,提供了强大的、几乎能控制一个请求的所有行为的能力,无论简单还是复杂的请求策略,可能你都会用上它,接下来我们看下它到底有什么神通。 + +## 中间件函数 + +请求中间件是一个异步函数,以下是一个简单的请求中间件,它在请求前和请求后分别打印了一些信息,没有改变任何请求行为。 + +```javascript +useRequest(todoList, { + async middleware(_, next) { + console.log('before request'); + await next(); + console.log('after requeste'); + } +}); +``` + +`next`函数是一个异步函数,调用它可以继续发送请求,此时将会把 _loading_ 状态设置为 true,然后发送请求。next 的返回值是带有响应数据的 Promise 实例,你可以在中间件函数中操纵返回值。 + +## 忽略请求 + +当你不希望发出请求时,可不调用 `next`来忽略本次请求,就好像从来没有发起过请求一样。例如在`useWatcher`中某个监听字段变化时不发出请求。 + +```js +useWatcher(() => todoList(), [state1], { + middleware: async (_, next) => { + if (state1 === 'a') { + return next(); + } + } +}); +``` + +## 控制响应数据 + +中间件函数的返回值将作为本次请求的响应数据参与后续的处理,如果中间件没有返回任何数据但调用了 `next`,则会将本次请求的响应数据参与后续处理。 + +```javascript +// 转换响应数据并返回 +useRequest(todoList, { + async middleware(_, next) { + const result = await next(); + result.code = 500; + return result; + } +}); + +// 将会以本次请求的响应数据参与后续处理 +useRequest(todoList, { + async middleware(_, next) { + await next(); + } +}); + +// 将会以字符串abc作为响应数据 +useRequest(todoList, { + async middleware(_, next) { + await next(); + return 'abc'; + } +}); +``` + +## 更改请求 + +有时候你想要更改请求,此时可以在 `next` 中指定另一个 method 实例,在发送请求时就会将这个 method 中的信息进行请求,同时你还可以通过 `next` 设置是否强制请求来穿透缓存,这也很简单。 + +```javascript +useRequest(todoList, { + async middleware(_, next) { + await next({ + // 更改请求的method实例 + method: newMethodInstance, + + // 本次是否强制请求 + force: true + }); + } +}); +``` + +## 控制错误 + +### 捕获错误 + +在中间件中,可以捕获 `next` 中产生的请求错误,捕获后,全局的`onError`钩子不再触发。 + +```javascript +useRequest(todoList, { + async middleware(_, next) { + try { + await next(); + } catch (e) { + console.error('捕获到错误', e); + } + } +}); +``` + +### 抛出错误 + +当然,也可以在中间件中抛出一个自定义错误,即使请求正常也将会进入请求错误的流程。 + +```javascript +// 未发出请求,同时还会触发全局的以及请求级的onError,如果是通过`method.send`发送的请求将返回reject的promise实例 +useRequest(todoList, { + async middleware(_, next) { + throw new Error('error on before request'); + await next(); + } +}); + +// 请求成功后,将触发全局的以及请求级的onError,如果是通过`method.send`发送的请求将返回reject的promise实例 +useRequest(todoList, { + async middleware(_, next) { + await next(); + throw new Error('error on after request'); + } +}); +``` + +## 控制响应延迟 + +在中间件中我们可以延迟响应,也可以提前响应,在提前的情况下,虽然获取不到响应数据,但可以返回一些其他的数据作为响应数据参与后续的处理。 + +```javascript +// 延迟1秒响应 +useRequest(todoList, { + async middleware(_, next) { + await new Promise(resolve => { + setTimeout(resolve, 1000); + }); + return next(); + } +}); + +// 立即响应,并使用字符串abc作为响应数据 +useRequest(todoList, { + async middleware(_, next) { + return 'abc'; + } +}); +``` + +## 不止于此 + +**至此,我们所提及的都是中间件的第二个参数 `next` 的使用,那第一个参数是做什么的呢?** + +中间件第一个参数中包含了本次请求的一些信息,以及对`loading`、`data`和`onSuccess`等 useHook 中返回的状态和事件的控制函数。我们接着往下看! + +## 包含的请求信息 + +```javascript +async function middleware(context, next) { + // 本次请求的method实例 + context.method; + + // send函数发送的参数数组,默认为[] + context.args; + + // 本次请求命中的缓存数据 + context.cachedResponse; + + // useHook的配置集合 + context.config; + + // useHook返回的各项状态,它是一个状态代理,包含以下属性 + // loading、data、error、downloading、uploading,以及通过managedStates管理的额外状态 + context.proxyStates; + + // 当前useHook中的操作函数,send、abort + // 在useFetcher中为context.fetch + context.send; + context.abort; +} +``` + +接下来,我们再来看看有哪些控制能力。 + +## 修改响应式数据 + +通过`context.proxyStates`可以修改当前 useHook 的状态化数据,由于 alova 的 useHook 可以兼容多 UI 框架,因此 proxyStates 是一个统一的状态代理,使用方式类似 vue 的 ref 值。 + +```javascript +async function middleware(context, next) { + const { loading, data } = context.proxyStates; + + // 获取loading值 + const loadingValue = loading.v; + // 修改loading状态为true + loading.v = true; + + // 修改data状态值 + data.v = { + /* ... */ + }; +} +``` + +关于状态代理的详细用法,请参考[状态代理](/docs/guide/state-proxy)。 + +## 中断或重复发送请求 + +中间件接收到的`abort`和`send`函数(useFetcher 中为`fetch`)还可以在触发一次请求意图时,发送多个请求。 + +典型的使用例子是请求重试,发送一次请求后如果请求失败将自动按一定策略再次请求,重试成功后再触发`onSuccess`。以下为简单的请求重试示例代码。 + +```javascript +async function middleware(context, next) { + return next().catch(error => { + if (needRetry) { + setTimeout(() => { + context.send(...context.sendArgs); + }, retryDelay); + } + return Promise.reject(error); + }); +} +``` + +如果需要在中间件内中断请求,可以调用`context.abort()`。 + +## 受控的加载状态 + +在上面内容中,我们知道了可以通过`loading.v`自定义修改响应式数据,不过当你在修改加载状态值`loading`时将会有所阻碍,因为在正常情况下,加载状态值会在调用`next`时自动设置为 true,在响应流程中自动设置 false,这将覆盖通过`loading.v`修改的加载状态值,此时我们可以开启受控的加载状态,开启后,在`next`函数和响应流程将不再修改加载状态值,而由我们完全控制。 + +我们还是以请求重试为例,我们希望在触发一次请求意图开始,经过请求重试直到请求结束为止,加载状态一直保持为 true。 + +使用`context.controlLoading`开启自定义控制加载状态。 + +```javascript +async function middleware(context, next) { + context.controlLoading(); + const { loading } = context.proxyStates; + + // 请求开始时设置为true + loading.v = true; + return next() + .then(value => { + // 请求成功后设置为false + loading.v = false; + return value; + }) + .catch(error => { + if (needRetry) { + setTimeout(() => { + context.send(...context.sendArgs); + }, retryDelay); + } else { + // 不再重试时也设置为false + loading.v = false; + } + return Promise.reject(error); + }); +} +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/06-error-logger.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/06-error-logger.md deleted file mode 100644 index 98dd64467..000000000 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/06-error-logger.md +++ /dev/null @@ -1,51 +0,0 @@ ---- -title: 错误日志 -sidebar_position: 60 ---- - -:::info 版本要求 - -v2.6.0+ - -::: - -为了方便调试,当在使用 use hooks 请求或响应处理错误时会默认在控制台打印错误日志,如果你在一些情况下(例如生产环境)不希望打印错误信息或自定义控制打印错误信息,alova 也提供了对它们的支持。 - -## 关闭打印错误日志 - -可在创建 alova 实例时将`errorLogger`设置为`false或null`关闭日志打印。 - -```javascript -const alovaInstance = createAlova({ - // ... - errorLogger: false -}); -``` - -你也可以根据不同的环境动态开启与关闭。 - -```javascript -const alovaInstance = createAlova({ - // ... - // 在开发环境开启错误日志 - errorLogger: process.env.NODE_ENV === 'development' -}); -``` - -## 自定义打印错误日志 - -错误日志默认通过`console.error`进行打印,如果你的项目环境中不支持`console.error`,或者希望收集错误信息,可以将`errorLogger`指定为一个函数自定义处理错误日志。 - -```javascript -const alovaInstance = createAlova({ - // ... - /** - * 自定义的错误日志函数 - * @param error 错误对象 - * @param method 当前的method实例 - */ - errorLogger(error, method) { - reportError(`${method.url}: ${error.message}`); - } -}); -``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-best-practice/01-manage-apis.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/01-manage-apis.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-best-practice/01-manage-apis.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/01-manage-apis.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-best-practice/02-skills.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/02-skills.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-best-practice/02-skills.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/02-skills.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-best-practice/03-manage-cache-by-indexeddb.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/03-manage-cache-by-indexeddb.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-best-practice/03-manage-cache-by-indexeddb.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/03-manage-cache-by-indexeddb.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-best-practice/04-multiple-servers.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/04-multiple-servers.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-best-practice/04-multiple-servers.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/04-multiple-servers.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-best-practice/05-middleware.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/05-middleware.md similarity index 96% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-best-practice/05-middleware.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/05-middleware.md index d8912f57e..aeb237f32 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-best-practice/05-middleware.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/05-middleware.md @@ -1,29 +1,29 @@ ---- -title: 常用中间件实践 -sidebar_position: 50 ---- - -## 延迟更新 loading - -当响应非常快的时候,加载状态会出现一次闪烁,这给会用户带来糟糕的体验,延迟更新 loading 可以让加载状态在一段时间后才显示,如果在这段时间内完成响应则不会出现加载状态。我们来实现一个带延迟更新 loading 的 middleware。 - -```javascript -const delayLoadingMiddleware = - (delayTimer = 1000) => - async (ctx, next) => { - // 自行控制loading - ctx.controlLoading(); - - // 延迟特定时间更新 - const timer = setTimeout(() => { - ctx.update({ loading: true }); - }, delayTimer); - await next(); - ctx.update({ loading: false }); - clearTimeout(timer); - }; - -useRequest(methodInstance, { - middleware: delayLoadingMiddleware() -}); -``` +--- +title: 常用中间件实践 +sidebar_position: 50 +--- + +## 延迟更新 loading + +当响应非常快的时候,加载状态会出现一次闪烁,这给会用户带来糟糕的体验,延迟更新 loading 可以让加载状态在一段时间后才显示,如果在这段时间内完成响应则不会出现加载状态。我们来实现一个带延迟更新 loading 的 middleware。 + +```javascript +const delayLoadingMiddleware = + (delayTimer = 1000) => + async (ctx, next) => { + // 自行控制loading + ctx.controlLoading(); + + // 延迟特定时间更新 + const timer = setTimeout(() => { + ctx.update({ loading: true }); + }, delayTimer); + await next(); + ctx.update({ loading: false }); + clearTimeout(timer); + }; + +useRequest(methodInstance, { + middleware: delayLoadingMiddleware() +}); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-best-practice/12-parallel-request.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/06-parallel-request.md similarity index 85% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-best-practice/12-parallel-request.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/06-parallel-request.md index 006742dba..ff72e5183 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-best-practice/12-parallel-request.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/06-parallel-request.md @@ -1,60 +1,68 @@ ---- -title: 并行请求 -sidebar_position: 120 ---- - -## 使用 method - -由于 method 是 PromiseLike 实例,通过 method 发送并行请求只需要使用`Promise.all`等待即可。 - -```javascript -const [todoList, todoCounter] = await Promise.all[(todoListGetter, todoCountGetter)]; -``` - -## 使用 useRequest - -简单的并行请求,只需要同时调用多个 useRequest 即可。 - -```javascript -const { data: todoList } = useRequest(todoListGetter); -const { data: todoCounter } = useRequest(todoCountGetter); -``` - -但这样的请求只适用于单纯的并行请求,如果你需要在并行请求都完成后再进行某些操作,有以下两种方式可以实现: - -### 方法 1 - -手动创建 promise 对象,并使用`Promise.all`完成效果。 - -```javascript -const { data: todoList, onSuccess: onListSuccess, onError: onListError } = useRequest(todoListGetter); -const { data: todoCounter, onSuccess: onCountSuccess, onError: onCountError } = useRequest(todoCountGetter); - -// 手动创建promise对象 -const listPromise = new Promise((resolve, reject) => { - onListSuccess(resolve); - onListError(reject); -}); -const countPromise = new Promise((resolve, reject) => { - onCountSuccess(resolve); - onCountError(reject); -}); -const [listEvent, countEvent] = await Promise.all([listPromise, countPromise]); -// 并行请求完成,继续处理业务... -``` - -### 方法 2 - -使用`useRequest`函数返回的`send`函数,调用`send`将会返回一个可用的 promise 对象。 - -```javascript -// 先让它们不自动发送请求 -const { send: sendList } = useRequest(todoListGetter, { immediate: false }); -const { send: sendCount } = useRequest(todoCountGetter, { immediate: false }); - -// 利用send函数返回的promise对象 -const parallelRequest = async () => { - const [listResponse, countResponse] = await Promise.all([sendList(), sendCount()]); - // 并行请求完成,继续处理业务... -}; -``` +--- +title: 并行请求 +sidebar_position: 60 +--- + +## 使用 method + +由于 method 是 PromiseLike 实例,通过 method 发送并行请求只需要使用`Promise.all`等待即可。 + +```javascript +const [todoList, todoCounter] = await Promise.all[(todoListGetter, todoCountGetter)]; +``` + +## 使用 useRequest + +简单的并行请求,只需要同时调用多个 useRequest 即可。 + +```javascript +const { data: todoList } = useRequest(todoListGetter); +const { data: todoCounter } = useRequest(todoCountGetter); +``` + +但这样的请求只适用于单纯的并行请求,如果你需要在并行请求都完成后再进行某些操作,有以下两种方式可以实现: + +### 方法 1 + +手动创建 promise 对象,并使用`Promise.all`完成效果。 + +```javascript +const { + data: todoList, + onSuccess: onListSuccess, + onError: onListError +} = useRequest(todoListGetter); +const { + data: todoCounter, + onSuccess: onCountSuccess, + onError: onCountError +} = useRequest(todoCountGetter); + +// 手动创建promise对象 +const listPromise = new Promise((resolve, reject) => { + onListSuccess(resolve); + onListError(reject); +}); +const countPromise = new Promise((resolve, reject) => { + onCountSuccess(resolve); + onCountError(reject); +}); +const [listEvent, countEvent] = await Promise.all([listPromise, countPromise]); +// 并行请求完成,继续处理业务... +``` + +### 方法 2 + +使用`useRequest`函数返回的`send`函数,调用`send`将会返回一个可用的 promise 对象。 + +```javascript +// 先让它们不自动发送请求 +const { send: sendList } = useRequest(todoListGetter, { immediate: false }); +const { send: sendCount } = useRequest(todoCountGetter, { immediate: false }); + +// 利用send函数返回的promise对象 +const parallelRequest = async () => { + const [listResponse, countResponse] = await Promise.all([sendList(), sendCount()]); + // 并行请求完成,继续处理业务... +}; +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-best-practice/13-serial-request.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/07-serial-request.md similarity index 84% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-best-practice/13-serial-request.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/07-serial-request.md index 54fda3717..461edd129 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-best-practice/13-serial-request.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/07-serial-request.md @@ -1,49 +1,54 @@ ---- -title: 串行请求 -sidebar_position: 130 ---- - -## 使用 method - -由于 method 是 PromiseLike 实例,可以使用`await`等待请求成功。 - -```javascript -const todoList = await todoListGetter; -const todoDetail = await todoDetailGetter(todoList[0].id); -``` - -## 使用 useRequest - -串行请求也具有两种方式。 - -### 方法 1 - -让第一个请求自动发出,第二个请求在第一个请求的`onSuccess`回调中触发,即可完成串行请求,可通过以下写法完成串行请求: - -```javascript -// -const { data: todoList, onSuccess } = useRequest(todoListGetter); -const { data: todoDetail, send: sendTodoDetail } = useRequest(todoId => todoDetailGetter(todoId), { immediate: false }); - -// 先获取列表,再获取第一个todo的详情 -onSuccess(event => { - sendTodoDetail(event.todoList[0].id); -}); -``` - -### 方法 2 - -使用`useRequest`函数返回的`send`函数,调用`send`将会返回一个可用的 promise 对象。 - -```javascript -// 先让它们不自动发送请求 -const { send: sendList } = useRequest(todoListGetter, { immediate: false }); -const { send: sendTodoDetail } = useRequest(todoId => todoDetailGetter(todoId), { immediate: false }); - -// 利用send函数返回的promise对象 -const serialRequest = async () => { - const todoList = await sendList(); - const todoDetail = await sendTodoDetail(todoList[0].id); - // 串行请求完成,继续处理业务... -}; -``` +--- +title: 串行请求 +sidebar_position: 70 +--- + +## 使用 method + +由于 method 是 PromiseLike 实例,可以使用`await`等待请求成功。 + +```javascript +const todoList = await todoListGetter; +const todoDetail = await todoDetailGetter(todoList[0].id); +``` + +## 使用 useRequest + +串行请求也具有两种方式。 + +### 方法 1 + +让第一个请求自动发出,第二个请求在第一个请求的`onSuccess`回调中触发,即可完成串行请求,可通过以下写法完成串行请求: + +```javascript +// +const { data: todoList, onSuccess } = useRequest(todoListGetter); +const { data: todoDetail, send: sendTodoDetail } = useRequest( + todoId => todoDetailGetter(todoId), + { immediate: false } +); + +// 先获取列表,再获取第一个todo的详情 +onSuccess(event => { + sendTodoDetail(event.todoList[0].id); +}); +``` + +### 方法 2 + +使用`useRequest`函数返回的`send`函数,调用`send`将会返回一个可用的 promise 对象。 + +```javascript +// 先让它们不自动发送请求 +const { send: sendList } = useRequest(todoListGetter, { immediate: false }); +const { send: sendTodoDetail } = useRequest(todoId => todoDetailGetter(todoId), { + immediate: false +}); + +// 利用send函数返回的promise对象 +const serialRequest = async () => { + const todoList = await sendList(); + const todoDetail = await sendTodoDetail(todoList[0].id); + // 串行请求完成,继续处理业务... +}; +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/08-l2-storage.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/08-l2-storage.md new file mode 100644 index 000000000..b726b0dc9 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/08-l2-storage.md @@ -0,0 +1,138 @@ +--- +title: 服务端L2存储实践 +sidebar_position: 80 +--- + +## 文件存储适配器 + +每个文件为一个缓存项,文件名为缓存的 key。 + +```ts +import { promises as fs } from 'fs'; +import * as path from 'path'; +import { AlovaGlobalCacheAdapter } from 'alova'; + +class FileStorageAdapter implements AlovaGlobalCacheAdapter { + private directory: string; + + constructor(directory: string) { + this.directory = directory; + } + + private getFilePath(key: string) { + return path.join(this.directory, `${key}.json`); + } + + async set(key: string, value: any) { + const filePath = this.getFilePath(key); + const data = JSON.stringify(value); + await fs.mkdir(this.directory, { recursive: true }); + await fs.writeFile(filePath, data, 'utf8'); + } + + async get(key: string): Promise { + const filePath = this.getFilePath(key); + try { + const data = await fs.readFile(filePath, 'utf8'); + return JSON.parse(data) as T; + } catch (error: any) { + if (error.code === 'ENOENT') { + return undefined; + } + throw error; + } + } + + async remove(key: string) { + const filePath = this.getFilePath(key); + try { + await fs.unlink(filePath); + } catch (error: any) { + if (error.code !== 'ENOENT') { + throw error; + } + } + } + + async clear() { + const files = await fs.readdir(this.directory); + const unlinkPromises = files.map(file => fs.unlink(path.join(this.directory, file))); + await Promise.all(unlinkPromises); + } +} +``` + +用法 + +```ts +const alovaInstance = createAlova({ + // ... + l2Cache: new FileStorageAdapter('tmp_cache') +}); +``` + +## Redis 适配器 + +```ts +import Redis from 'ioredis'; +import { AlovaGlobalCacheAdapter } from 'alova'; + +class RedisStorageAdapter implements AlovaGlobalCacheAdapter { + private client: Redis; + private cachePrefix: string; + + constructor(options: Redis.RedisOptions, cachePrefix = 'alova:') { + this.client = new Redis(options); + this.cachePrefix = cachePrefix; + } + + // Save or update cache + async set(key: string, value: [any, number]) { + const [data, expireTs] = value; + const now = Date.now(); + const dataToStore = JSON.stringify(data); + + // Calculate the TTL in milliseconds + const ttl = expireTs - now; + if (ttl > 0) { + await this.client.set(this.cachePrefix + key, dataToStore, 'PX', ttl); + } + } + + // Get value by key + async get(key: string): Promise { + const data = await this.client.get(this.cachePrefix + key); + if (!data) { + return undefined; + } + return JSON.parse(data); + } + + // Remove item + async remove(key: string) { + await this.client.del(this.cachePrefix + key); + } + + // Clear all cache + async clear() { + console.log('redis cache clear is not allowed'); + } +} +``` + +用法 + +```ts +const alovaInstance = createAlova({ + // ... + l2Cache: new RedisStorageAdapter( + { + host: 'localhost', + port: 6379, + password: 'yourpassword', + db: 0 + }, + 'myprefix:' + ) +}); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/01-mode.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/01-mode.md deleted file mode 100644 index 62da7b2db..000000000 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/01-mode.md +++ /dev/null @@ -1,218 +0,0 @@ ---- -title: 缓存模式 -sidebar_position: 10 ---- - -import MemoryCache from '@site/example-links/MemoryCache'; -import StoragePlaceholder from '@site/example-links/StoragePlaceholder'; -import StorageRestore from '@site/example-links/StorageRestore'; - -缓存模式可在全局或请求级等不同粒度下设置。全局设置时,所有由相同 alova 实例创建的 method 实例都会继承该设置。 - -:::info 注意 - -是否使用缓存模式,以及使用哪种缓存模式需要根据场景而定,下面在单独介绍不同缓存模式时将会提及它们的使用场景。 - -::: - -## 内存模式(默认) - -内存模式将缓存放在内存中,这意味着刷新页面缓存即失效,是最常用的缓存模式。 - -内存模式一般用于解决短时间内(几分钟或几秒钟)频繁请求相同数据带来的性能消耗,例如当你在写 todo 详情页的时候,你可能会想到用户会频繁在 todo 列表中点击查看详情,如果用户重复查看某条详情时不再重复请求接口,并且能立即返回数据,提升了响应速度的同时也减小了服务器压力。此时我们就可以为某个 todo 详情 method 实例设置响应数据缓存。 - -```javascript -alovaInstance.GET('/todo/list', { - // ... - // highlight-start - localCache: { - // 设置缓存模式为内存模式 - mode: 'memory', - - // 单位为毫秒 - // 当设置为`Infinity`,表示数据永不过期,设置为0或负数时表示不缓存 - expire: 60 * 10 * 1000 - } - // highlight-end -}); -``` - -内存模式为默认模式,你可以这样简写 - -```javascript -alovaInstance.GET('/todo/list', { - // ... - // highlight-start - localCache: 60 * 10 * 1000 - // highlight-end -}); -``` - -> GET 请求将默认设置 300000ms(5 分钟)的内存缓存时间,开发者也可以自定义设置。 - -> 如果你需要全局统一设置缓存模式,见本节底部的 [全局设置缓存模式](#全局设置缓存模式) - -### 内存缓存模式示例 - - - -## 缓存占位模式 - -这个缓存模式用于,当你不希望应用每次进入时都显示 Loading 图标,而希望使用旧数据替代时,你可以使用缓存占位模式,它的体验比 Loading 更好。 - -缓存占位模式下,`data`将立即被赋值为上次缓存的旧数据,你可以判断如果有旧数据则使用它替代 Loading 展示,同时它将发送请求获取最新数据并更新缓存,这样就达到了既快速展示实际数据,又获取了最新的数据。 - -在 method 实例上设置: - -```javascript -const todoListGetter = alovaInstance.Get('/todo/list', { - // ... - // highlight-start - localCache: { - // 设置缓存模式为持久化占位模式 - mode: 'placeholder', - // 缓存时间 - expire: 60 * 10 * 1000 - } - // highlight-end -}); -``` - -> 如果你需要全局统一设置缓存模式,见本节底部的 [全局设置缓存模式](#全局设置缓存模式) - -### 缓存占位模式示例 - - - -## 恢复模式 - -此模式下,服务端缓存数据将持久化,如果过期时间未到即使刷新页面缓存也不会失效,它一般用于一些需要服务端管理,但基本不变的数据,如每年的节假日具体日期有所不同,但不会再变动,这种场景下我们只需设置缓存过期时间为今年的最后一刻即可。 - -在 method 实例上设置: - -```javascript -const todoListGetter = alovaInstance.Get('/todo/list', { - // ... - // highlight-start - localCache: { - // 设置缓存模式为持久化模式 - mode: 'restore', - // 缓存时间 - expire: 60 * 10 * 1000 - } - // highlight-end -}); -``` - -:::warning 注意 - -当 request body 是**FormData**、**Blob**、**ArrayBuffer**、**URLSearchParams**、**ReadableStream**等特殊数据时,将会被认为你是有意图和服务端通信的,在这种情况下不会进行缓存。 - -::: - -> 如果你需要全局统一设置缓存模式,见本节底部的 [全局设置缓存模式](#全局设置缓存模式) - -### 恢复模式示例 - - - -### 恢复模式下数据有变怎么办? - -当设置了恢复模式的 method 实例,可能由于接口数据变动,或前端处理响应数据的逻辑变动,此时需要在发布应用后让用户重新缓存变动后的数据,此时你可以通过`tag`属性设置缓存标签,每一份持久化数据都包含一个`tag`标识,当`tag`改变后原有的持久化数据将会失效,并重新获取新的数据,并用新的`tag`进行标识。 - -```javascript -const todoListGetter = alovaInstance.Get('/todo/list', { - // ... - localCache: { - mode: 'restore', - expire: 60 * 10 * 1000, - - // highlight-start - // 新增或修改tag参数,已缓存的数据将失效 - // 建议使用版本号的形式管理 - tag: 'v1' - // highlight-end - } -}); -``` - -## 全局设置缓存模式 - -:::info 版本要求 - -v1.3.0+ - -::: - -以上设置均是在`Method`上单独设置缓存模式的,如果你需要全局设置缓存模式,可以按如下方式做: - -```javascript -const alovaInstance = createAlova({ - // ... - // highlight-start - localCache: { - // 统一设置POST的缓存模式 - POST: { - mode: 'placeholder', - expire: 60 * 10 * 1000 - }, - // 统一设置HEAD请求的缓存模式 - HEAD: 60 * 10 * 1000 - } - // highlight-end -}); -``` - -此后,通过`alovaInstance`实例创建的 method 实例,都将默认使用这份缓存设置,同时也可以在 method 实例中覆盖它。 - -> 注意:当全局设置了缓存模式后,原有的 5 分钟 GET 缓存模式将被覆盖。 - -## 全局关闭缓存模式 - -如果在你的项目中不希望使用任何请求缓存,可以在全局将它关闭,如果希望只在特定的几个请求中使用,也可以全局关闭它,并在指定的 method 实例中设置即可。 - -```javascript -const alovaInstance = createAlova({ - // ... - // highlight-start - // 设置为null即可全局关闭全部请求缓存 - localCache: null - // highlight-end -}); -``` - -## 过期时间类型 - -过期时间有两种类型可供选择,分别为 **相对时间** 和 **绝对时间** - -### 相对时间 - -即在保存缓存数据时开始,过期的时长,以 **毫秒** 为单位,以上示例均为此类型。 - -```javascript -localCache: 60 * 10 * 1000; -``` - -```javascript -localCache: { - expire: 60 * 10 * 1000, -} -``` - -### 绝对时间 - -以一个具体时间点为过期时间,缓存将在设定的时间点过期 - -```javascript -localCache: new Date('2030-01-01'); -``` - -```javascript -localCache: { - expire: new Date('2030-01-01'); -} -``` - -## 响应自动维护说明 - -响应数据缓存的 key 是由 method 实例的请求方法(method)、请求地址(url)、请求头参数(headers)、url 参数(params)、请求体参数(requestBody)组合作为唯一标识,任意一个信息或位置不同都将被当做不同的 key。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/04-force-request.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/04-force-request.md deleted file mode 100644 index ef4926f78..000000000 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/04-force-request.md +++ /dev/null @@ -1,75 +0,0 @@ ---- -title: 强制请求 -sidebar_position: 40 ---- - -强制请求是指绕过缓存的检查触发请求发送的机制,当需要在一定条件下获取最新的数据时很有用。 - -## 在 method 中强制请求 - -通过调用 method 实例的 send 函数,并传入 true 来强制请求。 - -```javascript -const response = await alovaInstance.Get('/api/user').send(true); -``` - -## 在 useHook 中强制请求 - -在`useRequest/useWatcher/useFetcher`三个核心 hook 中,都支持强制请求参数。 - -```javascript -// useRequest -useRequest(todoListGetter, { - // highlight-start - force: true - // highlight-end -}); - -// useWatcher -useWatcher(todoListGetter, [page], { - // highlight-start - force: true - // highlight-end -}); - -// useFetcher -useFetcher({ - // highlight-start - force: true - // highlight-end -}); -``` - -### 动态设置 force 值 - -实际情况中,我们经常需要根据不同情况来设置是否需要强制发送请求,此时可以将 force 设置为一个函数,此函数也将接收来自 `send` 函数传入的参数。在之前的[接收参数](/tutorial/combine-framework/receive-params)中已讲解过。 - -```javascript -useRequest(todoListGetter, { - // highlight-start - force: isForce => { - return isForce; - } - // highlight-end -}); - -// useWatcher -useWatcher(todoListGetter, [page], { - // highlight-start - force: isForce => { - return isForce; - } - // highlight-end -}); - -// useFetcher -useFetcher({ - // highlight-start - force: isForce => { - return isForce; - } - // highlight-end -}); -``` - -`useFetcher`是一个数据拉取的 useHook,将在后面的[数据拉取](/tutorial/advanced/use-fetcher)中讲解。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/05-set-and-query.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/05-set-and-query.md deleted file mode 100644 index 26d210dfb..000000000 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/05-set-and-query.md +++ /dev/null @@ -1,284 +0,0 @@ ---- -title: 更新与查找缓存 -sidebar_position: 50 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -缓存也支持更新和查找,在[缓存模式](/tutorial/cache/mode)中我们提到过,每份缓存数据是以发送请求的 method 实例作为 key 进行保存的,因此在手动更新缓存时也将使用 method 实例来查找对应的缓存数据。 - -## 更新静态缓存数据 - - - - -```html - - -``` - - - - -```jsx -import { setCache } from 'alova'; -import { useState } from 'react'; - -const getTodoListByDate = dateList => - alovaInstance.Get('/todo/list/dates', { - params: { dateList } - }); - -const App = () => { - // 初始化时批量获取5天的数据 - const [dates, setDates] = useState(['2022-05-01', '2022-05-02', '2022-05-03', '2022-05-04', '2022-05-05']); - const { - // ... - onSuccess - } = useWatcher(() => getTodoListByDate(dates.join()), [dates], { - immediate: true - }); - onSuccess(({ data: todoListDates }) => { - if (todoListDates.length <= 1) { - return; - } - - // highlight-start - // 默认情况下,这5天的数据会一起缓存到一个key中 - // 为了让后续请求某一天的数据时也能命中缓存,我们可以将5天的数据拆解为按天,并通过setCache一一手动设置响应缓存 - // setCache的第一个参数为method实例对象,它用于指定缓存的key - // 第二个参数为缓存数据 - todoListDates.forEach(todoDate => { - setCache(getTodoListByDate(todoDate.date), [todoDate]); - }); - // highlight-end - }); - - // highlight-start - const handleTodolistToggle = () => { - // 此时再在切换日期为5月1日时,它将会命中我们手动设置的响应缓存。 - // dates值正在被useWatcher监听,因此改变它就可以自动触发请求 - setDates(['2022-05-01']); - }; - // highlight-end - - return ; -}; -``` - - - - -```html - - -``` - - - - -```html - - -``` - - - - -## 动态更新缓存数据 - -你也可以在`setCache`中传入一个回调函数来动态计算缓存数据,并返回需要更新的缓存数据。 - -```javascript -setCache(getTodoListByDate('2022-10-01'), oldCache => { - // 返回需要缓存的数据 - return { - ...oldCache, - expire: isAfter('2022-10-01', new Date()) - }; -}); -``` - -同样的,你也可以通过 [method 匹配器](/tutorial/advanced/method-matcher) 动态查找 method 实例。 - -```javascript -setCache( - { - name: 'todoList', - filter: (method, index, ary) => { - return index < 5; - } - }, - 'newCache' -); -``` - -## 中断缓存更新 - -有时候你需要动态判断是否需要更新缓存,如果在`setCache`的回调函数中未返回数据,或返回了`undefined`,此时将不更新原缓存数据 - -```javascript -setCache(getTodoListByDate('2022-10-01'), oldCache => { - const isExpired = isAfter('2022-10-01', new Date()); - if (!isExpired) { - return; // 中断缓存更新 - } - return null; // 将缓存更新为null -}); -``` - -## 查询缓存 - -同时,我们也提供了缓存查询方法。 - -```javascript -import { queryCache } from 'alova'; - -const cacheData = queryCache(getTodoListByDate('2022-10-01')); -``` - -你也可以通过 [method 匹配器](/tutorial/advanced/method-matcher) 动态查找 method 实例。 - -```javascript -const cacheData = queryCache({ - name: 'todoList', - filter: (method, index, ary) => { - return index < 5; - } -}); -``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/06-controlled-cache.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/06-controlled-cache.md deleted file mode 100644 index 37e6c3ed3..000000000 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/06-controlled-cache.md +++ /dev/null @@ -1,83 +0,0 @@ ---- -title: 受控的缓存 -sidebar_position: 60 ---- - -:::info 版本要求 - -v2.1.0+ - -::: - -在发送一个请求时,默认会先检查是否存在匹配的缓存数据,匹配到则会使用它作为响应数据进行返回,如果在一些场景下,用户需要使用自定义的缓存就必须先使用`setCache`同步设置缓存数据才能行得通,无疑加大了用户的负担,这是一种不受控的缓存。 - -如果你想要在不受控的缓存下使用**IndexedDB**自定义管理缓存数据,你可能会先为即将发送的请求预先设置命中的缓存,像这样: - -```javascript -const getFile = fileName => { - const fileGetter = alovaInstance.GET(`/file/${fileName}`); - const tx = db.transaction(['files']); - const getRequest = tx.objectStore('files').get(fileName); - getRequest.onsuccess = ({ result }) => { - setCache(fileGetter, result); - }; - return fileGetter; -}; -``` - -**❌ 但并不推荐以上的写法**,原因如下: - -1. 每次调用`getFile`都会设置一次缓存,但 fileGetter 不一定用于发送请求; -2. IndexedDB 是异步接口,如果匹配缓存的步骤发生在 IndexedDB 触发 onsuccess 之前,那么就不会匹配到缓存数据,它们的顺序是不可预知的; -3. 自定义的缓存管理任务和 method 是分开的,但实际上它们应该聚合在一起; - -在这种情况下,你可以使用受控的缓存来解决上面的问题,使用受控缓存也很简单,可以在 method 中的 localCache 设置为异步或同步函数,在这个函数中返回自定义数据作为命中的缓存数据。 - -```javascript -const getFile = fileName => - alovaInstance.GET(`/file/${fileName}`, { - // 受控缓存函数支持异步和同步函数 - localCache() { - return new Promise((resolve, reject) => { - const tx = db.transaction(['files']); - const getRequest = tx.objectStore('files').get(fileName); - getRequest.onsuccess = resolve; - getRequest.onerror = reject; - }); - } - }); -``` - -## 不使用缓存 - -如果你希望继续发送请求,可以在`localCache`中返回 `undefined` 或不返回任何数据,这在自定义管理缓存时未命中缓存的情况下很有用。 - -## 使用 transformData 设置缓存 - -由于 `transformData` 函数具有以下两个特性: - -- 只有在响应时才被触发,而命中响应缓存时不会触发; -- 支持异步函数; - -因此,你还可以配合它保存自定义的缓存,例如以文件为响应数据的缓存场景下,可以配合 IndexedDB 进行文件数据的缓存。 - -```javascript -const fileGetter = alovaInstance.Get('/file/file_name', { - // 使用IndexedDB缓存文件 - async transformData(fileBlob) { - await new Promise((resolve, reject) => { - const tx = db.transaction(['files'], 'readwrite'); - const putRequest = tx.objectStore('files').put({ - file: fileBlob - }); - putRequest.onsuccess = resolve; - putRequest.onerror = reject; - }); - return fileBlob; - } -}); -``` - -## 注意事项 - -在 usehooks 中使用时,在`localCache`函数中抛出错误将会触发`onError`,使用 method 实例直接发起请求时,将会返回一个 reject 状态的 promise 实例。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/07-server-cache.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/07-server-cache.md deleted file mode 100644 index 2d809326f..000000000 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/07-server-cache.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: 服务端缓存 -sidebar_position: 70 ---- - -## alova id 设置 - -可以设置 alova id,用于固定请求,当没有设置时将按 alova 创建顺序递增。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/README.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/README.md deleted file mode 100644 index b0d7c75bb..000000000 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/README.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -title: 响应缓存 ---- - -响应缓存是一个将服务端返回的数据缓存到客户端的技术,在可重复利用服务端数据时避免重复发送请求,既能立即响应用户请求,也能节省服务端资源。根据不同的缓存场景,alova 提供了 3 种模式,分别为**内存模式、缓存占位模式、恢复模式**,选择适合你的使用即可。 - -此外,使用缓存操作 API,你还可以自由添加、修改和删除缓存,以及自定义缓存匹配规则。 - -接下来,让我们从缓存模式开始理解吧! diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/_category_.json b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/_category_.json deleted file mode 100644 index 2360c70d8..000000000 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-cache/_category_.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "label": "Response Cache" -} diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-request-adapter/01-alova-mock.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-request-adapter/01-alova-mock.md index d53238bcb..36676f14b 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-request-adapter/01-alova-mock.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-request-adapter/01-alova-mock.md @@ -1,329 +1,329 @@ ---- -title: 模拟数据 -sidebar_position: 10 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -此 mock 插件是一个 alova 的请求适配器,与传统的 Proxy 形式不同,你可以很好地控制使用 mock 数据的使用范围,你可以控制全局范围、某一组接口范围,甚至是某一个接口的启用和禁用,这在我们实际的业务场景中是很有用的,每一次的迭代都会新增或修改一组接口,我们希望让之前的功能还是走已开发好的接口,而让新增或修改的接口走模拟数据,此时就可以将每个开发人员针对本次迭代涉及到的接口分为一组,并对它们进行开启或关闭。 - -## 特性 - -- ✨ 与 alova 无缝协作 -- ✨ 模拟请求任意分组,可控制全局、组、以及单个模拟接口的启用和禁用 -- ✨ 与 mockjs 配合使用 -- ✨ 不污染生产环境 - -## 安装 - - - - -```bash -npm install @alova/mock --save -``` - - - - -```bash -yarn add @alova/mock -``` - - - - -以下为使用流程。 - -## 使用 - -### 定义 mock 接口 - -使用`defineMock`定义一组 mock 接口,你可以在每一项模拟接口中直接指定返回响应数据,或指定为回调函数动态计算响应数据。 - -```javascript title=mockGrou1.js -import { defineMock } from '@alova/mock'; - -export default defineMock( - { - // 捕获get请求 - '/todo': [1, 2, 3, 4], - - // rest风格请求 - '/todo/{id}': ({ params }) => { - const id = params.id; - // ... - return { - title: '...', - time: '10:00' - }; - }, - - // 捕获post请求 - '[POST]/todo': ({ query, data }) => { - // ... - return { success: true }; - }, - - // 返回更详细的信息 - '[POST]/todo': ({ query, data }) => { - // ... - return { - status: 403, - statusText: 'unknown error', - responseHeaders: { - // ... - }, - body: { - success: true - } - }; - }, - - // 模拟网络错误 - '[POST]/todo': ({ query, data }) => { - throw new Error('network error'); - }, - - // key前面添加`-`,表示禁用此mock接口 - '-[DELETE]/todo/{id}': ({ params }) => { - // ... - return { success: true }; - } - }, - true -); // 第二个参数表示是否启用本组mock接口,默认为true,可以指定为false关闭 -``` - -### 创建模拟请求适配器 - -在调用`createAlova`时创建一个模拟请求适配器,并将 mock 接口传入即可完成。 - -```javascript -import GlobalFetch from 'alova/GlobalFetch'; -import { createAlovaMockAdapter } from '@alova/mock'; -import mockGroup1 from './mockGroup1'; - -// highlight-start -const mockAdapter = createAlovaMockAdapter([mockGroup1, /** ... */], { - // 全局控制是否启用mock接口,默认为true - enable: true, - - // 非模拟请求适配器,用于未匹配mock接口时发送请求 - httpAdapter: GlobalFetch(), - - // mock接口响应延迟,单位毫秒 - delay: 1000, - - // 是否打印mock接口请求信息 - mockRequestLogger: true, - - // 模拟接口回调,data为返回的模拟数据,你可以用它构造任何你想要的对象返回给alova - // 以下为默认的回调函数,它适用于使用GlobalFetch请求适配器 - // 如果你使用的是其他请求适配器,在模拟接口回调中请自定义返回适合适配器的数据结构 - onMockResponse: data => new Response(JSON.stringify(data)) -}); -// highlight-end - -export const alovaInst = createAlova({ - baseURL: 'http://xxx', - - // 使用mock请求适配器,如果需要切换适配器,请看下面的实践建议 - requestAdapter: mockAdapter, - - statesHook: /** ... */ -}); -``` - -### 路径匹配模式 - -:::info 版本要求 - -1.5.0+ - -::: - -默认情况下,在`defineMock`中定义的路径是一个 url 的完整 pathname,看以下代码片段。 - -```javascript -const alovaInst = createAlova({ - baseURL: 'https://api.alovajs.org' - // ... -}); -alovaInst.Get('/user?id=1').send(); -``` - -示例中的请求路径为`https://api.alovajs.org/user?id=1`时,它的完整 pathname 为`/user`,此时可以匹配到`defineMock`中的`/user`。 - -通常情况下这已经足够了,但是当你的 baseURL 不仅仅是一个域名时。 - -```javascript -const alovaInst = createAlova({ - baseURL: 'https://api.alovajs.org/v1/subname' - // ... -}); -alovaInst.Get('/user?id=1').send(); -``` - -这个示例中的请求路径为`https://api.alovajs.org/v1/subname/user?id=1`,mock 的匹配路径为`/v1/subname/user`,需要将 baseURL 中的`/v1/subname`也一同写上,当接口数量较多时就稍显冗余。 - -此时,你可以在`createAlovaMockAdapter`中设置`matchMode`为`methodurl`,它将只匹配 method 实例中定义的 url,例如上面的实例将会匹配到`/user?id=1`,而不再需要写 baseURL 中的部分,相对的,如果 method 实例中的 url 中带了 get 参数时,也需要将它一同写到`defineMock`的匹配路径中,就像这边的`?id=1`。 - -```javascript -createAlovaMockAdapter([mockGroup1 /** ... */], { - // ... - // highlight-start - matchMode: 'methodurl' - // highlight-end -}); -``` - -## 实践建议 - -### 按每个开发者每次版本分组接口 - -在团队开发场景下,每次版本开发时我们经常只需要对部分未开发好的接口进行模拟请求,并且对之前版本的接口使用测试环境接口,此时为了达到更好的模拟接口管理,可以以开发版本和开发者两个维度将接口分组。 - -例如有两个开发者名为 _August_、_kevin_,他们正在开发 v1.1 产品功能,他们可以这样管理模拟接口。 - -```javascript title=August-v1.1.js -import { defineMock } from '@alova/mock'; - -export default defineMock({ - '/todo': [ - /** */ - ], - '[POST]/todo': ({ data }) => { - // ... - // return ... - } - // ... -}); -``` - -```javascript title=kevin-v1.1.js -import { defineMock } from '@alova/mock'; - -export default defineMock({ - '[PUT]/todo/add': ({ data }) => { - // ... - // return ... - }, - '[DELETE]/todo/remove': ({ data }) => { - // ... - // return ... - } - // ... -}); -``` - -```javascript title=request.js -import Augustv1_1 from './August-v1.1'; -import kevinv1_1 from './kevin-v1.1'; - -const mockAdapter = createAlovaMockAdapter([Augustv1_1, kevinv1_1], { - httpAdapter: GlobalFetch(), - delay: 1000 -}); -export const alovaInst = createAlova({ - baseURL: 'http://xxx', - requestAdapter: mockAdapter - // ... -}); -``` - -### 在生产环境中排除 mock 代码 - -mock 数据一般只作用于开发环境,在生产环境下将会切换到实际的接口中,因此这段 mock 代码在生产环境就变得没有作用,此时我们可以通过环境变量的判断来排除这块代码,你只需要这样做: - -```javascript -const globalFetch = GlobalFetch(); -const mockAdapter = createAlovaMockAdapter([mockGroup1, /** ... */], { - httpAdapter: globalFetch, - delay: 1000, -}); - -export const alovaInst = createAlova({ - baseURL: 'http://xxx', - - // highlight-start - // 通过环境变量控制生产环境下,不会将mock相关代码打包进去 - requestAdapter: process.env.NODE_ENV === 'development' ? mockAdapter : globalFetch, - // highlight-end - - statesHook: /** ... */ -}); -``` - -### 与 mockjs 一同使用 - -如果你不希望自己编写模拟数据,而是使用模拟数据库(例如 mockjs)一同使用,你可以这样做。 - -```javascript -import { defineMock } from '@alova/mock'; -import Mock from 'mockjs'; - -export default defineMock({ - '/api1': Mock.mock({ - 'id|1-10000': 100 - }) -}); -``` - -## 转换模拟数据 - -**@alova/mock** 默认将响应数据包装为 Response 实例,将响应头默认包装为 Headers 实例,这是针对`GlobalFetch`进行适配的,但如果使用其他的请求适配器,就需要将模拟数据转换为相应的格式。 - -### 转换响应数据 - -你可以在`onMockResponse`字段中拦截模拟响应数据并返回转换后的响应数据以及响应头。 - -> 你也可以在 onMockResponse 中抛出一个错误,表示请求失败。 - -```javascript -const mockAdapter = createAlovaMockAdapter( - [ - /* 模拟数据 */ - ], - { - // ... - // highlight-start - onMockResponse(response, request, currentMethod) { - // response为相应数据集合,其中包含status、statusText、responseHeaders、body - // request为请求数据,其中包含query、params、headers、data - // currentMethod为当前请求的method实例 - // ... - // 返回转换后的响应数据和响应头 - return { - response: /** 响应数据 */, - headers: /** 响应头 */ - }; - } - // highlight-end - } -); -``` - -### 转换错误对象 - -你可以在`onMockError`字段中拦截错误实例并返回转换后的错误信息。 - -```javascript -const mockAdapter = createAlovaMockAdapter( - [ - /* 模拟数据 */ - ], - { - // ... - // highlight-start - onMockError(error, currentMethod) { - // error为错误实例 - // currentMethod为当前请求的method实例 - // ... - // 返回转换后的错误信息集合 - } - // highlight-end - } -); -``` +--- +title: 模拟数据 +sidebar_position: 10 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +此 mock 插件是一个 alova 的请求适配器,与传统的 Proxy 形式不同,你可以很好地控制使用 mock 数据的使用范围,你可以控制全局范围、某一组接口范围,甚至是某一个接口的启用和禁用,这在我们实际的业务场景中是很有用的,每一次的迭代都会新增或修改一组接口,我们希望让之前的功能还是走已开发好的接口,而让新增或修改的接口走模拟数据,此时就可以将每个开发人员针对本次迭代涉及到的接口分为一组,并对它们进行开启或关闭。 + +## 特性 + +- 与 alova 无缝协作 +- 模拟请求任意分组,可控制全局、组、以及单个模拟接口的启用和禁用 +- 与 mockjs 配合使用 +- 不污染生产环境 + +## 安装 + + + + +```bash +npm install @alova/mock --save +``` + + + + +```bash +yarn add @alova/mock +``` + + + + +以下为使用流程。 + +## 使用 + +### 定义 mock 接口 + +使用`defineMock`定义一组 mock 接口,你可以在每一项模拟接口中直接指定返回响应数据,或指定为回调函数动态计算响应数据。 + +```javascript title=mockGrou1.js +import { defineMock } from '@alova/mock'; + +export default defineMock( + { + // 捕获get请求 + '/todo': [1, 2, 3, 4], + + // rest风格请求 + '/todo/{id}': ({ params }) => { + const id = params.id; + // ... + return { + title: '...', + time: '10:00' + }; + }, + + // 捕获post请求 + '[POST]/todo': ({ query, data }) => { + // ... + return { success: true }; + }, + + // 返回更详细的信息 + '[POST]/todo': ({ query, data }) => { + // ... + return { + status: 403, + statusText: 'unknown error', + responseHeaders: { + // ... + }, + body: { + success: true + } + }; + }, + + // 模拟网络错误 + '[POST]/todo': ({ query, data }) => { + throw new Error('network error'); + }, + + // key前面添加`-`,表示禁用此mock接口 + '-[DELETE]/todo/{id}': ({ params }) => { + // ... + return { success: true }; + } + }, + true +); // 第二个参数表示是否启用本组mock接口,默认为true,可以指定为false关闭 +``` + +### 创建模拟请求适配器 + +在调用`createAlova`时创建一个模拟请求适配器,并将 mock 接口传入即可完成。 + +```javascript +import GlobalFetch from 'alova/GlobalFetch'; +import { createAlovaMockAdapter } from '@alova/mock'; +import mockGroup1 from './mockGroup1'; + +// highlight-start +const mockAdapter = createAlovaMockAdapter([mockGroup1, /** ... */], { + // 全局控制是否启用mock接口,默认为true + enable: true, + + // 非模拟请求适配器,用于未匹配mock接口时发送请求 + httpAdapter: GlobalFetch(), + + // mock接口响应延迟,单位毫秒 + delay: 1000, + + // 是否打印mock接口请求信息 + mockRequestLogger: true, + + // 模拟接口回调,data为返回的模拟数据,你可以用它构造任何你想要的对象返回给alova + // 以下为默认的回调函数,它适用于使用GlobalFetch请求适配器 + // 如果你使用的是其他请求适配器,在模拟接口回调中请自定义返回适合适配器的数据结构 + onMockResponse: data => new Response(JSON.stringify(data)) +}); +// highlight-end + +export const alovaInst = createAlova({ + baseURL: 'http://xxx', + + // 使用mock请求适配器,如果需要切换适配器,请看下面的实践建议 + requestAdapter: mockAdapter, + + statesHook: /** ... */ +}); +``` + +### 路径匹配模式 + +:::info 版本要求 + +1.5.0+ + +::: + +默认情况下,在`defineMock`中定义的路径是一个 url 的完整 pathname,看以下代码片段。 + +```javascript +const alovaInst = createAlova({ + baseURL: 'https://api.alovajs.org' + // ... +}); +alovaInst.Get('/user?id=1').send(); +``` + +示例中的请求路径为`https://api.alovajs.org/user?id=1`时,它的完整 pathname 为`/user`,此时可以匹配到`defineMock`中的`/user`。 + +通常情况下这已经足够了,但是当你的 baseURL 不仅仅是一个域名时。 + +```javascript +const alovaInst = createAlova({ + baseURL: 'https://api.alovajs.org/v1/subname' + // ... +}); +alovaInst.Get('/user?id=1').send(); +``` + +这个示例中的请求路径为`https://api.alovajs.org/v1/subname/user?id=1`,mock 的匹配路径为`/v1/subname/user`,需要将 baseURL 中的`/v1/subname`也一同写上,当接口数量较多时就稍显冗余。 + +此时,你可以在`createAlovaMockAdapter`中设置`matchMode`为`methodurl`,它将只匹配 method 实例中定义的 url,例如上面的实例将会匹配到`/user?id=1`,而不再需要写 baseURL 中的部分,相对的,如果 method 实例中的 url 中带了 get 参数时,也需要将它一同写到`defineMock`的匹配路径中,就像这边的`?id=1`。 + +```javascript +createAlovaMockAdapter([mockGroup1 /** ... */], { + // ... + // highlight-start + matchMode: 'methodurl' + // highlight-end +}); +``` + +## 实践建议 + +### 按每个开发者每次版本分组接口 + +在团队开发场景下,每次版本开发时我们经常只需要对部分未开发好的接口进行模拟请求,并且对之前版本的接口使用测试环境接口,此时为了达到更好的模拟接口管理,可以以开发版本和开发者两个维度将接口分组。 + +例如有两个开发者名为 _August_、_kevin_,他们正在开发 v1.1 产品功能,他们可以这样管理模拟接口。 + +```javascript title=August-v1.1.js +import { defineMock } from '@alova/mock'; + +export default defineMock({ + '/todo': [ + /** */ + ], + '[POST]/todo': ({ data }) => { + // ... + // return ... + } + // ... +}); +``` + +```javascript title=kevin-v1.1.js +import { defineMock } from '@alova/mock'; + +export default defineMock({ + '[PUT]/todo/add': ({ data }) => { + // ... + // return ... + }, + '[DELETE]/todo/remove': ({ data }) => { + // ... + // return ... + } + // ... +}); +``` + +```javascript title=request.js +import Augustv1_1 from './August-v1.1'; +import kevinv1_1 from './kevin-v1.1'; + +const mockAdapter = createAlovaMockAdapter([Augustv1_1, kevinv1_1], { + httpAdapter: GlobalFetch(), + delay: 1000 +}); +export const alovaInst = createAlova({ + baseURL: 'http://xxx', + requestAdapter: mockAdapter + // ... +}); +``` + +### 在生产环境中排除 mock 代码 + +mock 数据一般只作用于开发环境,在生产环境下将会切换到实际的接口中,因此这段 mock 代码在生产环境就变得没有作用,此时我们可以通过环境变量的判断来排除这块代码,你只需要这样做: + +```javascript +const globalFetch = GlobalFetch(); +const mockAdapter = createAlovaMockAdapter([mockGroup1, /** ... */], { + httpAdapter: globalFetch, + delay: 1000, +}); + +export const alovaInst = createAlova({ + baseURL: 'http://xxx', + + // highlight-start + // 通过环境变量控制生产环境下,不会将mock相关代码打包进去 + requestAdapter: process.env.NODE_ENV === 'development' ? mockAdapter : globalFetch, + // highlight-end + + statesHook: /** ... */ +}); +``` + +### 与 mockjs 一同使用 + +如果你不希望自己编写模拟数据,而是使用模拟数据库(例如 mockjs)一同使用,你可以这样做。 + +```javascript +import { defineMock } from '@alova/mock'; +import Mock from 'mockjs'; + +export default defineMock({ + '/api1': Mock.mock({ + 'id|1-10000': 100 + }) +}); +``` + +## 转换模拟数据 + +**@alova/mock** 默认将响应数据包装为 Response 实例,将响应头默认包装为 Headers 实例,这是针对`GlobalFetch`进行适配的,但如果使用其他的请求适配器,就需要将模拟数据转换为相应的格式。 + +### 转换响应数据 + +你可以在`onMockResponse`字段中拦截模拟响应数据并返回转换后的响应数据以及响应头。 + +> 你也可以在 onMockResponse 中抛出一个错误,表示请求失败。 + +```javascript +const mockAdapter = createAlovaMockAdapter( + [ + /* 模拟数据 */ + ], + { + // ... + // highlight-start + onMockResponse(response, request, currentMethod) { + // response为相应数据集合,其中包含status、statusText、responseHeaders、body + // request为请求数据,其中包含query、params、headers、data + // currentMethod为当前请求的method实例 + // ... + // 返回转换后的响应数据和响应头 + return { + response: /** 响应数据 */, + headers: /** 响应头 */ + }; + } + // highlight-end + } +); +``` + +### 转换错误对象 + +你可以在`onMockError`字段中拦截错误实例并返回转换后的错误信息。 + +```javascript +const mockAdapter = createAlovaMockAdapter( + [ + /* 模拟数据 */ + ], + { + // ... + // highlight-start + onMockError(error, currentMethod) { + // error为错误实例 + // currentMethod为当前请求的method实例 + // ... + // 返回转换后的错误信息集合 + } + // highlight-end + } +); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/02-usePagination.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/02-usePagination.md index f4cf90157..92c4cfbfe 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/02-usePagination.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/02-usePagination.md @@ -1,727 +1,733 @@ ---- -title: 分页请求策略 -sidebar_position: 20 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -:::info 策略类型 - -use hook - -::: - -> 在使用扩展 hooks 前,确保你已熟悉了 alova 的基本使用。 - -为分页场景下设计的 hook,它可以帮助你自动管理分页数据,数据预加载,减少不必要的数据刷新,**流畅性提高 300%,编码难度降低 50%**。你可以在下拉加载和页码翻页两种分页场景下使用它,此 hook 提供了丰富的特性,助你的应用打造性能更好,使用更便捷的分页功能。 - -## 示例 - -[页码列表](/tutorial/example/paginated-list) - -[下拉加载更多](/tutorial/example/load-more) - -## 特性 - -- ✨ 丰富全面的分页状态; -- ✨ 丰富全面的分页事件; -- ✨ 更改 page、pageSize 自动获取指定分页数据; -- ✨ 数据缓存,无需重复请求相同参数的列表数据; -- ✨ 前后页预加载,翻页不再等待; -- ✨ 搜索条件监听自动重新获取页数; -- ✨ 支持列表数据的新增、编辑、删除; -- ✨ 支持刷新指定页的数据,无需重置; -- ✨ 请求级搜索防抖,无需自行维护; - -## 安装 - - - - -```bash -# npm -npm install @alova/scene-vue --save -# yarn -yarn add @alova/scene-vue - -``` - - - - -```bash -# npm -npm install @alova/scene-react --save -# yarn -yarn add @alova/scene-react - -``` - - - - - -```bash -# npm -npm install @alova/scene-svelte --save -# yarn -yarn add @alova/scene-svelte - -``` - - - - -## 使用 - -### 展示列表数据 - - - - -```html - - - -``` - - - - -```jsx -import { queryStudents } from './api.js'; -import { usePagination } from '@alova/scene-react'; - -const App = () => { - const { - // 加载状态 - loading, - - // 列表数据 - data, - - // 是否为最后一页 - // 下拉加载时可通过此参数判断是否还需要加载 - isLastPage, - - // 当前页码,改变此页码将自动触发请求 - page: [page, setPage], - - // 每页数据条数 - pageSize: [page, setPageSize], - - // 分页页数 - pageCount, - - // 总数据量 - total - } = usePagination( - // Method实例获取函数,它将接收page和pageSize,并返回一个Method实例 - (page, pageSize) => queryStudents(page, pageSize), - { - // 请求前的初始数据(接口返回的数据格式) - initialData: { - total: 0, - data: [] - }, - initialPage: 1, // 初始页码,默认为1 - initialPageSize: 10 // 初始每页数据条数,默认为10 - } - ); - - // 翻到上一页,page值更改后将自动发送请求 - const handlePrevPage = () => { - setPage(value => value - 1); - }; - - // 翻到下一页,page值更改后将自动发送请求 - const handleNextPage = () => { - setPage(value => value + 1); - }; - - // 更改每页数量,pageSize值更改后将自动发送请求 - const handleSetPageSize = () => { - setPageSize(20); - }; - - return ( -
- {data.map(item => ( -
- {item.name} -
- ))} - - - - 共有{pageCount}页 - 共有{total}条数据 -
- ); -}; -``` - -
- - -```html - - -{#each $data as item} -
- {item.name} -
-{/each} - - - -共有{pageCount}页 -共有{total}条数据 -``` - -
-
- -### 指定分页数据 - -每个分页数据接口返回的数据结构各不相同,因此我们需要分别告诉`usePagination`列表数据与总条数,从而帮助我们管理分页数据。 - -假如你的分页接口返回的数据格式是这样的: - -```typescript -interface PaginationData { - totalNumber: number; - list: any[]; -} -``` - -此时你需要通过函数的形式返回列表数据与总条数。 - -```javascript -usePagination((page, pageSize) => queryStudents(page, pageSize), { - // ... - // highlight-start - total: response => response.totalNumber, - data: response => response.list - // highlight-end -}); -``` - -如果不指定 total 和 data 回调函数,它们将默认通过以下方式获取数据。 - -```javascript -const total = response => response.total; -const data = response => response.data; -``` - -:::warning 注意 - -data 回调函数必须返回一个列表数据,表示分页中所使用的数据集合,而 total 主要用于计算当前页数,在 total 回调函数中如果未返回数字,将会通过请求的列表数量是否少于 pageSize 值来判断当前是否为最后一页,这一般用于下拉加载时使用。 - -::: - -### 开启追加模式 - -默认情况下,翻页时会替换原有的列表数据,而追加模式是在翻页时会将下一页的数据追加到当前列表底部,常见的使用场景是下拉加载更多。 - -```javascript -usePagination((page, pageSize) => queryStudents(page, pageSize), { - // ... - // highlight-start - append: true - // highlight-end -}); -``` - -### 预加载相邻页数据 - -为了让分页提供更好的体验,在当前页的上一页和下一页满足条件时将会自动预加载,这样在用户翻页时可直接显示数据而不需要等待,这是默认的行为。如果你不希望预加载相邻页的数据,可通过以下方式关闭。 - -```javascript -usePagination((page, pageSize) => queryStudents(page, pageSize), { - // ... - // highlight-start - preloadPreviousPage: false, // 关闭预加载上一页数据 - preloadNextPage: false // 关闭预加载下一页数据 - // highlight-end -}); -``` - -:::warning 预加载触发条件 - -在开启预加载时,并不会一味地加载下一页,需要满足以下两个条件: - -1. 预加载是基于缓存的,用于分页加载的 Method 实例必须开启缓存,默认情况下 get 请求会有 5 分钟的 memory 缓存,如果是非 get 请求或者全局关闭了缓存,你还需要在这个 Method 实例中单独设置`localCache`开启缓存。 -2. 根据`total`和`pageSize`参数判断出下一页还有数据。 - -::: - -除了`onSuccess、onError、onComplete`请求事件外,在触发了预加载时,你还可以通过`fetching`来获知预加载状态,还可以通过`onFetchSuccess、onFetchError、onFetchComplete`来监听预加载请求的事件。 - -```javascript -const { - // 预加载状态 - fetching, - - // 预加载成功事件绑定函数 - onFetchSuccess, - - // 预加载错误事件绑定函数 - onFetchError, - - // 预加载完成事件绑定函数 - onFetchComplete -} = usePagination((page, pageSize) => queryStudents(page, pageSize), { - // ... -}); -``` - -### 监听筛选条件 - -很多时候列表需要通过条件进行筛选,此时可以通过`usePagination`的状态监听来触发重新请求,这与 alova 提供的`useWatcher`是一样的。 - -例如通过学生姓名、学生年级进行筛选。 - - - - -```html - - - -``` - - - - -```jsx -import { queryStudents } from './api.js'; -import { usePagination } from '@alova/scene-react'; - -const App = () => { - // 搜索条件状态 - const [studentName, setStudentName] = useState(''); - const [clsName, setClsName] = useState(''); - const { - // ... - } = usePagination( - (page, pageSize) => queryStudents(page, pageSize, studentName, clsName), - { - // ... - // highlight-start - watchingStates: [studentName, clsName] - // highlight-end - } - ); - - return ( - // highlight-start - setStudentName(target.value)} /> - - // highlight-end - // ... - ); -}; -``` - - - - -```html - - - - - - - -``` - - - - -与`useWatcher`相同,你也可以通过指定`debounce`来实现请求防抖,具体可参考[useWatcher 的 debounce 参数设置](/api/core-hooks#usewatcher)。 - -```javascript -usePagination((page, pageSize) => queryStudents(page, pageSize, studentName, clsName), { - // ... - // highlight-start - debounce: 300 // 防抖参数,单位为毫秒数,也可以设置为数组对watchingStates单独设置防抖时间 - // highlight-end -}); -``` - -需要注意的是,`debounce`是通过 [**useWatcher**](/api/core-hooks#usewatcher) 中的请求防抖实现的。**监听状态末尾分别还有 page 和 pageSize 两个隐藏的监听状态,也可以通过 debounce 来设置。** - -举例来说,当`watchingStates`设置了`[studentName, clsName]`,内部将会监听`[studentName, clsName, page, pageSize]`,因此如果需要对 page 和 pageSize 设置防抖时,可以指定为`[0, 0, 500, 500]`。 - -### 关闭初始化请求 - -默认情况下,`usePagination`会在初始化时发起请求,但你也可以使用`immediate`关闭它,并在后续通过`send`函数,或者改变`page`或`pageSize`,以及`watchingStates`等监听状态来发起请求。 - -```javascript -usePagination((page, pageSize) => queryStudents(page, pageSize, studentName, clsName), { - // ... - // highlight-start - immediate: false - // highlight-end -}); -``` - -## 列表操作函数 - -usePagination 提供了功能完善的列表操作函数,它可以在不重新请求列表的情况下,做到与重新请求列表一致的效果,大大提高了页面的交互体验,具体的函数说明继续往下看吧! - -### 插入列表项 - -你可以用它插入列表项到列表任意位置,它将会在插入之后去掉末尾的一项,来保证和重新请求当前页数据一致的效果。 - -```typescript -/** - * 插入一条数据 - * 如果未传入index,将默认插入到最前面 - * 如果传入一个列表项,将插入到这个列表项的后面,如果列表项未在列表数据中将会抛出错误 - * @param item 插入项 - * @param indexOrItem 插入位置(索引) - */ -declare function insert(item: LD[number], indexOrItem?: number | LD[number]): void; -``` - -以下为**非 append 模式**下(页码翻页场景),返回第一页再插入列表项的示例: - -```javascript -page.value = 1; -nextTick(() => { - insert(newItem, 0); -}); -``` - -以下为在**append 模式**下(下拉加载场景),插入列表项后滚动到最顶部的示例: - -```javascript -insert(newItem, 0); -nextTick(() => { - window.scrollTo(0, {}); -}); -``` - -你也可以将`insert`的第二个参数指定为列表项,当查找到这个列表项的相同引用时,插入项将插入到这个列表项的后面。 - -```javascript -insert(newItem, afterItem); -``` - -:::warning 注意 - -为了让数据正确,insert 函数调用会清除全部缓存。 - -::: - -### 移除列表项 - -在下一页有缓存的情况下,它将会在移除一项后使用下一页的缓存补充到列表项尾部,来保证和重新请求当前页数据一致的效果,在**append 模式**和**非 append 模式**下表现相同。 - -```typescript -/** - * 移除一条数据 - * 如果传入的是列表项,将移除此列表项,如果列表项未在列表数据中将会抛出错误 - * @param position 移除的索引或列表项 - */ -declare function remove(position: number | LD[number]): void; -``` - -你也可以将`remove`的第二个参数指定为列表项,当查找到这个列表项的相同引用时,将会移除此列表项。 - -但在以下两种情况下,它将重新发起请求刷新对应页的数据: - -1. 下一页没有缓存 -2. 同步连续调用了超过下一页缓存列表项的数据,缓存数据已经不够补充到当前页列表了。 - -:::warning 注意 - -为了让数据正确,remove 函数调用会清除全部缓存。 - -::: - -### 更新数据项 - -当你想要更新列表项时,使用此函数实现。 - -```typescript -/** - * 替换一条数据 - * 当position传入数字时表示替换索引,负数表示从末尾算起,当 position 传入的是列表项,将替换此列表项,如果列表项未在列表数据中将会抛出错误 - * @param item 替换项 - * @param position 替换位置(索引)或列表项 - */ -declare function replace(item: LD extends any[] ? LD[number] : any, position: number | LD[number]): void; -``` - -你也可以将`replace`的第二个参数指定为列表项,当查找到这个列表项的相同引用时,将会替换此列表项。 - -### 刷新指定页的数据 - -当你在数据操作后不希望本地更新列表项,而是重新请求服务端的数据,你可以用 refresh 刷新任意页的数据,而不需要重置列表数据让用户又从第一页开始浏览。 - -```typescript -/** - * 刷新指定页码数据,此函数将忽略缓存强制发送请求 - * 如果未传入页码则会刷新当前页 - * 如果传入一个列表项,将会刷新此列表项所在页 - * @param pageOrItemPage 刷新的页码或列表项 - */ -declare function refresh(pageOrItemPage?: number | LD[number]): void; -``` - -在 append 模式下,你可以将`refresh`的参数指定为列表项,当查找到这个列表项的相同引用时,刷新此列表项所在页数的数据。 - -### 手动更新列表数据 - -使用`update`函数更新响应式数据,这与[useRequest 的 update](/tutorial/combine-framework/use-request)相似,唯一不同的是,在调用`update`更新`data`时,更新的是列表数据,而非响应数据。这在手动清除列表数据,而不重新发起请求时很有用。 - -```typescript -// 情况列表数据 -update({ - data: [] -}); -``` - -### 重置列表 - -它将清空全部缓存,并重新加载第一页。 - -```typescript -/** - * 从第一页开始重新加载列表,并清空缓存 - */ -declare function reload(): void; -``` - -## 限制 - -**缓存占位模式** 暂时无效 - -## API - -### Hook 配置 - -继承[**useWatcher**](/api/core-hooks#usewatcher)所有配置。 - -| 名称 | 描述 | 类型 | 默认值 | 版本 | -| ------------------- | ---------------------------------------- | ------------------------- | -------------------------- | ---- | -| initialPage | 初始页码 | number | 1 | - | -| initialPageSize | 初始每页数据条数 | number | 10 | - | -| watchingStates | 状态监听触发请求,使用 useWatcher 实现 | any[] | [page, pageSize] | - | -| debounce | 状态监听的防抖参数,使用 useWatcher 实现 | number \| number[] | - | - | -| append | 是否开启追加模式 | boolean | false | - | -| data | 指定分页的数组数据 | (response: any) => any[] | response => response.data | - | -| total | 指定数据总数量值 | (response: any) => number | response => response.total | - | -| preloadPreviousPage | 是否预加载上一页数据 | boolean | true | - | -| preloadNextPage | 是否预加载下一页数据 | boolean | true | - | - -### 响应式数据 - -继承[**useWatcher**](/api/core-hooks#usewatcher)所有响应式数据。 - -| 名称 | 描述 | 类型 | 版本 | -| ---------- | ------------------------------------------------------------------------------------------------------------------ | ------- | ---- | -| page | 当前页码,由 initialPage 决定 | number | - | -| pageSize | 当前每页数量,由 initialPageSize 决定 | number | - | -| data | 分页列表数组数据,由 data 配置得到 | any[] | - | -| total | 数据总数量,由 total 配置得到,可为空 | number | - | -| pageCount | 总页数,由 total 和 pageSize 计算得到 | number | - | -| isLastPage | 当前是否为最后一页,pageCount 有值时会通过 pageCount 和 page 对比得到,否则会通过列表数据长度是否少于 pagSize 得到 | number | - | -| fetching | 是否正在预加载数据 | boolean | - | - -### 操作函数 - -继承[**useWatcher**](/api/core-hooks#usewatcher)所有操作函数。 - -| 名称 | 描述 | 函数参数 | 返回值 | 版本 | -| ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------- | ------ | ---- | -| refresh | 刷新指定页码数据,此函数将忽略缓存强制发送请求,append 模式下可传入列表项表示刷新此列表项所在的页数 | pageOrItemPage: 刷新的页码或列表项 | - | - | -| insert | 插入一条数据,如果未传入 index,将默认插入到最前面,如果传入一个列表项,将插入到这个列表项的后面,如果列表项未在列表数据中将会抛出错误 | 1. item: 插入项
2. indexOrItem: 插入位置(索引)或列表项,默认为 0 | - | - | -| remove | 移除一条数据,当传入数字时表示移除的索引,当 position 传入的是列表项,将移除此列表项,如果列表项未在列表数据中将会抛出错误 | position: 移除位置(索引)或列表项 | - | - | -| replace | 替换一条数据,当第二个参数传入数字时表示替换索引,负数表示从末尾算起,当 position 传入的是列表项,将替换此列表项,如果列表项未在列表数据中将会抛出错误 | 1. item: 替换项
2. position: 替换位置(索引)或列表项,传入负数时表示从末尾开始算起 | - | - | -| reload | 清空数据,并重新请求第一页数据 | - | - | - | -| update | 更新状态数据,与 alova 的 use hook 用法相同,但在更新 data 字段时是更新列表数据 | newFrontStates:新的状态数据对象 | - | - | - -### 事件 - -继承[**useWatcher**](/api/core-hooks#usewatcher)所有事件。 - -| 名称 | 描述 | 回调参数 | 版本 | -| --------------- | ------------------------ | ------------------------- | ---- | -| onFetchSuccess | fetch 成功的回调绑定函数 | event: alova 成功事件对象 | - | -| onFetchError | fetch 失败的回调绑定函数 | event: alova 失败事件对象 | - | -| onFetchComplete | fetch 完成的回调绑定函数 | event: alova 完成事件对象 | - | +--- +title: 分页请求策略 +sidebar_position: 20 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info 策略类型 + +use hook + +::: + +> 在使用扩展 hooks 前,确保你已熟悉了 alova 的基本使用。 + +为分页场景下设计的 hook,它可以帮助你自动管理分页数据,数据预加载,减少不必要的数据刷新,**流畅性提高 300%,编码难度降低 50%**。你可以在下拉加载和页码翻页两种分页场景下使用它,此 hook 提供了丰富的特性,助你的应用打造性能更好,使用更便捷的分页功能。 + +## 示例 + +[页码列表](/tutorial/example/paginated-list) + +[下拉加载更多](/tutorial/example/load-more) + +## 特性 + +- 丰富全面的分页状态; +- 丰富全面的分页事件; +- 更改 page、pageSize 自动获取指定分页数据; +- 数据缓存,无需重复请求相同参数的列表数据; +- 前后页预加载,翻页不再等待; +- 搜索条件监听自动重新获取页数; +- 支持列表数据的新增、编辑、删除; +- 支持刷新指定页的数据,无需重置; +- 请求级搜索防抖,无需自行维护; + +## 安装 + + + + +```bash +# npm +npm install @alova/scene-vue --save +# yarn +yarn add @alova/scene-vue + +``` + + + + +```bash +# npm +npm install @alova/scene-react --save +# yarn +yarn add @alova/scene-react + +``` + + + + + +```bash +# npm +npm install @alova/scene-svelte --save +# yarn +yarn add @alova/scene-svelte + +``` + + + + +## 使用 + +### 展示列表数据 + + + + +```html + + + +``` + + + + +```jsx +import { queryStudents } from './api.js'; +import { usePagination } from '@alova/scene-react'; + +const App = () => { + const { + // 加载状态 + loading, + + // 列表数据 + data, + + // 是否为最后一页 + // 下拉加载时可通过此参数判断是否还需要加载 + isLastPage, + + // 当前页码,改变此页码将自动触发请求 + page: [page, setPage], + + // 每页数据条数 + pageSize: [page, setPageSize], + + // 分页页数 + pageCount, + + // 总数据量 + total + } = usePagination( + // Method实例获取函数,它将接收page和pageSize,并返回一个Method实例 + (page, pageSize) => queryStudents(page, pageSize), + { + // 请求前的初始数据(接口返回的数据格式) + initialData: { + total: 0, + data: [] + }, + initialPage: 1, // 初始页码,默认为1 + initialPageSize: 10 // 初始每页数据条数,默认为10 + } + ); + + // 翻到上一页,page值更改后将自动发送请求 + const handlePrevPage = () => { + setPage(value => value - 1); + }; + + // 翻到下一页,page值更改后将自动发送请求 + const handleNextPage = () => { + setPage(value => value + 1); + }; + + // 更改每页数量,pageSize值更改后将自动发送请求 + const handleSetPageSize = () => { + setPageSize(20); + }; + + return ( +
+ {data.map(item => ( +
+ {item.name} +
+ ))} + + + + 共有{pageCount}页 + 共有{total}条数据 +
+ ); +}; +``` + +
+ + +```html + + +{#each $data as item} +
+ {item.name} +
+{/each} + + + +共有{pageCount}页 +共有{total}条数据 +``` + +
+
+ +### 指定分页数据 + +每个分页数据接口返回的数据结构各不相同,因此我们需要分别告诉`usePagination`列表数据与总条数,从而帮助我们管理分页数据。 + +假如你的分页接口返回的数据格式是这样的: + +```typescript +interface PaginationData { + totalNumber: number; + list: any[]; +} +``` + +此时你需要通过函数的形式返回列表数据与总条数。 + +```javascript +usePagination((page, pageSize) => queryStudents(page, pageSize), { + // ... + // highlight-start + total: response => response.totalNumber, + data: response => response.list + // highlight-end +}); +``` + +如果不指定 total 和 data 回调函数,它们将默认通过以下方式获取数据。 + +```javascript +const total = response => response.total; +const data = response => response.data; +``` + +:::warning 注意 + +data 回调函数必须返回一个列表数据,表示分页中所使用的数据集合,而 total 主要用于计算当前页数,在 total 回调函数中如果未返回数字,将会通过请求的列表数量是否少于 pageSize 值来判断当前是否为最后一页,这一般用于下拉加载时使用。 + +::: + +### 开启追加模式 + +默认情况下,翻页时会替换原有的列表数据,而追加模式是在翻页时会将下一页的数据追加到当前列表底部,常见的使用场景是下拉加载更多。 + +```javascript +usePagination((page, pageSize) => queryStudents(page, pageSize), { + // ... + // highlight-start + append: true + // highlight-end +}); +``` + +### 预加载相邻页数据 + +为了让分页提供更好的体验,在当前页的上一页和下一页满足条件时将会自动预加载,这样在用户翻页时可直接显示数据而不需要等待,这是默认的行为。如果你不希望预加载相邻页的数据,可通过以下方式关闭。 + +```javascript +usePagination((page, pageSize) => queryStudents(page, pageSize), { + // ... + // highlight-start + preloadPreviousPage: false, // 关闭预加载上一页数据 + preloadNextPage: false // 关闭预加载下一页数据 + // highlight-end +}); +``` + +:::warning 预加载触发条件 + +在开启预加载时,并不会一味地加载下一页,需要满足以下两个条件: + +1. 预加载是基于缓存的,用于分页加载的 Method 实例必须开启缓存,默认情况下 get 请求会有 5 分钟的 memory 缓存,如果是非 get 请求或者全局关闭了缓存,你还需要在这个 Method 实例中单独设置`localCache`开启缓存。 +2. 根据`total`和`pageSize`参数判断出下一页还有数据。 + +::: + +除了`onSuccess、onError、onComplete`请求事件外,在触发了预加载时,你还可以通过`fetching`来获知预加载状态,还可以通过`onFetchSuccess、onFetchError、onFetchComplete`来监听预加载请求的事件。 + +```javascript +const { + // 预加载状态 + fetching, + + // 预加载成功事件绑定函数 + onFetchSuccess, + + // 预加载错误事件绑定函数 + onFetchError, + + // 预加载完成事件绑定函数 + onFetchComplete +} = usePagination((page, pageSize) => queryStudents(page, pageSize), { + // ... +}); +``` + +### 监听筛选条件 + +很多时候列表需要通过条件进行筛选,此时可以通过`usePagination`的状态监听来触发重新请求,这与 alova 提供的`useWatcher`是一样的。 + +例如通过学生姓名、学生年级进行筛选。 + + + + +```html + + + +``` + + + + +```jsx +import { queryStudents } from './api.js'; +import { usePagination } from '@alova/scene-react'; + +const App = () => { + // 搜索条件状态 + const [studentName, setStudentName] = useState(''); + const [clsName, setClsName] = useState(''); + const { + // ... + } = usePagination( + (page, pageSize) => queryStudents(page, pageSize, studentName, clsName), + { + // ... + // highlight-start + watchingStates: [studentName, clsName] + // highlight-end + } + ); + + return ( + // highlight-start + setStudentName(target.value)} /> + + // highlight-end + // ... + ); +}; +``` + + + + +```html + + + + + + + +``` + + + + +与`useWatcher`相同,你也可以通过指定`debounce`来实现请求防抖,具体可参考[useWatcher 的 debounce 参数设置](/api/core-hooks#usewatcher)。 + +```javascript +usePagination((page, pageSize) => queryStudents(page, pageSize, studentName, clsName), { + // ... + // highlight-start + debounce: 300 // 防抖参数,单位为毫秒数,也可以设置为数组对watchingStates单独设置防抖时间 + // highlight-end +}); +``` + +需要注意的是,`debounce`是通过 [**useWatcher**](/api/core-hooks#usewatcher) 中的请求防抖实现的。**监听状态末尾分别还有 page 和 pageSize 两个隐藏的监听状态,也可以通过 debounce 来设置。** + +举例来说,当`watchingStates`设置了`[studentName, clsName]`,内部将会监听`[studentName, clsName, page, pageSize]`,因此如果需要对 page 和 pageSize 设置防抖时,可以指定为`[0, 0, 500, 500]`。 + +### 关闭初始化请求 + +默认情况下,`usePagination`会在初始化时发起请求,但你也可以使用`immediate`关闭它,并在后续通过`send`函数,或者改变`page`或`pageSize`,以及`watchingStates`等监听状态来发起请求。 + +```javascript +usePagination((page, pageSize) => queryStudents(page, pageSize, studentName, clsName), { + // ... + // highlight-start + immediate: false + // highlight-end +}); +``` + +## 列表操作函数 + +usePagination 提供了功能完善的列表操作函数,它可以在不重新请求列表的情况下,做到与重新请求列表一致的效果,大大提高了页面的交互体验,具体的函数说明继续往下看吧! + +### 插入列表项 + +你可以用它插入列表项到列表任意位置,它将会在插入之后去掉末尾的一项,来保证和重新请求当前页数据一致的效果。 + +```typescript +/** + * 插入一条数据 + * 如果未传入index,将默认插入到最前面 + * 如果传入一个列表项,将插入到这个列表项的后面,如果列表项未在列表数据中将会抛出错误 + * @param item 插入项 + * @param indexOrItem 插入位置(索引) + */ +declare function insert(item: LD[number], indexOrItem?: number | LD[number]): void; +``` + +以下为**非 append 模式**下(页码翻页场景),返回第一页再插入列表项的示例: + +```javascript +page.value = 1; +nextTick(() => { + insert(newItem, 0); +}); +``` + +以下为在**append 模式**下(下拉加载场景),插入列表项后滚动到最顶部的示例: + +```javascript +insert(newItem, 0); +nextTick(() => { + window.scrollTo(0, {}); +}); +``` + +你也可以将`insert`的第二个参数指定为列表项,当查找到这个列表项的相同引用时,插入项将插入到这个列表项的后面。 + +```javascript +insert(newItem, afterItem); +``` + +:::warning 注意 + +为了让数据正确,insert 函数调用会清除全部缓存。 + +::: + +### 移除列表项 + +在下一页有缓存的情况下,它将会在移除一项后使用下一页的缓存补充到列表项尾部,来保证和重新请求当前页数据一致的效果,在**append 模式**和**非 append 模式**下表现相同。 + +```typescript +/** + * 移除一条数据 + * 如果传入的是列表项,将移除此列表项,如果列表项未在列表数据中将会抛出错误 + * @param position 移除的索引或列表项 + */ +declare function remove(position: number | LD[number]): void; +``` + +你也可以将`remove`的第二个参数指定为列表项,当查找到这个列表项的相同引用时,将会移除此列表项。 + +但在以下两种情况下,它将重新发起请求刷新对应页的数据: + +1. 下一页没有缓存 +2. 同步连续调用了超过下一页缓存列表项的数据,缓存数据已经不够补充到当前页列表了。 + +:::warning 注意 + +为了让数据正确,remove 函数调用会清除全部缓存。 + +::: + +### 更新数据项 + +当你想要更新列表项时,使用此函数实现。 + +```typescript +/** + * 替换一条数据 + * 当position传入数字时表示替换索引,负数表示从末尾算起,当 position 传入的是列表项,将替换此列表项,如果列表项未在列表数据中将会抛出错误 + * @param item 替换项 + * @param position 替换位置(索引)或列表项 + */ +declare function replace( + item: LD extends any[] ? LD[number] : any, + position: number | LD[number] +): void; +``` + +你也可以将`replace`的第二个参数指定为列表项,当查找到这个列表项的相同引用时,将会替换此列表项。 + +### 刷新指定页的数据 + +当你在数据操作后不希望本地更新列表项,而是重新请求服务端的数据,你可以用 refresh 刷新任意页的数据,而不需要重置列表数据让用户又从第一页开始浏览。 + +```typescript +/** + * 刷新指定页码数据,此函数将忽略缓存强制发送请求 + * 如果未传入页码则会刷新当前页 + * 如果传入一个列表项,将会刷新此列表项所在页 + * @param pageOrItemPage 刷新的页码或列表项 + */ +declare function refresh(pageOrItemPage?: number | LD[number]): void; +``` + +在 append 模式下,你可以将`refresh`的参数指定为列表项,当查找到这个列表项的相同引用时,刷新此列表项所在页数的数据。 + +### 手动更新列表数据 + +使用`update`函数更新响应式数据,这与[useRequest 的 update](/tutorial/combine-framework/use-request)相似,唯一不同的是,在调用`update`更新`data`时,更新的是列表数据,而非响应数据。这在手动清除列表数据,而不重新发起请求时很有用。 + +```typescript +// 情况列表数据 +update({ + data: [] +}); +``` + +### 重置列表 + +它将清空全部缓存,并重新加载第一页。 + +```typescript +/** + * 从第一页开始重新加载列表,并清空缓存 + */ +declare function reload(): void; +``` + +## 限制 + +**缓存占位模式** 暂时无效 + +## API + +### Hook 配置 + +继承[**useWatcher**](/api/core-hooks#usewatcher)所有配置。 + +| 名称 | 描述 | 类型 | 默认值 | 版本 | +| ------------------- | ---------------------------------------- | ------------------------- | -------------------------- | ---- | +| initialPage | 初始页码 | number | 1 | - | +| initialPageSize | 初始每页数据条数 | number | 10 | - | +| watchingStates | 状态监听触发请求,使用 useWatcher 实现 | any[] | [page, pageSize] | - | +| debounce | 状态监听的防抖参数,使用 useWatcher 实现 | number \| number[] | - | - | +| append | 是否开启追加模式 | boolean | false | - | +| data | 指定分页的数组数据 | (response: any) => any[] | response => response.data | - | +| total | 指定数据总数量值 | (response: any) => number | response => response.total | - | +| preloadPreviousPage | 是否预加载上一页数据 | boolean | true | - | +| preloadNextPage | 是否预加载下一页数据 | boolean | true | - | + +### 响应式数据 + +继承[**useWatcher**](/api/core-hooks#usewatcher)所有响应式数据。 + +| 名称 | 描述 | 类型 | 版本 | +| ---------- | ------------------------------------------------------------------------------------------------------------------ | ------- | ---- | +| page | 当前页码,由 initialPage 决定 | number | - | +| pageSize | 当前每页数量,由 initialPageSize 决定 | number | - | +| data | 分页列表数组数据,由 data 配置得到 | any[] | - | +| total | 数据总数量,由 total 配置得到,可为空 | number | - | +| pageCount | 总页数,由 total 和 pageSize 计算得到 | number | - | +| isLastPage | 当前是否为最后一页,pageCount 有值时会通过 pageCount 和 page 对比得到,否则会通过列表数据长度是否少于 pagSize 得到 | number | - | +| fetching | 是否正在预加载数据 | boolean | - | + +### 操作函数 + +继承[**useWatcher**](/api/core-hooks#usewatcher)所有操作函数。 + +| 名称 | 描述 | 函数参数 | 返回值 | 版本 | +| ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------- | ------ | ---- | +| refresh | 刷新指定页码数据,此函数将忽略缓存强制发送请求,append 模式下可传入列表项表示刷新此列表项所在的页数 | pageOrItemPage: 刷新的页码或列表项 | - | - | +| insert | 插入一条数据,如果未传入 index,将默认插入到最前面,如果传入一个列表项,将插入到这个列表项的后面,如果列表项未在列表数据中将会抛出错误 | 1. item: 插入项
2. indexOrItem: 插入位置(索引)或列表项,默认为 0 | - | - | +| remove | 移除一条数据,当传入数字时表示移除的索引,当 position 传入的是列表项,将移除此列表项,如果列表项未在列表数据中将会抛出错误 | position: 移除位置(索引)或列表项 | - | - | +| replace | 替换一条数据,当第二个参数传入数字时表示替换索引,负数表示从末尾算起,当 position 传入的是列表项,将替换此列表项,如果列表项未在列表数据中将会抛出错误 | 1. item: 替换项
2. position: 替换位置(索引)或列表项,传入负数时表示从末尾开始算起 | - | - | +| reload | 清空数据,并重新请求第一页数据 | - | - | - | +| update | 更新状态数据,与 alova 的 use hook 用法相同,但在更新 data 字段时是更新列表数据 | newFrontStates:新的状态数据对象 | - | - | + +### 事件 + +继承[**useWatcher**](/api/core-hooks#usewatcher)所有事件。 + +| 名称 | 描述 | 回调参数 | 版本 | +| --------------- | ------------------------ | ------------------------- | ---- | +| onFetchSuccess | fetch 成功的回调绑定函数 | event: alova 成功事件对象 | - | +| onFetchError | fetch 失败的回调绑定函数 | event: alova 失败事件对象 | - | +| onFetchComplete | fetch 完成的回调绑定函数 | event: alova 完成事件对象 | - | diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/03-useForm.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/03-useForm.md index 9625d6aee..1ed893ee2 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/03-useForm.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/03-useForm.md @@ -1,525 +1,525 @@ ---- -title: 表单提交策略 -sidebar_position: 30 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -:::info 策略类型 - -use hook - -::: - -> 在使用扩展 hooks 前,确保你已熟悉了 alova 的基本使用。 - -为表单提交而设计的 hook,通过此 hook 你可以很方便地实现表单草稿、多页面(多步骤)表单,除此以外还提供了表单重置等常用功能。 - -## 示例 - -[表单提交 Demo](/tutorial/example/form-hook) - -## 特性 - -- ✨ 表单草稿; -- ✨ 多页面(多步骤)表单; -- ✨ 表单提交自动重置数据; -- ✨ 手动重置表单数据; - -## 安装 - - - - -```bash -# npm -npm install @alova/scene-vue --save -# yarn -yarn add @alova/scene-vue - -``` - - - - -```bash -# npm -npm install @alova/scene-react --save -# yarn -yarn add @alova/scene-react - -``` - - - - - -```bash -# npm -npm install @alova/scene-svelte --save -# yarn -yarn add @alova/scene-svelte - -``` - - - - -## 使用 - -### 基本用法 - -展示表单 hook 的基本使用。 - - - - -```html - - - -``` - - - - -```jsx -import { formSubmit } from './api.js'; -import { useForm } from '@alova/scene-react'; - -const App = () => { - const { - // 提交状态 - loading: submiting, - - // 响应式的表单数据,内容由initialForm决定 - form, - - // 提交数据函数 - send: submit, - - // 更新表单项 - updateForm, - - // 提交成功回调绑定 - onSuccess, - - // 提交失败回调绑定 - onError, - - // 提交完成回调绑定 - onComplete - } = useForm( - formData => { - // 可以在此转换表单数据并提交 - return formSubmit(formData); - }, - { - // 初始化表单数据 - initialForm: { - name: '', - cls: '1' - } - } - ); - - // 提交表单数据 - const handleSubmit = () => { - // 验证表单数据... - submit(); - }; - - return ( -
- updateForm({ name: target.value })} - /> - - -
- ); -}; -``` - -
- - -```html - - - - - -``` - - -
- -`useForm`默认不会请求,在调用`send`后才会发出请求,同时在`useForm`的回调函数将传入最新的 form 数据,如果需要在提交前转换数据,可在此进行转换,也可以在`formSubmit`函数中转换。 - -:::warning 注意 - -1. `initialForm`是设置初始表单数据,`initialData`是设置初始响应数据,注意区分; -2. `updateForm`是更新表单数据,`update`是更新响应数据,注意区分; - -::: - -以上示例只展示了简单的表单提交功能,普通的表单提交没有差别,但`useForm`还实现了更多的实用功能,让我们继续往下看。 - -### 提交自动重置表单 - -很多时候,我们都需要在表单提交后重置表单数据,在自行实现时我们总是需要手动一个一个重新赋值,而`useForm`可以帮我们自动完成。 - -```javascript -useForm(submitData, { - // ... - // highlight-start - // 设置这个参数为true即可在提交完成后自动重置表单数据 - resetAfterSubmiting: true - // highlight-end -}); -``` - -如果你需要手动重置表单数据,也可以通过调用`reset`函数做到。 - -```javascript -const { - // highlight-start - // 表单重置函数 - reset - // highlight-end -} = useForm(submitData, { - // ... -}); - -// highlight-start -const handleReset = () => { - reset(); -}; -// highlight-end -``` - -### 更新表单数据 - -在编辑表单时,我们需要展示原表单的数据,此时可以使用`updateForm`来异步更新表单数据。 - -```javascript -const { - // ... - updateForm -} = useForm(submitData, { - // ... - { - // 初始化表单数据 - initialForm: { - name: '', - cls: '1' - } - } -}); - -// 请求表单数据并更新到表单中 -const { onSuccess } = useRequest(getData); -onSuccess(({ data }) => { - updateForm({ - name: data.name, - cls: data.cls - }); -}); -``` - -### 表单草稿 - -`useForm`还提供了表单草稿功能,在数据重置前即使刷新页面也可以恢复表单数据,其原理是使用 alova 实例上的存储适配器,将表单数据进行持久化。使用时只需要将`store`设置为 true 即可。 - -```javascript -useForm(submitData, { - // ... - // highlight-start - // 开启持久化保存数据,设置为true后将实时持久化未提交的数据 - store: true - // highlight-end -}); -``` - -数据持久化前将会调用`JSON.stringify`转换为 JSON 字符串,在默认情况下,表单数据在持久化保存时将会进行数据序列化,`useForm`内置了`Date`和`RegExp`实例的序列化,这在使用时间选择器时将很有用。 - -在表单数据只涉及到`Date`和`RegExp`实例时你无需进行更多操作,但如果有其他非 JSON 数据时,例如`moment`实例,我们需要自定义序列化器,不过别担心,自定义序列化器很简单,以下将展示设置一个`moment`序列化器。 - -```javascript -import moment from 'moment'; -const momentSerializer = { - // forward在序列化时被调用 - // 需要判断是否为moment实例,如果不是目标值则返回undefined,表示不处理它 - forward: data => moment.isMoment(data) ? data.valueOf() : undefined, - - // backward在反序列化时被调用,data为forward中返回的值 - backward: timestamp => moment(timestamp); -}; - -useForm( - submitData, - { - store: { - enable: true, - serializers: { - moment: momentSerializer - } - } - } -); -``` - -### 多页面/多步骤表单 - -很多时候我们会遇到表单项分为了多个页面,或多个步骤进行填写,并在最后统一提交的情况,例如多步骤的用户注册、填写问卷等,以及多个步骤的表单可能有相互依赖关系,如果自行实现将会带来一定麻烦。而`useForm`实现了表单数据共享,你可以在不同的页面或组件中获取到同一份表单数据,解决了多步骤表单数据依赖的问题,也不需要在提交时汇总表单数据,可直接提交。 - -使用时,你需要通过`useForm`设置 id,通过相同的 id 你可以在不同页面间共享同一份表单数据。例如我们有一个表单需要经过 3 个步骤填写表单,它们分别会经过组件 A、组件 B、组件 C。 - -``` -组件A -> 组件B -> 组件C -``` - -此时,我们可以在组件 A 内初始化表单数据: - -```javascript title=组件A -const returnStates = useForm(submitData, { - initialForm: { - step1Input: '', - step2Input: '', - step3Input: '' - }, - // highlight-start - id: 'testForm' - // highlight-end -}); -const { form, send } = returnStates; -``` - -在组件 B、组件 C 中可直接在第一个参数中传入 id,获取组件 A 内的共享数据。 - -```javascript title=组件B、组件C -const returnStates = useForm('testForm'); -const { form, send } = returnStates; -``` - -组件 B、C 内通过 id 返回的 `returnStates` 与组件 A 内的 `returnStates` 是相同的引用,你可以使用同一个 `form`,也可以在任意一个组件内调用 `send` 统一提交表单数据。 - -**额外的** - -在组件 B、C 中直接指定 id 的方式获取共享数据时,这个 id 必须先初始化表单数据,就像在组件 A 中那样,否则将会抛出`the form data of id {1} is not initial`错误。如果你的多步骤表单并不是按一定顺序,而是根据一定条件随机顺序的,例如: - -```bash -# 可能的顺序1 -组件B -> 组件A -> 组件C - -# 可能的顺序2 -组件A -> 组件C -> 组件B - -# 可能的顺序3 -组件C -> 组件A -> 组件B - -# ... -``` - -在这种情况下,你可以在组件 B、C 中像组件 A 一样使用`useForm`。 - -```javascript title=组件B、组件C -const returnStates = useForm(submitData, { - initialForm: { - step1Input: '', - step2Input: '', - step3Input: '' - }, - id: 'testForm' -}); -``` - -这样无论先渲染哪个组件都可以对 id 为 testForm 的表单初始化,后面的组件在遇到 id 为 testForm 时将优先使用已初始化的表单数据,而不会再次初始化。这样你就可以在任意组件内初始化表单数据。 - -> 更详细的多步骤表单也可以在[表单提交 Demo](/tutorial/example/form-hook)中体验和查看。 - -### 条件筛选 - -`useForm`还可以用于对数据筛选场景所用到的筛选表单,例如你希望通过城市名搜索城市信息,你可以设置`immedidate=true`,它将会在初始化时就开始查询数据,在之后的操作中再调用`send`重复查询数据。 - -```javascript -const { send: searchData } = useForm(queryCity, { - initialForm: { - cityName: '' - }, - immediate: true -}); -``` - -:::warning 条件限制 - -在条件筛选场景下,`useForm`更适用于非分页的列表条件查询,如果你需要在分页列表中进行条件查询,建议使用 [分页请求策略(usePagination)](/tutorial/strategy/usePagination)。 - -::: - -## API - -### Hook 配置 - -继承[**useRequest**](/api/core-hooks#userequest)所有配置。 - -| 名称 | 描述 | 类型 | 默认值 | 版本 | -| ------------------- | -------------------------------------------------------------------------------------------------------- | -------------------------------------------------- | ------ | ---- | -| initialForm | 初始表单数据 | any | - | - | -| id | form id,相同 id 的 data 数据是同一份引用,可以用于在多页表单时共用同一份表单数据。单页表单不需要指定 id | string \| number | - | - | -| store | 是否持久化保存数据,设置为 true 后将实时持久化未提交的数据 | boolean \| [StoreDetailConfig](#storedetailconfig) | false | - | -| resetAfterSubmiting | 提交后重置数据 | boolean | false | - | - -### 响应式数据 - -继承[**useRequest**](/api/core-hooks#userequest)所有响应式数据。 - -| 名称 | 描述 | 类型 | 版本 | -| ---- | ----------------------------- | ---- | ---- | -| form | 表单数据,由 initialForm 决定 | any | - | - -#### StoreDetailConfig - -| 名称 | 描述 | 类型 | 默认值 | 版本 | -| ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | -------- | ---- | -| enable | 是否启用持久化数据 | boolean | required | - | -| serializers | 自定义序列化器的集合,内置的序列化器:
1. date 序列化器用于转换日期
2. regexp 序列化器用于转化正则表达式
可以通过设置同名序列化器来覆盖内置序列化器 | Record\ | - | - | - -#### DataSerializer - -| 名称 | 描述 | 类型 | 默认值 | 版本 | -| -------- | ----------------------------------------------------------------------------------------------------- | --------------------------------------- | -------- | ---- | -| forward | 序列化函数,forward 中序列化时需判断是否为指定的数据,并返回转换后的数据,否则返回 undefined 或不返回 | (data: any) => any \| undefined \| void | required | - | -| backward | 反序列化函数,直接反序列化数据 | (data: any) => any \| undefined \| void | required | - | - -### 操作函数 - -继承[**useRequest**](/api/core-hooks#userequest)所有操作函数。 - -| 名称 | 描述 | 函数参数 | 返回值 | 版本 | -| ---------- | ------------------------------------------ | ---------------------------------------------------------------------- | ------ | ---- | -| updateForm | 更新一项或多项表单数据 | newForm: Partial\ \| (oldForm: F) => F)
F 为`initialForm`类型 | - | - | -| reset | 重置为初始化数据,如果有持久化数据也会清空 | - | - | - | - -### 事件 - -继承[**useRequest**](/api/core-hooks#userequest)所有事件。 - -| 名称 | 描述 | 回调参数 | 版本 | -| --------- | -------------------- | -------- | ---- | -| onRestore | 持久化数据恢复后触发 | - | - | +--- +title: 表单提交策略 +sidebar_position: 30 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info 策略类型 + +use hook + +::: + +> 在使用扩展 hooks 前,确保你已熟悉了 alova 的基本使用。 + +为表单提交而设计的 hook,通过此 hook 你可以很方便地实现表单草稿、多页面(多步骤)表单,除此以外还提供了表单重置等常用功能。 + +## 示例 + +[表单提交 Demo](/tutorial/example/form-hook) + +## 特性 + +- 表单草稿; +- 多页面(多步骤)表单; +- 表单提交自动重置数据; +- 手动重置表单数据; + +## 安装 + + + + +```bash +# npm +npm install @alova/scene-vue --save +# yarn +yarn add @alova/scene-vue + +``` + + + + +```bash +# npm +npm install @alova/scene-react --save +# yarn +yarn add @alova/scene-react + +``` + + + + + +```bash +# npm +npm install @alova/scene-svelte --save +# yarn +yarn add @alova/scene-svelte + +``` + + + + +## 使用 + +### 基本用法 + +展示表单 hook 的基本使用。 + + + + +```html + + + +``` + + + + +```jsx +import { formSubmit } from './api.js'; +import { useForm } from '@alova/scene-react'; + +const App = () => { + const { + // 提交状态 + loading: submiting, + + // 响应式的表单数据,内容由initialForm决定 + form, + + // 提交数据函数 + send: submit, + + // 更新表单项 + updateForm, + + // 提交成功回调绑定 + onSuccess, + + // 提交失败回调绑定 + onError, + + // 提交完成回调绑定 + onComplete + } = useForm( + formData => { + // 可以在此转换表单数据并提交 + return formSubmit(formData); + }, + { + // 初始化表单数据 + initialForm: { + name: '', + cls: '1' + } + } + ); + + // 提交表单数据 + const handleSubmit = () => { + // 验证表单数据... + submit(); + }; + + return ( +
+ updateForm({ name: target.value })} + /> + + +
+ ); +}; +``` + +
+ + +```html + + + + + +``` + + +
+ +`useForm`默认不会请求,在调用`send`后才会发出请求,同时在`useForm`的回调函数将传入最新的 form 数据,如果需要在提交前转换数据,可在此进行转换,也可以在`formSubmit`函数中转换。 + +:::warning 注意 + +1. `initialForm`是设置初始表单数据,`initialData`是设置初始响应数据,注意区分; +2. `updateForm`是更新表单数据,`update`是更新响应数据,注意区分; + +::: + +以上示例只展示了简单的表单提交功能,普通的表单提交没有差别,但`useForm`还实现了更多的实用功能,让我们继续往下看。 + +### 提交自动重置表单 + +很多时候,我们都需要在表单提交后重置表单数据,在自行实现时我们总是需要手动一个一个重新赋值,而`useForm`可以帮我们自动完成。 + +```javascript +useForm(submitData, { + // ... + // highlight-start + // 设置这个参数为true即可在提交完成后自动重置表单数据 + resetAfterSubmiting: true + // highlight-end +}); +``` + +如果你需要手动重置表单数据,也可以通过调用`reset`函数做到。 + +```javascript +const { + // highlight-start + // 表单重置函数 + reset + // highlight-end +} = useForm(submitData, { + // ... +}); + +// highlight-start +const handleReset = () => { + reset(); +}; +// highlight-end +``` + +### 更新表单数据 + +在编辑表单时,我们需要展示原表单的数据,此时可以使用`updateForm`来异步更新表单数据。 + +```javascript +const { + // ... + updateForm +} = useForm(submitData, { + // ... + { + // 初始化表单数据 + initialForm: { + name: '', + cls: '1' + } + } +}); + +// 请求表单数据并更新到表单中 +const { onSuccess } = useRequest(getData); +onSuccess(({ data }) => { + updateForm({ + name: data.name, + cls: data.cls + }); +}); +``` + +### 表单草稿 + +`useForm`还提供了表单草稿功能,在数据重置前即使刷新页面也可以恢复表单数据,其原理是使用 alova 实例上的存储适配器,将表单数据进行持久化。使用时只需要将`store`设置为 true 即可。 + +```javascript +useForm(submitData, { + // ... + // highlight-start + // 开启持久化保存数据,设置为true后将实时持久化未提交的数据 + store: true + // highlight-end +}); +``` + +数据持久化前将会调用`JSON.stringify`转换为 JSON 字符串,在默认情况下,表单数据在持久化保存时将会进行数据序列化,`useForm`内置了`Date`和`RegExp`实例的序列化,这在使用时间选择器时将很有用。 + +在表单数据只涉及到`Date`和`RegExp`实例时你无需进行更多操作,但如果有其他非 JSON 数据时,例如`moment`实例,我们需要自定义序列化器,不过别担心,自定义序列化器很简单,以下将展示设置一个`moment`序列化器。 + +```javascript +import moment from 'moment'; +const momentSerializer = { + // forward在序列化时被调用 + // 需要判断是否为moment实例,如果不是目标值则返回undefined,表示不处理它 + forward: data => moment.isMoment(data) ? data.valueOf() : undefined, + + // backward在反序列化时被调用,data为forward中返回的值 + backward: timestamp => moment(timestamp); +}; + +useForm( + submitData, + { + store: { + enable: true, + serializers: { + moment: momentSerializer + } + } + } +); +``` + +### 多页面/多步骤表单 + +很多时候我们会遇到表单项分为了多个页面,或多个步骤进行填写,并在最后统一提交的情况,例如多步骤的用户注册、填写问卷等,以及多个步骤的表单可能有相互依赖关系,如果自行实现将会带来一定麻烦。而`useForm`实现了表单数据共享,你可以在不同的页面或组件中获取到同一份表单数据,解决了多步骤表单数据依赖的问题,也不需要在提交时汇总表单数据,可直接提交。 + +使用时,你需要通过`useForm`设置 id,通过相同的 id 你可以在不同页面间共享同一份表单数据。例如我们有一个表单需要经过 3 个步骤填写表单,它们分别会经过组件 A、组件 B、组件 C。 + +``` +组件A -> 组件B -> 组件C +``` + +此时,我们可以在组件 A 内初始化表单数据: + +```javascript title=组件A +const returnStates = useForm(submitData, { + initialForm: { + step1Input: '', + step2Input: '', + step3Input: '' + }, + // highlight-start + id: 'testForm' + // highlight-end +}); +const { form, send } = returnStates; +``` + +在组件 B、组件 C 中可直接在第一个参数中传入 id,获取组件 A 内的共享数据。 + +```javascript title=组件B、组件C +const returnStates = useForm('testForm'); +const { form, send } = returnStates; +``` + +组件 B、C 内通过 id 返回的 `returnStates` 与组件 A 内的 `returnStates` 是相同的引用,你可以使用同一个 `form`,也可以在任意一个组件内调用 `send` 统一提交表单数据。 + +**额外的** + +在组件 B、C 中直接指定 id 的方式获取共享数据时,这个 id 必须先初始化表单数据,就像在组件 A 中那样,否则将会抛出`the form data of id {1} is not initial`错误。如果你的多步骤表单并不是按一定顺序,而是根据一定条件随机顺序的,例如: + +```bash +# 可能的顺序1 +组件B -> 组件A -> 组件C + +# 可能的顺序2 +组件A -> 组件C -> 组件B + +# 可能的顺序3 +组件C -> 组件A -> 组件B + +# ... +``` + +在这种情况下,你可以在组件 B、C 中像组件 A 一样使用`useForm`。 + +```javascript title=组件B、组件C +const returnStates = useForm(submitData, { + initialForm: { + step1Input: '', + step2Input: '', + step3Input: '' + }, + id: 'testForm' +}); +``` + +这样无论先渲染哪个组件都可以对 id 为 testForm 的表单初始化,后面的组件在遇到 id 为 testForm 时将优先使用已初始化的表单数据,而不会再次初始化。这样你就可以在任意组件内初始化表单数据。 + +> 更详细的多步骤表单也可以在[表单提交 Demo](/tutorial/example/form-hook)中体验和查看。 + +### 条件筛选 + +`useForm`还可以用于对数据筛选场景所用到的筛选表单,例如你希望通过城市名搜索城市信息,你可以设置`immedidate=true`,它将会在初始化时就开始查询数据,在之后的操作中再调用`send`重复查询数据。 + +```javascript +const { send: searchData } = useForm(queryCity, { + initialForm: { + cityName: '' + }, + immediate: true +}); +``` + +:::warning 条件限制 + +在条件筛选场景下,`useForm`更适用于非分页的列表条件查询,如果你需要在分页列表中进行条件查询,建议使用 [分页请求策略(usePagination)](/tutorial/strategy/usePagination)。 + +::: + +## API + +### Hook 配置 + +继承[**useRequest**](/api/core-hooks#userequest)所有配置。 + +| 名称 | 描述 | 类型 | 默认值 | 版本 | +| ------------------- | -------------------------------------------------------------------------------------------------------- | -------------------------------------------------- | ------ | ---- | +| initialForm | 初始表单数据 | any | - | - | +| id | form id,相同 id 的 data 数据是同一份引用,可以用于在多页表单时共用同一份表单数据。单页表单不需要指定 id | string \| number | - | - | +| store | 是否持久化保存数据,设置为 true 后将实时持久化未提交的数据 | boolean \| [StoreDetailConfig](#storedetailconfig) | false | - | +| resetAfterSubmiting | 提交后重置数据 | boolean | false | - | + +### 响应式数据 + +继承[**useRequest**](/api/core-hooks#userequest)所有响应式数据。 + +| 名称 | 描述 | 类型 | 版本 | +| ---- | ----------------------------- | ---- | ---- | +| form | 表单数据,由 initialForm 决定 | any | - | + +#### StoreDetailConfig + +| 名称 | 描述 | 类型 | 默认值 | 版本 | +| ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | -------- | ---- | +| enable | 是否启用持久化数据 | boolean | required | - | +| serializers | 自定义序列化器的集合,内置的序列化器:
1. date 序列化器用于转换日期
2. regexp 序列化器用于转化正则表达式
可以通过设置同名序列化器来覆盖内置序列化器 | Record\ | - | - | + +#### DataSerializer + +| 名称 | 描述 | 类型 | 默认值 | 版本 | +| -------- | ----------------------------------------------------------------------------------------------------- | --------------------------------------- | -------- | ---- | +| forward | 序列化函数,forward 中序列化时需判断是否为指定的数据,并返回转换后的数据,否则返回 undefined 或不返回 | (data: any) => any \| undefined \| void | required | - | +| backward | 反序列化函数,直接反序列化数据 | (data: any) => any \| undefined \| void | required | - | + +### 操作函数 + +继承[**useRequest**](/api/core-hooks#userequest)所有操作函数。 + +| 名称 | 描述 | 函数参数 | 返回值 | 版本 | +| ---------- | ------------------------------------------ | ---------------------------------------------------------------------- | ------ | ---- | +| updateForm | 更新一项或多项表单数据 | newForm: Partial\ \| (oldForm: F) => F)
F 为`initialForm`类型 | - | - | +| reset | 重置为初始化数据,如果有持久化数据也会清空 | - | - | - | + +### 事件 + +继承[**useRequest**](/api/core-hooks#userequest)所有事件。 + +| 名称 | 描述 | 回调参数 | 版本 | +| --------- | -------------------- | -------- | ---- | +| onRestore | 持久化数据恢复后触发 | - | - | diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/04-tokenAuthentication.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/04-tokenAuthentication.md index 18ea75a98..f032d35e0 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/04-tokenAuthentication.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/04-tokenAuthentication.md @@ -1,552 +1,585 @@ ---- -title: Token认证拦截器 -sidebar_position: 40 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -:::info - -策略类型:拦截器 - -版本要求:v1.3.0+ - -::: - -> 在使用扩展 hooks 前,确保你已熟悉了 alova 的基本使用。 - -Token 认证拦截器,对基于 token 的登录、登出、token 附带、token 刷新进行统一管理,并支持无感刷新 token。 - -## 特性 - -- ✨ 统一维护 Token 身份认证的所有代码,包括登录、登出、token 附带、token 刷新等; -- ✨ 支持在客户端和服务端验证 token 过期,并无感刷新 token; -- ✨ 依赖 token 的请求自动等待 token 刷新完成再请求; -- ✨ 使用元数据设置请求身份; -- ✨ 自动放行不依赖 token 的访客请求; - -## 安装 - - - - -```bash -# npm -npm install @alova/scene-vue --save -# yarn -yarn add @alova/scene-vue - -``` - - - - -```bash -# npm -npm install @alova/scene-react --save -# yarn -yarn add @alova/scene-react - -``` - - - - - -```bash -# npm -npm install @alova/scene-svelte --save -# yarn -yarn add @alova/scene-svelte - -``` - - - - -:::info - -以下所有拦截器都是可选的,只需要选择需要使用的即可。 - -::: - -## 绑定 Token 认证拦截器 - -Token 身份认证是通过全局的拦截器完成的,分别提供了`createClientTokenAuthentication`和`createServerTokenAuthentication` 用于基于客户端和服务端的身份认证。 - -- 基于客户端的身份认证:表示从客户端判断 token 是否过期,例如在登录时获取到的 token 过期时间; -- 基于服务端的身份认证:表示从服务端返回的状态判断 token 是否过期,例如`status`为 401 时表示过期; - -### 绑定基于客户端的身份认证的拦截器 - -```javascript -import { createClientTokenAuthentication } from '@alova/scene-*'; -import { createAlova } from 'alova'; - -const { onAuthRequired, onResponseRefreshToken } = createClientTokenAuthentication({ - // ... -}); - -const alovaInstance = createAlova({ - // ... - beforeRequest: onAuthRequired(method => { - // ...原请求前拦截器 - }), - responded: onResponseRefreshToken((response, method) => { - //...原响应成功拦截器 - return response.json(); - }) -}); -``` - -在`onResponseRefreshToken`中也可以绑定响应错误和完成的拦截器,也和原来的用法相同。 - -```javascript -createAlova({ - // ... - // highlight-start - responded: onResponseRefreshToken({ - onSuccess: (response, method) => { - //...原响应成功拦截器 - }, - onError: (error, method) => { - //...原响应错误拦截器 - }, - onComplete: method => { - //...原响应完成拦截器 - } - }) - // highlight-end -}); -``` - -如果不需要设置拦截器,也可以不传入拦截器函数。 - -```javascript -createAlova({ - //... - // highlight-start - beforeRequest: onAuthRequired(), - responded: onResponseRefreshToken() - // highlight-end -}); -``` - -### 绑定基于服务端的身份认证拦截器 - -与基于客户端的用法相同 - -```javascript -import { createServerTokenAuthentication } from '@alova/scene-*'; -import { createAlova } from 'alova'; - -const { onAuthRequired, onResponseRefreshToken } = createServerTokenAuthentication({ - // ... -}); - -const alovaInstance = createAlova({ - // ... - beforeRequest: onAuthRequired(method => { - // ...原请求前拦截器 - }), - responded: onResponseRefreshToken((response, method) => { - //...原响应成功拦截器 - return response.json(); - }) -}); -``` - -:::warning - -当你使用`GlobalFetch`适配器时,你可能会遇到`TypeError: Failed to execute 'json' on 'Response': body stream already read`这个问题,这是因为`Response`的`body stream`只能访问一次,你可以`response.clone().json()`来解决它。 - -::: - -## 在客户端无感刷新 Token - -设置`refreshToken`并指定 token 是否过期,以及调用刷新 token 的函数就可以了。当 token 刷新完成前,所有依赖 token 的请求都将会等待 token 刷新完成。 - -```javascript -createClientTokenAuthentication({ - refreshToken: { - // 在请求前触发,将接收到method参数,并返回boolean表示token是否过期 - isExpired: method => { - return tokenExpireTime < Date.now(); - }, - - // 当token过期时触发,在此函数中触发刷新token - handler: async method => { - const { token, refresh_token } = await refreshToken(); - localStorage.setItem('token', token); - localStorage.setItem('refresh_token', refresh_token); - } - } -}); -``` - -为了让`refreshToken`请求顺利通过,需要通过元数据标识`authRole`为`refreshToken`。 - -> 了解更多元数据的信息,请前往[method 元数据](/tutorial/getting-started/method-metadata)。 - -```javascript -export const refreshToken = () => { - const method = alovaInstance.Get('/refresh_token'); - method.meta = { - authRole: 'refreshToken' - }; - return method; -}; -``` - -## 在服务端无感刷新 Token - -与在客户端无感刷新 Token 相同,指定 token 是否过期,以及调用刷新 token 的函数就可以了。当 token 刷新完成前,所有依赖 token 的请求都将会等待 token 刷新完成。 - -### 在请求成功拦截器中处理 - -当使用`GlobalFetch`时,只要服务端返回了响应数据,就会触发响应成功拦截器,此时我们需要在响应成功拦截器中处理 token 的刷新。 - -```javascript -createServerTokenAuthentication({ - refreshTokenOnSuccess: { - // 响应时触发,可获取到response和method,并返回boolean表示token是否过期 - // 当服务端返回401时,表示token过期 - isExpired: (response, method) => { - return response.status === 401; - }, - - // 当token过期时触发,在此函数中触发刷新token - handler: async (response, method) => { - const { token, refresh_token } = await refreshToken(); - localStorage.setItem('token', token); - localStorage.setItem('refresh_token', refresh_token); - } - } -}); -``` - -### 在请求错误拦截器中处理 - -当使用`axios`拦截器时,服务端返回了非`200/300`的状态码就会触发响应错误拦截器,此时我们需要在响应错误拦截器中处理 token 的刷新。 - -```javascript -createServerTokenAuthentication({ - refreshTokenOnError: { - // 响应时触发,可获取到error和method,并返回boolean表示token是否过期 - // 当服务端返回401时,表示token过期 - isExpired: (error, method) => { - return error.response.status === 401; - }, - - // 当token过期时触发,在此函数中触发刷新token - handler: async (error, method) => { - const { token, refresh_token } = await refreshToken(); - localStorage.setItem('token', token); - localStorage.setItem('refresh_token', refresh_token); - } - } -}); -``` - -为了让`refreshToken`请求顺利通过,需要通过元数据标识`authRole`为`refreshToken`。 - -> 了解更多元数据的信息,请前往[method 元数据](/tutorial/getting-started/method-metadata)。 - -```javascript -export const refreshToken = () => { - const method = alovaInstance.Get('/refresh_token'); - method.meta = { - authRole: 'refreshToken' - }; - return method; -}; -``` - -## 放行访客请求 - -有些接口不需要依赖 token 认证,我们称它们为“访客请求”,此时我们可以设置它们的元数据为`authRole: null`来绕过前端的拦截,让它们顺利发出请求和接收响应。 - -```javascript -export const requestTokenNotRequired = () => { - const method = alovaInstance.Get('/token_not_required'); - method.meta = { - authRole: null - }; - return method; -}; -``` - -## 登录拦截 - -在身份认证拦截器中,你还可以拦截登录请求,在拦截器中保存登录信息,达到统一维护身份认证代码的目的。 - -首先标识登录请求的元数据为`authRole: 'login'`。 - -```javascript -export const login = () => { - const method = alovaInstance.Get('/login'); - method.meta = { - authRole: 'login' - }; - return method; -}; -``` - -再在登录拦截器中保存登录信息。 - -```javascript -createClientTokenAuthentication({ - login(response, method) { - localStorage.setItem('token', response.token); - localStorage.setItem('refresh_token', response.refresh_token); - } -}); -``` - -> `createServerTokenAuthentication`的登录拦截器用法相同。 - -## 附加 token - -通常,我们会在`beforeRequest`附加 token 到请求信息中。在 Token 认证拦截器中提供了`assignToken`回调函数用于附加 token,它会过滤访客请求和登录请求,并在请求前触发,也可以达到统一维护身份认证代码的目的。 - -```javascript -createClientTokenAuthentication({ - assignToken: method => { - method.config.headers.Authorization = localStorage.getItem('token')}; - } -}); -``` - -> `createServerTokenAuthentication`的 assignToken 回调函数用法相同。 - -## 登出拦截 - -当你的登出也需要调用接口时,也可以拦截登出请求,清除登录信息。 - -首先标识登出请求的元数据为`authRole: 'logout'`。 - -```javascript -export const logout = () => { - const method = alovaInstance.Get('/logout'); - method.meta = { - authRole: 'logout' - }; - return method; -}; -``` - -再在登出拦截器中清除登录信息。 - -```javascript -createClientTokenAuthentication({ - logout(response, method) { - localStorage.removeItem('token'); - localStorage.removeItem('refresh_token'); - } -}); -``` - -> `createServerTokenAuthentication`的登录拦截器用法相同。 - -## 自定义标识身份 - -在上面的元数据身份标识中,实际上都默认的身份标识,如果需要自定义身份标识,你可以按下面这样设置。 - -### token 刷新身份标识 - -```javascript -createClientTokenAuthentication({ - refreshToken: { - // highlight-start - metaMatches: { - refreshToken: true - } - // highlight-end - // ... - } -}); -``` - -```javascript -createServerTokenAuthentication({ - refreshTokenOnSuccess: { - // highlight-start - metaMatches: { - refreshToken: true - } - // highlight-end - // ... - }, - refreshTokenOnError: { - // highlight-start - metaMatches: { - refreshToken: true - } - // highlight-end - // ... - } -}); -``` - -然后,元数据中具有`refreshToken: true`的请求,就会被认定为`refreshToken`身份。 - -```javascript -export const refreshToken = () => { - const method = alovaInstance.Get('/refresh_token'); - method.meta = { - refreshToken: true - }; - return method; -}; -``` - -### 访客身份标识 - -```javascript -createClientTokenAuthentication({ - visitorMeta: { - isVisitor: true - } -}); -``` - -然后,元数据中具有`isVisitor: true`的请求,就会被认定为访客身份。 - -```javascript -export const requestTokenNotRequired = () => { - const method = alovaInstance.Get('/token_not_required'); - method.meta = { - isVisitor: true - }; - return method; -}; -``` - -### 登录身份标识 - -```javascript -createClientTokenAuthentication({ - login: { - // highlight-start - metaMatches: { - login: true - }, - // highlight-end - handler(response, method) { - // 登录拦截器 - } - } -}); -``` - -然后,元数据中具有`login: true`的请求,就会被认定为`login`身份。 - -```javascript -export const login = () => { - const method = alovaInstance.Get('/login'); - method.meta = { - login: true - }; - return method; -}; -``` - -### 登出身份标识 - -```javascript -createClientTokenAuthentication({ - logout: { - // highlight-start - metaMatches: { - logout: true - }, - // highlight-end - handler(response, method) { - // 登出拦截器 - } - } -}); -``` - -然后,元数据中具有`logout: true`的请求,就会被认定为`logout`身份。 - -```javascript -export const logout = () => { - const method = alovaInstance.Get('/logout'); - method.meta = { - logout: true - }; - return method; -}; -``` - -> `createServerTokenAuthentication`的登录拦截器用法相同。 - -## Typescript - -默认情况下,`createClientServerTokenAuthentication`和`createServerTokenAuthentication`适配了`GlobalFetch`请求适配器,你只需要指定`statesHook`的类型,如下: - -```typescript -// highlight-start -const { onAuthRequired, onResponseRefreshToken } = createClientTokenAuthentication({ - // highlight-end - //... -}); - -const alovaInstance = createAlova({ - // ... - statesHook: VueHook, - beforeRequest: onAuthRequired(method => { - // method的类型为 Method - }), - responded: onResponseRefreshToken((response, method) => { - // response的类型为Response - return response.json(); - }) -}); -``` - -如果你使用的不是`GlobalFetch`请求适配器,你还需要指定请求适配器的类型,这也很简单。 - -以下为 axios 请求适配器为例,在`createClientTokenAuthentication`中指定请求适配器类型。 - -```typescript -import { axiosRequestAdapter } from '@alova/adapter-axios'; - -// highlight-start -const { onAuthRequired, onResponseRefreshToken } = createClientTokenAuthentication< - typeof VueHook, - typeof axiosRequestAdapter ->({ - // highlight-end - //... -}); -const alovaInstance = createAlova({ - //... - statesHook: VueHook, - // highlight-start - beforeRequest: onAuthRequired(method => { - // method的类型为 Method - // highlight-end - }), - // highlight-start - responded: onResponseRefreshToken((response, method) => { - // response的类型为AxiosResponse - // highlight-end - return response.data; - }) -}); -``` - -基于服务端的 Token 认证拦截器的用法相同。 - -```typescript -import { axiosRequestAdapter } from '@alova/adapter-axios'; - -// highlight-start -createServerTokenAuthentication({ - // highlight-end - //... -}); -``` +--- +title: Token认证拦截器 +sidebar_position: 40 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info + +策略类型:拦截器 + +版本要求:v1.3.0+ + +::: + +> 在使用扩展 hooks 前,确保你已熟悉了 alova 的基本使用。 + +Token 认证拦截器,对基于 token 的登录、登出、token 附带、token 刷新进行统一管理,并支持无感刷新 token。 + +## 特性 + +- 统一维护 Token 身份认证的所有代码,包括登录、登出、token 附带、token 刷新等; +- 支持在客户端和服务端验证 token 过期,并无感刷新 token; +- 依赖 token 的请求自动等待 token 刷新完成再请求; +- 使用元数据设置请求身份; +- 自动放行不依赖 token 的访客请求; + +## 安装 + + + + +```bash +# npm +npm install @alova/scene-vue --save +# yarn +yarn add @alova/scene-vue + +``` + + + + +```bash +# npm +npm install @alova/scene-react --save +# yarn +yarn add @alova/scene-react + +``` + + + + + +```bash +# npm +npm install @alova/scene-svelte --save +# yarn +yarn add @alova/scene-svelte + +``` + + + + +:::info + +以下所有拦截器都是可选的,只需要选择需要使用的即可。 + +::: + +## 绑定 Token 认证拦截器 + +Token 身份认证是通过全局的拦截器完成的,分别提供了`createClientTokenAuthentication`和`createServerTokenAuthentication` 用于基于客户端和服务端的身份认证。 + +- 基于客户端的身份认证:表示从客户端判断 token 是否过期,例如在登录时获取到的 token 过期时间; +- 基于服务端的身份认证:表示从服务端返回的状态判断 token 是否过期,例如`status`为 401 时表示过期; + +### 绑定基于客户端的身份认证的拦截器 + +```javascript +import { createClientTokenAuthentication } from '@alova/scene-*'; +import { createAlova } from 'alova'; + +const { onAuthRequired, onResponseRefreshToken } = createClientTokenAuthentication({ + // ... +}); + +const alovaInstance = createAlova({ + // ... + beforeRequest: onAuthRequired(method => { + // ...原请求前拦截器 + }), + responded: onResponseRefreshToken((response, method) => { + //...原响应成功拦截器 + return response.json(); + }) +}); +``` + +在`onResponseRefreshToken`中也可以绑定响应错误和完成的拦截器,也和原来的用法相同。 + +```javascript +createAlova({ + // ... + // highlight-start + responded: onResponseRefreshToken({ + onSuccess: (response, method) => { + //...原响应成功拦截器 + }, + onError: (error, method) => { + //...原响应错误拦截器 + }, + onComplete: method => { + //...原响应完成拦截器 + } + }) + // highlight-end +}); +``` + +如果不需要设置拦截器,也可以不传入拦截器函数。 + +```javascript +createAlova({ + //... + // highlight-start + beforeRequest: onAuthRequired(), + responded: onResponseRefreshToken() + // highlight-end +}); +``` + +### 绑定基于服务端的身份认证拦截器 + +与基于客户端的用法相同 + +```javascript +import { createServerTokenAuthentication } from '@alova/scene-*'; +import { createAlova } from 'alova'; + +const { onAuthRequired, onResponseRefreshToken } = createServerTokenAuthentication({ + // ... +}); + +const alovaInstance = createAlova({ + // ... + beforeRequest: onAuthRequired(method => { + // ...原请求前拦截器 + }), + responded: onResponseRefreshToken((response, method) => { + //...原响应成功拦截器 + return response.json(); + }) +}); +``` + +:::warning + +当你使用`GlobalFetch`适配器时,你可能会遇到`TypeError: Failed to execute 'json' on 'Response': body stream already read`这个问题,这是因为`Response`的`body stream`只能访问一次,你可以`response.clone().json()`来解决它。 + +::: + +## 在客户端无感刷新 Token + +设置`refreshToken`并指定 token 是否过期,以及调用刷新 token 的函数就可以了。当 token 刷新完成前,所有依赖 token 的请求都将会等待 token 刷新完成。 + +```javascript +createClientTokenAuthentication({ + refreshToken: { + // 在请求前触发,将接收到method参数,并返回boolean表示token是否过期 + isExpired: method => { + return tokenExpireTime < Date.now(); + }, + + // 当token过期时触发,在此函数中触发刷新token + handler: async method => { + try { + const { token, refresh_token } = await refreshToken(); + localStorage.setItem('token', token); + localStorage.setItem('refresh_token', refresh_token); + } catch (error) { + // token刷新失败,跳转回登录页 + location.href = '/login'; + // 并抛出错误 + throw error; + } + } + } +}); +``` + +:::warning 注意 + +1. 为了让`refreshToken`请求顺利通过,需要通过元数据标识`authRole`为`refreshToken`。 +2. 如果 token 刷新失败必须抛出错误,阻止失败接口重试和等待接口继续请求。 + +::: + +> 了解更多元数据的信息,请前往[method 元数据](/tutorial/getting-started/method-metadata)。 + +```javascript +export const refreshToken = () => { + const method = alovaInstance.Get('/refresh_token'); + method.meta = { + authRole: 'refreshToken' + }; + return method; +}; +``` + +## 在服务端无感刷新 Token + +与在客户端无感刷新 Token 相同,指定 token 是否过期,以及调用刷新 token 的函数就可以了。当 token 刷新完成前,所有依赖 token 的请求都将会等待 token 刷新完成。 + +### 在请求成功拦截器中处理 + +当使用`GlobalFetch`时,只要服务端返回了响应数据,就会触发响应成功拦截器,此时我们需要在响应成功拦截器中处理 token 的刷新。 + +```javascript +createServerTokenAuthentication({ + refreshTokenOnSuccess: { + // 响应时触发,可获取到response和method,并返回boolean表示token是否过期 + // 当服务端返回401时,表示token过期 + isExpired: (response, method) => { + return response.status === 401; + }, + + // 当token过期时触发,在此函数中触发刷新token + handler: async (response, method) => { + try { + const { token, refresh_token } = await refreshToken(); + localStorage.setItem('token', token); + localStorage.setItem('refresh_token', refresh_token); + } catch (error) { + // token刷新失败,跳转回登录页 + location.href = '/login'; + // 并抛出错误 + throw error; + } + } + } +}); +``` + +### 在请求错误拦截器中处理 + +当使用`axios`拦截器时,服务端返回了非`200/300`的状态码就会触发响应错误拦截器,此时我们需要在响应错误拦截器中处理 token 的刷新。 + +```javascript +createServerTokenAuthentication({ + refreshTokenOnError: { + // 响应时触发,可获取到error和method,并返回boolean表示token是否过期 + // 当服务端返回401时,表示token过期 + isExpired: (error, method) => { + return error.response.status === 401; + }, + + // 当token过期时触发,在此函数中触发刷新token + handler: async (error, method) => { + try { + const { token, refresh_token } = await refreshToken(); + localStorage.setItem('token', token); + localStorage.setItem('refresh_token', refresh_token); + } catch (error) { + // token刷新失败,跳转回登录页 + location.href = '/login'; + // 并抛出错误 + throw error; + } + } + } +}); +``` + +:::warning 注意 + +1. 为了让`refreshToken`请求顺利通过,需要通过元数据标识`authRole`为`refreshToken`。 +2. 如果 token 刷新失败必须抛出错误,阻止失败接口重试和等待接口继续请求。 + +::: + +> 了解更多元数据的信息,请前往[method 元数据](/tutorial/getting-started/method-metadata)。 + +```javascript +export const refreshToken = () => { + const method = alovaInstance.Get('/refresh_token'); + method.meta = { + authRole: 'refreshToken' + }; + return method; +}; +``` + +## 放行访客请求 + +有些接口不需要依赖 token 认证,我们称它们为“访客请求”,此时我们可以设置它们的元数据为`authRole: null`来绕过前端的拦截,让它们顺利发出请求和接收响应。 + +```javascript +export const requestTokenNotRequired = () => { + const method = alovaInstance.Get('/token_not_required'); + method.meta = { + authRole: null + }; + return method; +}; +``` + +## 登录拦截 + +在身份认证拦截器中,你还可以拦截登录请求,在拦截器中保存登录信息,达到统一维护身份认证代码的目的。 + +首先标识登录请求的元数据为`authRole: 'login'`。 + +```javascript +export const login = () => { + const method = alovaInstance.Get('/login'); + method.meta = { + authRole: 'login' + }; + return method; +}; +``` + +再在登录拦截器中保存登录信息。 + +```javascript +createClientTokenAuthentication({ + login(response, method) { + localStorage.setItem('token', response.token); + localStorage.setItem('refresh_token', response.refresh_token); + } +}); +``` + +> `createServerTokenAuthentication`的登录拦截器用法相同。 + +## 附加 token + +通常,我们会在`beforeRequest`附加 token 到请求信息中。在 Token 认证拦截器中提供了`assignToken`回调函数用于附加 token,它会过滤访客请求和登录请求,并在请求前触发,也可以达到统一维护身份认证代码的目的。 + +```javascript +createClientTokenAuthentication({ + assignToken: method => { + method.config.headers.Authorization = localStorage.getItem('token')}; + } +}); +``` + +> `createServerTokenAuthentication`的 assignToken 回调函数用法相同。 + +## 登出拦截 + +当你的登出也需要调用接口时,也可以拦截登出请求,清除登录信息。 + +首先标识登出请求的元数据为`authRole: 'logout'`。 + +```javascript +export const logout = () => { + const method = alovaInstance.Get('/logout'); + method.meta = { + authRole: 'logout' + }; + return method; +}; +``` + +再在登出拦截器中清除登录信息。 + +```javascript +createClientTokenAuthentication({ + logout(response, method) { + localStorage.removeItem('token'); + localStorage.removeItem('refresh_token'); + } +}); +``` + +> `createServerTokenAuthentication`的登录拦截器用法相同。 + +## 自定义标识身份 + +在上面的元数据身份标识中,实际上都默认的身份标识,如果需要自定义身份标识,你可以按下面这样设置。 + +### token 刷新身份标识 + +```javascript +createClientTokenAuthentication({ + refreshToken: { + // highlight-start + metaMatches: { + refreshToken: true + } + // highlight-end + // ... + } +}); +``` + +```javascript +createServerTokenAuthentication({ + refreshTokenOnSuccess: { + // highlight-start + metaMatches: { + refreshToken: true + } + // highlight-end + // ... + }, + refreshTokenOnError: { + // highlight-start + metaMatches: { + refreshToken: true + } + // highlight-end + // ... + } +}); +``` + +然后,元数据中具有`refreshToken: true`的请求,就会被认定为`refreshToken`身份。 + +```javascript +export const refreshToken = () => { + const method = alovaInstance.Get('/refresh_token'); + method.meta = { + refreshToken: true + }; + return method; +}; +``` + +### 访客身份标识 + +```javascript +createClientTokenAuthentication({ + visitorMeta: { + isVisitor: true + } +}); +``` + +然后,元数据中具有`isVisitor: true`的请求,就会被认定为访客身份。 + +```javascript +export const requestTokenNotRequired = () => { + const method = alovaInstance.Get('/token_not_required'); + method.meta = { + isVisitor: true + }; + return method; +}; +``` + +### 登录身份标识 + +```javascript +createClientTokenAuthentication({ + login: { + // highlight-start + metaMatches: { + login: true + }, + // highlight-end + handler(response, method) { + // 登录拦截器 + } + } +}); +``` + +然后,元数据中具有`login: true`的请求,就会被认定为`login`身份。 + +```javascript +export const login = () => { + const method = alovaInstance.Get('/login'); + method.meta = { + login: true + }; + return method; +}; +``` + +### 登出身份标识 + +```javascript +createClientTokenAuthentication({ + logout: { + // highlight-start + metaMatches: { + logout: true + }, + // highlight-end + handler(response, method) { + // 登出拦截器 + } + } +}); +``` + +然后,元数据中具有`logout: true`的请求,就会被认定为`logout`身份。 + +```javascript +export const logout = () => { + const method = alovaInstance.Get('/logout'); + method.meta = { + logout: true + }; + return method; +}; +``` + +> `createServerTokenAuthentication`的登录拦截器用法相同。 + +## Typescript + +默认情况下,`createClientServerTokenAuthentication`和`createServerTokenAuthentication`适配了`GlobalFetch`请求适配器,你只需要指定`statesHook`的类型,如下: + +```typescript +// highlight-start +const { onAuthRequired, onResponseRefreshToken } = createClientTokenAuthentication< + typeof VueHook +>({ + // highlight-end + //... +}); + +const alovaInstance = createAlova({ + // ... + statesHook: VueHook, + beforeRequest: onAuthRequired(method => { + // method的类型为 Method + }), + responded: onResponseRefreshToken((response, method) => { + // response的类型为Response + return response.json(); + }) +}); +``` + +如果你使用的不是`GlobalFetch`请求适配器,你还需要指定请求适配器的类型,这也很简单。 + +以下为 axios 请求适配器为例,在`createClientTokenAuthentication`中指定请求适配器类型。 + +```typescript +import { axiosRequestAdapter } from '@alova/adapter-axios'; + +// highlight-start +const { onAuthRequired, onResponseRefreshToken } = createClientTokenAuthentication< + typeof VueHook, + typeof axiosRequestAdapter +>({ + // highlight-end + //... +}); +const alovaInstance = createAlova({ + //... + statesHook: VueHook, + // highlight-start + beforeRequest: onAuthRequired(method => { + // method的类型为 Method + // highlight-end + }), + // highlight-start + responded: onResponseRefreshToken((response, method) => { + // response的类型为AxiosResponse + // highlight-end + return response.data; + }) +}); +``` + +基于服务端的 Token 认证拦截器的用法相同。 + +```typescript +import { axiosRequestAdapter } from '@alova/adapter-axios'; + +// highlight-start +createServerTokenAuthentication({ + // highlight-end + //... +}); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/06-useAutoRequest.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/06-useAutoRequest.md index f1d4eb022..88d17cd8b 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/06-useAutoRequest.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/06-useAutoRequest.md @@ -1,168 +1,171 @@ ---- -title: 自动拉取数据 -sidebar_position: 60 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -:::info 策略类型 - -use hook - -::: - -> 在使用扩展 hooks 前,确保你已熟悉了 alova 的基本使用。 - -通过浏览器事件或轮询自动拉取数据,让界面展示最新数据。 - -## 特性 - -- ✨ 支持浏览器聚焦、tab 切换、网络重连、轮询请求等场景下拉取最新数据,可自定义配置监听类型; -- ✨ 支持请求节流,在短时间内多次触发只会发送 1 次请求; -- ✨ 支持自定义事件的监听函数,以适应非浏览器环境下的使用场景; - -## 安装 - - - - -```bash -# npm -npm install @alova/scene-vue --save -# yarn -yarn add @alova/scene-vue - -``` - - - - -```bash -# npm -npm install @alova/scene-react --save -# yarn -yarn add @alova/scene-react - -``` - - - - - -```bash -# npm -npm install @alova/scene-svelte --save -# yarn -yarn add @alova/scene-svelte - -``` - - - - -## 基本用法 - -在默认情况下,自动拉取数据的 useHook`useAutoRequest`会在浏览器显示隐藏、聚焦、网络重连时自动拉取最新数据,并在组件卸载时自动取消监听事件。 - -```javascript -import { useAutoRequest } from '@alova/scene-*'; - -const { loading, data, error } = useAutoRequest(() => method()); -``` - -`useAutoRequest`的返回值与[useRequest](/api/core-hooks#userequest)相同。 - -除了支持[useRequest](/api/core-hooks#userequest)的所有配置参数外,还支持自动拉取的配置参数,你可以通过以下配置开启或关闭一些事件,或修改请求节流事件。 - -```javascript -const { loading, data, error, onSuccess, onError, onComplete } = useAutoRequest(() => method(), { - /** - * 浏览器显示隐藏触发 - * @default true - */ - enableVisibility: true, - - /** - * 浏览器聚焦触发 - * @default true - */ - enableFocus: true, - - /** - * 网络重连触发 - * @default true - */ - enableNetwork: true, - - /** - * 节流时间,在一定时间内多次触发只会发送1次请求,单位ms - * @default 1000 - */ - throttle: 1000, - - /** - * 轮询请求的时间,设置大于0时有效,单位ms - * @default 0 - */ - pollingTime: 2000 - - // 其他参数同useRequest... -}); -``` - -:::warning 缓存建议 - -建议在使用`useAutoRequest`时关闭对应请求的缓存,因为当设置缓存时,在触发自动请求时也会命中缓存而获取不到最新数据。具体请阅读[缓存模式](/tutorial/cache/mode)。 - -::: - -## 自定义监听函数 - -以上 4 种自动拉取数据的方式,默认是监听浏览器事件来实现的,当用户在非浏览器环境下使用时,需要自定义监听函数,此函数接收通知请求和 useHook 配置对象作为参数,并返回一个取消监听函数。 -。 - -以下是在`react-native`中自定义监听函数的示例: - -### 网络重连自定义函数 - -```javascript -import NetInfo from '@react-native-community/netinfo'; -useAutoRequest.onNetwork = (notify, config) => { - const unsubscribe = NetInfo.addEventListener(({ isConnected }) => { - isConnected && notify(); - }); - return unsubscribe; -}; -``` - -### 轮询自定义函数 - -```javascript -useAutoRequest.onPolling = (notify, config) => { - const timer = setInterval(notify, config.pollingTime); - return () => clearInterval(timer); -}; -``` - -### 应用切换自定义函数 - -```javascript -import { AppState, Text } from 'react-native'; -useAutoRequest.onVisibility = (notify, config) => { - const subscription = AppState.addEventListener('change', state => { - state === 'active' && notify(); - }); - return () => subscription.remove(); -}; -``` - -### 应用聚焦自定义函数 - -由于 App 没有聚焦事件,所以可以设置为空函数避免报错。 - -```javascript -useAutoRequest.onFocus = (notify, config) => { - return () => {}; -}; -``` +--- +title: 自动拉取数据 +sidebar_position: 60 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info 策略类型 + +use hook + +::: + +> 在使用扩展 hooks 前,确保你已熟悉了 alova 的基本使用。 + +通过浏览器事件或轮询自动拉取数据,让界面展示最新数据。 + +## 特性 + +- 支持浏览器聚焦、tab 切换、网络重连、轮询请求等场景下拉取最新数据,可自定义配置监听类型; +- 支持请求节流,在短时间内多次触发只会发送 1 次请求; +- 支持自定义事件的监听函数,以适应非浏览器环境下的使用场景; + +## 安装 + + + + +```bash +# npm +npm install @alova/scene-vue --save +# yarn +yarn add @alova/scene-vue + +``` + + + + +```bash +# npm +npm install @alova/scene-react --save +# yarn +yarn add @alova/scene-react + +``` + + + + + +```bash +# npm +npm install @alova/scene-svelte --save +# yarn +yarn add @alova/scene-svelte + +``` + + + + +## 基本用法 + +在默认情况下,自动拉取数据的 useHook`useAutoRequest`会在浏览器显示隐藏、聚焦、网络重连时自动拉取最新数据,并在组件卸载时自动取消监听事件。 + +```javascript +import { useAutoRequest } from '@alova/scene-*'; + +const { loading, data, error } = useAutoRequest(() => method()); +``` + +`useAutoRequest`的返回值与[useRequest](/api/core-hooks#userequest)相同。 + +除了支持[useRequest](/api/core-hooks#userequest)的所有配置参数外,还支持自动拉取的配置参数,你可以通过以下配置开启或关闭一些事件,或修改请求节流事件。 + +```javascript +const { loading, data, error, onSuccess, onError, onComplete } = useAutoRequest( + () => method(), + { + /** + * 浏览器显示隐藏触发 + * @default true + */ + enableVisibility: true, + + /** + * 浏览器聚焦触发 + * @default true + */ + enableFocus: true, + + /** + * 网络重连触发 + * @default true + */ + enableNetwork: true, + + /** + * 节流时间,在一定时间内多次触发只会发送1次请求,单位ms + * @default 1000 + */ + throttle: 1000, + + /** + * 轮询请求的时间,设置大于0时有效,单位ms + * @default 0 + */ + pollingTime: 2000 + + // 其他参数同useRequest... + } +); +``` + +:::warning 缓存建议 + +建议在使用`useAutoRequest`时关闭对应请求的缓存,因为当设置缓存时,在触发自动请求时也会命中缓存而获取不到最新数据。具体请阅读[缓存模式](/tutorial/cache/mode)。 + +::: + +## 自定义监听函数 + +以上 4 种自动拉取数据的方式,默认是监听浏览器事件来实现的,当用户在非浏览器环境下使用时,需要自定义监听函数,此函数接收通知请求和 useHook 配置对象作为参数,并返回一个取消监听函数。 +。 + +以下是在`react-native`中自定义监听函数的示例: + +### 网络重连自定义函数 + +```javascript +import NetInfo from '@react-native-community/netinfo'; +useAutoRequest.onNetwork = (notify, config) => { + const unsubscribe = NetInfo.addEventListener(({ isConnected }) => { + isConnected && notify(); + }); + return unsubscribe; +}; +``` + +### 轮询自定义函数 + +```javascript +useAutoRequest.onPolling = (notify, config) => { + const timer = setInterval(notify, config.pollingTime); + return () => clearInterval(timer); +}; +``` + +### 应用切换自定义函数 + +```javascript +import { AppState, Text } from 'react-native'; +useAutoRequest.onVisibility = (notify, config) => { + const subscription = AppState.addEventListener('change', state => { + state === 'active' && notify(); + }); + return () => subscription.remove(); +}; +``` + +### 应用聚焦自定义函数 + +由于 App 没有聚焦事件,所以可以设置为空函数避免报错。 + +```javascript +useAutoRequest.onFocus = (notify, config) => { + return () => {}; +}; +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/08-useCaptcha.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/08-useCaptcha.md index ba30dbeee..0d07a8203 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/08-useCaptcha.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/08-useCaptcha.md @@ -1,213 +1,213 @@ ---- -title: 发送验证码 -sidebar_position: 80 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -:::info 策略类型 - -use hook - -::: - -> 在使用扩展 hooks 前,确保你已熟悉了 alova 的基本使用。 - -验证码发送 hook,减掉你在开发验证码发送功能时的繁琐。 - -## 示例 - -[发送验证码 Demo](/tutorial/example/captcha-send) - -## 特性 - -- ✨ 验证码发送后自动开始倒计时; -- ✨ 自定义倒计时秒数; -- ✨ 验证码发送限制; - -## 安装 - - - - -```bash -# npm -npm install @alova/scene-vue --save -# yarn -yarn add @alova/scene-vue - -``` - - - - -```bash -# npm -npm install @alova/scene-react --save -# yarn -yarn add @alova/scene-react - -``` - - - - - -```bash -# npm -npm install @alova/scene-svelte --save -# yarn -yarn add @alova/scene-svelte - -``` - - - - -## 使用 - -## 基本使用 - -展示表单 hook 的基本使用。 - - - - -```html - - - -``` - - - - -```jsx -import { useState } from 'react'; -import { apiSendCaptcha } from './api.js'; -import { useCaptcha } from '@alova/scene-react'; - -const App = () => { - const [mobile, setMobile] = ref(''); - const { - // 发送状态 - loading: sending, - - // 调用sendCaptcha才会请求接口发送验证码 - send: sendCaptcha - } = useCaptcha(() => apiSendCaptcha(mobile)); - - return ( -
- setMobile(target.value)} - /> - -
- ); -}; -``` - -
- - -```html - - - - -``` - - -
- -默认情况下,验证码发送成功后将会倒计时 60 秒,当倒计时没有结束时再调用`send`将会抛出错误。 - -### 自定义倒计时秒数 - -你也可以自定义倒计时秒数 - -```javascript -useCaptcha(() => apiSendCaptcha(mobile.value), { - // ... - // highlight-start - // 将倒计时设为20秒 - initialCountdown: 20 - // highlight-end -}); -``` - -## API - -### Hook 配置 - -继承[**useRequest**](/api/core-hooks#userequest)除`immediate`外的所有配置,`useCaptcha`中`immediate`已硬编码为 false。 - -| 名称 | 描述 | 类型 | 默认值 | 版本 | -| ---------------- | ------------------------------------------------------ | ------ | ------ | ---- | -| initialCountdown | 初始倒计时,当验证码发送成功时将会以此数据来开始倒计时 | number | 60 | - | - -### 响应式数据 - -继承[**useRequest**](/api/core-hooks#userequest)所有响应式数据。 - -| 名称 | 描述 | 类型 | 版本 | -| --------- | ------------------------------------------------------ | ------ | ---- | -| countdown | 当前倒计时,每秒-1,当倒计时结束后才可以再次发送验证码 | number | - | - -### 操作函数 - -继承[**useRequest**](/api/core-hooks#userequest)所有操作函数。 - -| 名称 | 描述 | 函数参数 | 返回值 | 版本 | -| ---- | ---------------------------------------- | ----------------------- | ------------------- | ---- | -| send | 发送请求,当倒计时未结束时调用将抛出错误 | 与 useRequest.send 一致 | Promise\ | - | - -### 事件 - -继承[**useRequest**](/api/core-hooks#userequest)所有事件。 +--- +title: 发送验证码 +sidebar_position: 80 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info 策略类型 + +use hook + +::: + +> 在使用扩展 hooks 前,确保你已熟悉了 alova 的基本使用。 + +验证码发送 hook,减掉你在开发验证码发送功能时的繁琐。 + +## 示例 + +[发送验证码 Demo](/tutorial/example/captcha-send) + +## 特性 + +- 验证码发送后自动开始倒计时; +- 自定义倒计时秒数; +- 验证码发送限制; + +## 安装 + + + + +```bash +# npm +npm install @alova/scene-vue --save +# yarn +yarn add @alova/scene-vue + +``` + + + + +```bash +# npm +npm install @alova/scene-react --save +# yarn +yarn add @alova/scene-react + +``` + + + + + +```bash +# npm +npm install @alova/scene-svelte --save +# yarn +yarn add @alova/scene-svelte + +``` + + + + +## 使用 + +## 基本使用 + +展示表单 hook 的基本使用。 + + + + +```html + + + +``` + + + + +```jsx +import { useState } from 'react'; +import { apiSendCaptcha } from './api.js'; +import { useCaptcha } from '@alova/scene-react'; + +const App = () => { + const [mobile, setMobile] = ref(''); + const { + // 发送状态 + loading: sending, + + // 调用sendCaptcha才会请求接口发送验证码 + send: sendCaptcha + } = useCaptcha(() => apiSendCaptcha(mobile)); + + return ( +
+ setMobile(target.value)} + /> + +
+ ); +}; +``` + +
+ + +```html + + + + +``` + + +
+ +默认情况下,验证码发送成功后将会倒计时 60 秒,当倒计时没有结束时再调用`send`将会抛出错误。 + +### 自定义倒计时秒数 + +你也可以自定义倒计时秒数 + +```javascript +useCaptcha(() => apiSendCaptcha(mobile.value), { + // ... + // highlight-start + // 将倒计时设为20秒 + initialCountdown: 20 + // highlight-end +}); +``` + +## API + +### Hook 配置 + +继承[**useRequest**](/api/core-hooks#userequest)除`immediate`外的所有配置,`useCaptcha`中`immediate`已硬编码为 false。 + +| 名称 | 描述 | 类型 | 默认值 | 版本 | +| ---------------- | ------------------------------------------------------ | ------ | ------ | ---- | +| initialCountdown | 初始倒计时,当验证码发送成功时将会以此数据来开始倒计时 | number | 60 | - | + +### 响应式数据 + +继承[**useRequest**](/api/core-hooks#userequest)所有响应式数据。 + +| 名称 | 描述 | 类型 | 版本 | +| --------- | ------------------------------------------------------ | ------ | ---- | +| countdown | 当前倒计时,每秒-1,当倒计时结束后才可以再次发送验证码 | number | - | + +### 操作函数 + +继承[**useRequest**](/api/core-hooks#userequest)所有操作函数。 + +| 名称 | 描述 | 函数参数 | 返回值 | 版本 | +| ---- | ---------------------------------------- | ----------------------- | ------------------- | ---- | +| send | 发送请求,当倒计时未结束时调用将抛出错误 | 与 useRequest.send 一致 | Promise\ | - | + +### 事件 + +继承[**useRequest**](/api/core-hooks#userequest)所有事件。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/09-actionDelegationMiddleware.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/09-actionDelegationMiddleware.md index ef8a5d3ec..8508856a2 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/09-actionDelegationMiddleware.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/09-actionDelegationMiddleware.md @@ -1,244 +1,244 @@ ---- -title: 跨组件触发请求 -sidebar_position: 90 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -:::info 策略类型 - -中间件 - -::: - -> 在使用扩展 hooks 前,确保你已熟悉了 alova 的基本使用。 - -从前,在一个组件中想要触发另一个组件中的请求,你需要将数据保存到 Store 中,通过分发 Action 完成。现在,你可以使用这个中间件**消除组件层级的限制**,在任意组件中快速地触发任意请求的操作函数。 - -例如,你可以某个组件中更新了菜单数据后,重新触发侧边菜单栏的重新请求,从而刷新数据。当操作了列表数据后,触发列表更新。 - -## 示例 - -[跨组件触发请求 Demo](/tutorial/example/action-delegation-middleware) - -## 特性 - -- ✨ 委托任意 alova 中的 use hook 的操作函数; -- ✨ 任意位置触发已委托的操作函数; - -## 安装 - - - - -```bash -# npm -npm install @alova/scene-vue --save -# yarn -yarn add @alova/scene-vue - -``` - - - - -```bash -# npm -npm install @alova/scene-react --save -# yarn -yarn add @alova/scene-react - -``` - - - - - -```bash -# npm -npm install @alova/scene-svelte --save -# yarn -yarn add @alova/scene-svelte - -``` - - - - -## 使用 - -### 基本使用 - -> 以 vue3 为例,在 react、svelte 中用法相同。 - -在组件 A 中使用`actionDelegationMiddleware`委托`useRequest`的操作函数。 - -```javascript title=组件A -import { actionDelegationMiddleware } from '@alova/scene-vue'; - -useRequest(queryTodo, { - // ... - middleware: actionDelegationMiddleware('actionName') -}); -``` - -在任意一个组件中(如组件 B)通过`accessAction`传入指定的委托名称触发组件 A 中的`useRequest`的操作函数。 - -```javascript title=组件B -import { accessAction } from '@alova/scene-vue'; - -accessAction('actionName', delegatedActions => { - // 调用组件A中的send函数 - delegatedActions.send(); - - // 调用组件A中的abort函数 - delegatedActions.abort(); -}); -``` - -:::info 注意 - -1. alova 内的全部 use hook 都支持操作函数委托,但不同的 use hook 所委托的函数有所不同。 -2. 使用`actionDelegationMiddleware`时,委托名称可传入字符串、数字、symbol 值。 - -::: - -### 批量触发操作函数 - -在上面的例子中,我们使用`accessAction`触发了一个 use hook 的操作函数,但实际上,相同名称的委托不会互相覆盖,而是会保存在一组中,我们可以使用这个名称同时触发它们委托的函数。 - -```javascript title=组件C -import { actionDelegationMiddleware } from '@alova/scene-vue'; - -useRequest(queryTodo, { - // ... - middleware: actionDelegationMiddleware('actionName1') -}); -``` - -```javascript title=组件D -import { actionDelegationMiddleware } from '@alova/scene-vue'; - -useRequest(queryTodo, { - // ... - middleware: actionDelegationMiddleware('actionName1') -}); -``` - -```javascript title=组件E -import { accessAction } from '@alova/scene-vue'; - -// 因为将会匹配组件C、组件D的委托hook,因此回调函数将被执行两次 -accessAction('actionName1', delegatedActions => { - // 调用组件C、组件D中的send函数 - delegatedActions.send(); - - // 调用组件C、组件D中的abort函数 - delegatedActions.abort(); -}); -``` - -同时,还可以在`accessAction`中使用正则表达式来批量触发委托名称满足条件的操作函数 - -```javascript title=组件F -import { actionDelegationMiddleware } from '@alova/scene-vue'; - -useRequest(queryTodo, { - // ... - middleware: actionDelegationMiddleware('prefix_name1') -}); -``` - -```javascript title=组件G -import { actionDelegationMiddleware } from '@alova/scene-vue'; - -useRequest(queryTodo, { - // ... - middleware: actionDelegationMiddleware('prefix_name2') -}); -``` - -```javascript title=组件H -import { accessAction } from '@alova/scene-vue'; - -// 因为将会匹配组件F、组件G的委托hook,因此回调函数将被执行两次 -accessAction(/^prefix_/, delegatedActions => { - // 调用组件F、组件G中的send函数 - delegatedActions.send(); - - // 调用组件F、组件G中的abort函数 - delegatedActions.abort(); -}); -``` - -## 操作函数委托表 - -尽管大部分 hook 委托的操作函数与它本身带有的操作函数相同,但这不是绝对的,以下是每个 hook 的操作函数委托表。 - -### useRequest - -| 名称 | 描述 | 函数参数 | 返回值 | 版本 | -| ------ | ------------------------------------------------------- | -------- | ------ | ---- | -| send | 与 [useRequset](/api/core-hooks#userequest).send 相同 | | | - | -| abort | 与 [useRequset](/api/core-hooks#userequest).abort 相同 | | | - | -| update | 与 [useRequset](/api/core-hooks#userequest).update 相同 | | | - | - -### useWatcher - -与[useRequest 委托列表](#userequest)相同。 - -### useFetcher - -| 名称 | 描述 | 函数参数 | 返回值 | 版本 | -| ------ | ------------------------------------------------------- | -------- | ------ | ---- | -| fetch | 与 [useFetcher](/api/core-hooks#usefetcher).fetch 相同 | | | - | -| abort | 与 [useFetcher](/api/core-hooks#usefetcher).abort 相同 | | | - | -| update | 与 [useFetcher](/api/core-hooks#usefetcher).update 相同 | | | - | - -### usePagination - -| 名称 | 描述 | 函数参数 | 返回值 | 版本 | -| -------- | ------------------------------------------------------------------ | ---------------------------------------------------------------------------------- | ------------------ | ---- | -| refresh | 详见[usePagination 操作函数](/tutorial/strategy/usePagination#api) | | | - | -| insert | 详见[usePagination 操作函数](/tutorial/strategy/usePagination#api) | | | - | -| remove | 详见[usePagination 操作函数](/tutorial/strategy/usePagination#api) | | | - | -| replace | 详见[usePagination 操作函数](/tutorial/strategy/usePagination#api) | | | - | -| reload | 详见[usePagination 操作函数](/tutorial/strategy/usePagination#api) | | | - | -| update | 详见[usePagination 操作函数](/tutorial/strategy/usePagination#api) | | | - | -| getState | 按名称获取分页相关数据 | stateKey: 'page' \| 'pageSize' \| 'data' \| 'pageCount' \| 'total' \| 'isLastPage' | 对应 statekey 的值 | - | - -### useSQRequest - -与[useRequest 委托列表](#userequest)相同。 - -### useForm - -| 名称 | 描述 | 函数参数 | 返回值 | 版本 | -| ---------- | ------------------------------------------------------- | -------- | ------ | ---- | -| updateForm | 详见[useForm 操作函数](/tutorial/strategy/useForm#api) | | | - | -| reset | 详见[useForm 操作函数](/tutorial/strategy/useForm#api) | | | - | -| send | 与 [useRequset](/api/core-hooks#userequest).send 相同 | | | - | -| abort | 与 [useRequset](/api/core-hooks#userequest).abort 相同 | | | - | -| update | 与 [useRequset](/api/core-hooks#userequest).update 相同 | | | - | - -### useCaptcha - -与[useRequest 委托列表](#userequest)相同。 - -### useRetriableRequest - -| 名称 | 描述 | 函数参数 | 返回值 | 版本 | -| ------ | ------------------------------------------------------------------------------ | -------- | ------ | ---- | -| stop | 详见[useRetriableRequest 操作函数](/tutorial/strategy/useRetriableRequest#api) | | | - | -| send | 与 [useRequset](/api/core-hooks#userequest).send 相同 | | | - | -| abort | 与 [useRequset](/api/core-hooks#userequest).abort 相同 | | | - | -| update | 与 [useRequset](/api/core-hooks#userequest).update 相同 | | | - | - -### useSerialRequest - -与[useRequest 委托列表](#userequest)相同。 - -### useSerialWatcher - -与[useRequest 委托列表](#userequest)相同。 +--- +title: 跨组件触发请求 +sidebar_position: 90 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info 策略类型 + +中间件 + +::: + +> 在使用扩展 hooks 前,确保你已熟悉了 alova 的基本使用。 + +从前,在一个组件中想要触发另一个组件中的请求,你需要将数据保存到 Store 中,通过分发 Action 完成。现在,你可以使用这个中间件**消除组件层级的限制**,在任意组件中快速地触发任意请求的操作函数。 + +例如,你可以某个组件中更新了菜单数据后,重新触发侧边菜单栏的重新请求,从而刷新数据。当操作了列表数据后,触发列表更新。 + +## 示例 + +[跨组件触发请求 Demo](/tutorial/example/action-delegation-middleware) + +## 特性 + +- 委托任意 alova 中的 use hook 的操作函数; +- 任意位置触发已委托的操作函数; + +## 安装 + + + + +```bash +# npm +npm install @alova/scene-vue --save +# yarn +yarn add @alova/scene-vue + +``` + + + + +```bash +# npm +npm install @alova/scene-react --save +# yarn +yarn add @alova/scene-react + +``` + + + + + +```bash +# npm +npm install @alova/scene-svelte --save +# yarn +yarn add @alova/scene-svelte + +``` + + + + +## 使用 + +### 基本使用 + +> 以 vue3 为例,在 react、svelte 中用法相同。 + +在组件 A 中使用`actionDelegationMiddleware`委托`useRequest`的操作函数。 + +```javascript title=组件A +import { actionDelegationMiddleware } from '@alova/scene-vue'; + +useRequest(queryTodo, { + // ... + middleware: actionDelegationMiddleware('actionName') +}); +``` + +在任意一个组件中(如组件 B)通过`accessAction`传入指定的委托名称触发组件 A 中的`useRequest`的操作函数。 + +```javascript title=组件B +import { accessAction } from '@alova/scene-vue'; + +accessAction('actionName', delegatedActions => { + // 调用组件A中的send函数 + delegatedActions.send(); + + // 调用组件A中的abort函数 + delegatedActions.abort(); +}); +``` + +:::info 注意 + +1. alova 内的全部 use hook 都支持操作函数委托,但不同的 use hook 所委托的函数有所不同。 +2. 使用`actionDelegationMiddleware`时,委托名称可传入字符串、数字、symbol 值。 + +::: + +### 批量触发操作函数 + +在上面的例子中,我们使用`accessAction`触发了一个 use hook 的操作函数,但实际上,相同名称的委托不会互相覆盖,而是会保存在一组中,我们可以使用这个名称同时触发它们委托的函数。 + +```javascript title=组件C +import { actionDelegationMiddleware } from '@alova/scene-vue'; + +useRequest(queryTodo, { + // ... + middleware: actionDelegationMiddleware('actionName1') +}); +``` + +```javascript title=组件D +import { actionDelegationMiddleware } from '@alova/scene-vue'; + +useRequest(queryTodo, { + // ... + middleware: actionDelegationMiddleware('actionName1') +}); +``` + +```javascript title=组件E +import { accessAction } from '@alova/scene-vue'; + +// 因为将会匹配组件C、组件D的委托hook,因此回调函数将被执行两次 +accessAction('actionName1', delegatedActions => { + // 调用组件C、组件D中的send函数 + delegatedActions.send(); + + // 调用组件C、组件D中的abort函数 + delegatedActions.abort(); +}); +``` + +同时,还可以在`accessAction`中使用正则表达式来批量触发委托名称满足条件的操作函数 + +```javascript title=组件F +import { actionDelegationMiddleware } from '@alova/scene-vue'; + +useRequest(queryTodo, { + // ... + middleware: actionDelegationMiddleware('prefix_name1') +}); +``` + +```javascript title=组件G +import { actionDelegationMiddleware } from '@alova/scene-vue'; + +useRequest(queryTodo, { + // ... + middleware: actionDelegationMiddleware('prefix_name2') +}); +``` + +```javascript title=组件H +import { accessAction } from '@alova/scene-vue'; + +// 因为将会匹配组件F、组件G的委托hook,因此回调函数将被执行两次 +accessAction(/^prefix_/, delegatedActions => { + // 调用组件F、组件G中的send函数 + delegatedActions.send(); + + // 调用组件F、组件G中的abort函数 + delegatedActions.abort(); +}); +``` + +## 操作函数委托表 + +尽管大部分 hook 委托的操作函数与它本身带有的操作函数相同,但这不是绝对的,以下是每个 hook 的操作函数委托表。 + +### useRequest + +| 名称 | 描述 | 函数参数 | 返回值 | 版本 | +| ------ | ------------------------------------------------------- | -------- | ------ | ---- | +| send | 与 [useRequset](/api/core-hooks#userequest).send 相同 | | | - | +| abort | 与 [useRequset](/api/core-hooks#userequest).abort 相同 | | | - | +| update | 与 [useRequset](/api/core-hooks#userequest).update 相同 | | | - | + +### useWatcher + +与[useRequest 委托列表](#userequest)相同。 + +### useFetcher + +| 名称 | 描述 | 函数参数 | 返回值 | 版本 | +| ------ | ------------------------------------------------------- | -------- | ------ | ---- | +| fetch | 与 [useFetcher](/api/core-hooks#usefetcher).fetch 相同 | | | - | +| abort | 与 [useFetcher](/api/core-hooks#usefetcher).abort 相同 | | | - | +| update | 与 [useFetcher](/api/core-hooks#usefetcher).update 相同 | | | - | + +### usePagination + +| 名称 | 描述 | 函数参数 | 返回值 | 版本 | +| -------- | ------------------------------------------------------------------ | ---------------------------------------------------------------------------------- | ------------------ | ---- | +| refresh | 详见[usePagination 操作函数](/tutorial/strategy/usePagination#api) | | | - | +| insert | 详见[usePagination 操作函数](/tutorial/strategy/usePagination#api) | | | - | +| remove | 详见[usePagination 操作函数](/tutorial/strategy/usePagination#api) | | | - | +| replace | 详见[usePagination 操作函数](/tutorial/strategy/usePagination#api) | | | - | +| reload | 详见[usePagination 操作函数](/tutorial/strategy/usePagination#api) | | | - | +| update | 详见[usePagination 操作函数](/tutorial/strategy/usePagination#api) | | | - | +| getState | 按名称获取分页相关数据 | stateKey: 'page' \| 'pageSize' \| 'data' \| 'pageCount' \| 'total' \| 'isLastPage' | 对应 statekey 的值 | - | + +### useSQRequest + +与[useRequest 委托列表](#userequest)相同。 + +### useForm + +| 名称 | 描述 | 函数参数 | 返回值 | 版本 | +| ---------- | ------------------------------------------------------- | -------- | ------ | ---- | +| updateForm | 详见[useForm 操作函数](/tutorial/strategy/useForm#api) | | | - | +| reset | 详见[useForm 操作函数](/tutorial/strategy/useForm#api) | | | - | +| send | 与 [useRequset](/api/core-hooks#userequest).send 相同 | | | - | +| abort | 与 [useRequset](/api/core-hooks#userequest).abort 相同 | | | - | +| update | 与 [useRequset](/api/core-hooks#userequest).update 相同 | | | - | + +### useCaptcha + +与[useRequest 委托列表](#userequest)相同。 + +### useRetriableRequest + +| 名称 | 描述 | 函数参数 | 返回值 | 版本 | +| ------ | ------------------------------------------------------------------------------ | -------- | ------ | ---- | +| stop | 详见[useRetriableRequest 操作函数](/tutorial/strategy/useRetriableRequest#api) | | | - | +| send | 与 [useRequset](/api/core-hooks#userequest).send 相同 | | | - | +| abort | 与 [useRequset](/api/core-hooks#userequest).abort 相同 | | | - | +| update | 与 [useRequset](/api/core-hooks#userequest).update 相同 | | | - | + +### useSerialRequest + +与[useRequest 委托列表](#userequest)相同。 + +### useSerialWatcher + +与[useRequest 委托列表](#userequest)相同。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/10-useSerialRequest.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/10-useSerialRequest.md index 86847d2d3..05ba766a2 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/10-useSerialRequest.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/10-useSerialRequest.md @@ -1,144 +1,144 @@ ---- -title: 串行请求的useRequest -sidebar_position: 100 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -:::info 策略类型 - -use hook - -::: - -> 在使用扩展 hooks 前,确保你已熟悉了 alova 的基本使用。 - -这个 use hook 比[在最佳实践中的串行请求](/tutorial/best-practice/skills)更加简洁易用,统一的 loading 状态、error、回调函数。 - -## 特性 - -- ✨ 更加简洁易用的串行方式; -- ✨ 统一的请求状态和回调函数; -- ✨ send 函数可触发多个请求串行执行; - -## 示例 - -[串行请求](/tutorial/example/serial-request) - -## 安装 - - - - -```bash -# npm -npm install @alova/scene-vue --save -# yarn -yarn add @alova/scene-vue - -``` - - - - -```bash -# npm -npm install @alova/scene-react --save -# yarn -yarn add @alova/scene-react - -``` - - - - - -```bash -# npm -npm install @alova/scene-svelte --save -# yarn -yarn add @alova/scene-svelte - -``` - - - - -## 使用 - -### 基本用法 - -和`useRequest`的用法一样,只是第一个参数改变成了一个串行执行的 handler 数组,每个 handler 将接收上一个请求的响应数据。 - -```javascript -const { - // 串行加载状态,全部请求完成才会改为false - loading, - - // 最后一个请求的响应数据 - data, - - // 任意一个请求错误都将在这记录错误信息 - error, - - // 手动发送串行请求 - send, - - // 串行请求成功回调绑定函数 - onSuccess, - - // 串行请求错误回调绑定函数,任意一个请求错误都将触发它 - onError, - - // 串行请求完成回调绑定函数 - onComplete -} = useSerialRequest( - [ - // args为send函数传入的参数 - (...args) => request1(args), - - // 从第二个handler开始,第一个参数为上一个请求的响应数据,args从第二个开始接收 - (response1, ...args) => request2(response1, args), - (response2, ...args) => request3(response2, args) - ], - { - immediate: false - } -); - -// 手动触发请求并传参 -send(1, 2, 3); -``` - -值得注意的是,handler 数组中的第一项也可以指定为一个 method 实例,从第二项开始必须为函数。 - -```javascript -useSerialRequest([ - methodInstance, - (response1, ...args) => request2(response1, args), - (response2, ...args) => request3(response2, args) -]); -``` - -### 请求错误 - -串行请求任意一个请求错误时,将会触发`onError`,它的`event.method`将指向请求错误的 method 实例。 - -## API - -### Hook 配置 - -继承[**useRequest**](/api/core-hooks#userequest)所有配置。 - -### 响应式数据 - -继承[**useRequest**](/api/core-hooks#userequest)所有响应式数据。 - -### 操作函数 - -继承[**useRequest**](/api/core-hooks#userequest)所有操作函数。 - -### 事件 - -继承[**useRequest**](/api/core-hooks#userequest)所有事件。 +--- +title: 串行请求的useRequest +sidebar_position: 100 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info 策略类型 + +use hook + +::: + +> 在使用扩展 hooks 前,确保你已熟悉了 alova 的基本使用。 + +这个 use hook 比[在最佳实践中的串行请求](/tutorial/best-practice/skills)更加简洁易用,统一的 loading 状态、error、回调函数。 + +## 特性 + +- 更加简洁易用的串行方式; +- 统一的请求状态和回调函数; +- send 函数可触发多个请求串行执行; + +## 示例 + +[串行请求](/tutorial/example/serial-request) + +## 安装 + + + + +```bash +# npm +npm install @alova/scene-vue --save +# yarn +yarn add @alova/scene-vue + +``` + + + + +```bash +# npm +npm install @alova/scene-react --save +# yarn +yarn add @alova/scene-react + +``` + + + + + +```bash +# npm +npm install @alova/scene-svelte --save +# yarn +yarn add @alova/scene-svelte + +``` + + + + +## 使用 + +### 基本用法 + +和`useRequest`的用法一样,只是第一个参数改变成了一个串行执行的 handler 数组,每个 handler 将接收上一个请求的响应数据。 + +```javascript +const { + // 串行加载状态,全部请求完成才会改为false + loading, + + // 最后一个请求的响应数据 + data, + + // 任意一个请求错误都将在这记录错误信息 + error, + + // 手动发送串行请求 + send, + + // 串行请求成功回调绑定函数 + onSuccess, + + // 串行请求错误回调绑定函数,任意一个请求错误都将触发它 + onError, + + // 串行请求完成回调绑定函数 + onComplete +} = useSerialRequest( + [ + // args为send函数传入的参数 + (...args) => request1(args), + + // 从第二个handler开始,第一个参数为上一个请求的响应数据,args从第二个开始接收 + (response1, ...args) => request2(response1, args), + (response2, ...args) => request3(response2, args) + ], + { + immediate: false + } +); + +// 手动触发请求并传参 +send(1, 2, 3); +``` + +值得注意的是,handler 数组中的第一项也可以指定为一个 method 实例,从第二项开始必须为函数。 + +```javascript +useSerialRequest([ + methodInstance, + (response1, ...args) => request2(response1, args), + (response2, ...args) => request3(response2, args) +]); +``` + +### 请求错误 + +串行请求任意一个请求错误时,将会触发`onError`,它的`event.method`将指向请求错误的 method 实例。 + +## API + +### Hook 配置 + +继承[**useRequest**](/api/core-hooks#userequest)所有配置。 + +### 响应式数据 + +继承[**useRequest**](/api/core-hooks#userequest)所有响应式数据。 + +### 操作函数 + +继承[**useRequest**](/api/core-hooks#userequest)所有操作函数。 + +### 事件 + +继承[**useRequest**](/api/core-hooks#userequest)所有事件。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/11-useSerialWatcher.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/11-useSerialWatcher.md index 0d8a15ded..cf12a0c0d 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/11-useSerialWatcher.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/11-useSerialWatcher.md @@ -1,145 +1,145 @@ ---- -title: 串行请求的useWatcher -sidebar_position: 110 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -:::info 策略类型 - -use hook - -::: - -> 在使用扩展 hooks 前,确保你已熟悉了 alova 的基本使用。 - -状态更新触发一组串行请求,比[在最佳实践中的串行请求](/tutorial/best-practice/skills)更加简洁易用,统一的 loading 状态、error、回调函数。 - -## 特性 - -- ✨ 更加简洁易用的串行方式; -- ✨ 统一的请求状态和回调函数; -- ✨ 状态更新触发多个请求串行执行; - -## 示例 - -[串行请求](/tutorial/example/serial-request) - -## 安装 - - - - -```bash -# npm -npm install @alova/scene-vue --save -# yarn -yarn add @alova/scene-vue - -``` - - - - -```bash -# npm -npm install @alova/scene-react --save -# yarn -yarn add @alova/scene-react - -``` - - - - - -```bash -# npm -npm install @alova/scene-svelte --save -# yarn -yarn add @alova/scene-svelte - -``` - - - - -## 使用 - -### 基本用法 - -和`useWatcher`的用法一样,只是第一个参数改变成了一个串行执行的 handler 数组,每个 handler 将接收上一个请求的响应数据。 - -```javascript -const { - // 串行加载状态,全部请求完成才会改为false - loading, - - // 最后一个请求的响应数据 - data, - - // 任意一个请求错误都将在这记录错误信息 - error, - - // 手动发送串行请求 - send, - - // 串行请求成功回调绑定函数 - onSuccess, - - // 串行请求错误回调绑定函数,任意一个请求错误都将触发它 - onError, - - // 串行请求完成回调绑定函数 - onComplete -} = useSerialWatcher( - [ - // args为send函数传入的参数 - (...args) => request1(args), - - // 从第二个handler开始,第一个参数为上一个请求的响应数据,args从第二个开始接收 - (response1, ...args) => request2(response1, args), - (response2, ...args) => request3(response2, args) - ], - [watchedState1, watchedState2], - { - immediate: true - } -); - -// 手动触发请求并传参 -send(1, 2, 3); -``` - -值得注意的是,handler 数组中的第一项也可以指定为一个 method 实例,从第二项开始必须为函数。 - -```javascript -useSerialRequest([ - methodInstance, - (response1, ...args) => request2(response1, args), - (response2, ...args) => request3(response2, args) -]); -``` - -### 请求错误 - -串行请求任意一个请求错误时,将会触发`onError`,它的`event.method`将指向请求错误的 method 实例。 - -## API - -### Hook 配置 - -继承[**useWatcher**](/api/core-hooks#usewatcher)所有配置。 - -### 响应式数据 - -继承[**useWatcher**](/api/core-hooks#usewatcher)所有响应式数据。 - -### 操作函数 - -继承[**useWatcher**](/api/core-hooks#usewatcher)所有操作函数。 - -### 事件 - -继承[**useWatcher**](/api/core-hooks#usewatcher)所有事件。 +--- +title: 串行请求的useWatcher +sidebar_position: 110 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info 策略类型 + +use hook + +::: + +> 在使用扩展 hooks 前,确保你已熟悉了 alova 的基本使用。 + +状态更新触发一组串行请求,比[在最佳实践中的串行请求](/tutorial/best-practice/skills)更加简洁易用,统一的 loading 状态、error、回调函数。 + +## 特性 + +- 更加简洁易用的串行方式; +- 统一的请求状态和回调函数; +- 状态更新触发多个请求串行执行; + +## 示例 + +[串行请求](/tutorial/example/serial-request) + +## 安装 + + + + +```bash +# npm +npm install @alova/scene-vue --save +# yarn +yarn add @alova/scene-vue + +``` + + + + +```bash +# npm +npm install @alova/scene-react --save +# yarn +yarn add @alova/scene-react + +``` + + + + + +```bash +# npm +npm install @alova/scene-svelte --save +# yarn +yarn add @alova/scene-svelte + +``` + + + + +## 使用 + +### 基本用法 + +和`useWatcher`的用法一样,只是第一个参数改变成了一个串行执行的 handler 数组,每个 handler 将接收上一个请求的响应数据。 + +```javascript +const { + // 串行加载状态,全部请求完成才会改为false + loading, + + // 最后一个请求的响应数据 + data, + + // 任意一个请求错误都将在这记录错误信息 + error, + + // 手动发送串行请求 + send, + + // 串行请求成功回调绑定函数 + onSuccess, + + // 串行请求错误回调绑定函数,任意一个请求错误都将触发它 + onError, + + // 串行请求完成回调绑定函数 + onComplete +} = useSerialWatcher( + [ + // args为send函数传入的参数 + (...args) => request1(args), + + // 从第二个handler开始,第一个参数为上一个请求的响应数据,args从第二个开始接收 + (response1, ...args) => request2(response1, args), + (response2, ...args) => request3(response2, args) + ], + [watchedState1, watchedState2], + { + immediate: true + } +); + +// 手动触发请求并传参 +send(1, 2, 3); +``` + +值得注意的是,handler 数组中的第一项也可以指定为一个 method 实例,从第二项开始必须为函数。 + +```javascript +useSerialRequest([ + methodInstance, + (response1, ...args) => request2(response1, args), + (response2, ...args) => request3(response2, args) +]); +``` + +### 请求错误 + +串行请求任意一个请求错误时,将会触发`onError`,它的`event.method`将指向请求错误的 method 实例。 + +## API + +### Hook 配置 + +继承[**useWatcher**](/api/core-hooks#usewatcher)所有配置。 + +### 响应式数据 + +继承[**useWatcher**](/api/core-hooks#usewatcher)所有响应式数据。 + +### 操作函数 + +继承[**useWatcher**](/api/core-hooks#usewatcher)所有操作函数。 + +### 事件 + +继承[**useWatcher**](/api/core-hooks#usewatcher)所有事件。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/12-useRetriableRequest.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/12-useRetriableRequest.md index f736f4386..086f1e48a 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/12-useRetriableRequest.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/12-useRetriableRequest.md @@ -1,269 +1,269 @@ ---- -title: 请求重试策略 -sidebar_position: 120 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -:::info 策略类型 - -use hook - -::: - -> 在使用扩展 hooks 前,确保你已熟悉了 alova 的基本使用。 - -请求失败可自动重试的 use hook,你可以将它用于重要的请求上。 - -## 示例 - -[请求重试 Demo](/tutorial/example/retriable-hook) - -## 特性 - -- ✨ 自定义重试次数或按条件判断是否需要重试; -- ✨ 重试延迟机制; -- ✨ 手动停止重试; - -## 安装 - - - - -```bash -# npm -npm install @alova/scene-vue --save -# yarn -yarn add @alova/scene-vue - -``` - - - - -```bash -# npm -npm install @alova/scene-react --save -# yarn -yarn add @alova/scene-react - -``` - - - - - -```bash -# npm -npm install @alova/scene-svelte --save -# yarn -yarn add @alova/scene-svelte - -``` - - - - -## 使用 - -### 基本用法 - -```javascript -const { - // 加载状态,在重试期间一直为true,直到重试成功或失败 - loading, - - // 响应数据 - data, - - // 请求错误信息,每次请求或重试失败都将会有error实例 - // 上一次的error实例将被覆盖 - error, - - // 每次请求或重试失败都将触发onError事件 - onError, - - // 请求重试事件,在每次重试请求发出后立即触发 - onRetry, - - // 请求重试失败事件 - // 达到最大重试次数仍未请求成功,或手动停止重试都将触发 - onFail, - - // 请求或重试成功事件 - onSuccess, - - // 每次请求或重试,无论成功或失败都将触发完成事件 - onComplete -} = useRetriableRequest(request); -``` - -`useRetriableRequest`的最大请求重试次数默认为 3,且每次将延迟 1 秒重试。同时也会默认发出请求,可以通过设置`immediate`为 false 改变行为。 - -### 设置静态的最大重试次数 - -最大重试次数表示首次请求失败后,最多重试请求的次数,期间如果请求成功的话将会停止继续重试。默认最大重试次数为 3 次,你可以通过以下方式自定义设置。 - -请求重试达到最大次数仍然未成功时,将会触发`onFail`事件并停止请求重试,如果你在失败后希望继续重试,可以调用`send`函数,此时它将进行新一轮的请求和重试。 - -```javascript -const { send } = useRetriableRequest(request, { - // ... - // highlight-start - // 设置最大重试次数为5 - retry: 5 - // highlight-end -}); -``` - -### 动态设置最大重试次数 - -可能有时候你希望通过某个条件来判断是否需要继续重试,此时你可以将`retry`设置为返回 boolean 值的函数,来动态判断是否继续重试。 - -```javascript -useRetriableRequest(request, { - // ... - // highlight-start - // 第一个参数为上一次的错误实例,从第二个参数开始为send传入的参数 - retry(error, ...args) { - // 请求超时则继续重试 - return /network timeout/i.test(error.message); - } - // highlight-end -}); -``` - -### 设置延迟时间 - -默认重试延迟时间为 1 秒,你可以通过以下方式自定义设置。 - -```javascript -useRetriableRequest(request, { - // ... - backoff: { - // highlight-start - // 设置延迟时间为2秒 - delay: 2000 - // highlight-end - } -}); -``` - -### 设置不固定的重试延迟时间 - -有时候你希望每次请求延迟时间都不是固定的,你可以按以下方式设置延迟增长倍数,延迟时间将按重试次数指数增长。 - -```javascript -useRetriableRequest(request, { - // ... - backoff: { - delay: 2000, - // highlight-start - // multiplier设置为2时,第一次重试延迟为2秒,第二次为4秒,第三次为8秒,以此类推。 - multiplier: 2 - // highlight-end - } -}); -``` - -还不够?你甚至还可以为每次延迟时间增加随机抖动值,让它看着不那么规律。 - -```javascript -useRetriableRequest(request, { - // ... - backoff: { - delay: 2000, - multiplier: 2, - // highlight-start - /** - * 延迟请求的抖动起始百分比值,范围为0-1 - * 当只设置了startQuiver时,endQuiver默认为1 - * 例如设置为0.5,它将在当前延迟时间上增加50%到100%的随机时间 - * 如果endQuiver有值,则延迟时间将增加startQuiver和endQuiver范围的随机值 - */ - startQuiver: 0.5, - - /** - * 延迟请求的抖动结束百分比值,范围为0-1 - * 当只设置了endQuiver时,startQuiver默认为0 - * 例如设置为0.8,它将在当前延迟时间上增加0%到80%的随机时间 - * 如果startQuiver有值,则延迟时间将增加startQuiver和endQuiver范围的随机值 - */ - endQuiver: 0.8; - // highlight-end - } -}); -``` - -### 手动停止重试 - -因某些情况需要手动停止重试,无论当前正在请求中,还是在等待下一次重试,你都可以使用`stop`来停止它。 - -```javascript -const { stop } = useRetriableRequest(request, { - // ... -}); - -const handleStop = () => { - stop(); -}; -``` - -## API - -### Hook 配置 - -继承[**useRequest**](/api/core-hooks#userequest)所有配置。 - -| 名称 | 描述 | 类型 | 默认值 | 版本 | -| ------- | ------------------------------------------------------------------------- | ------------------------------- | ---------------------------------------------- | ---- | -| retry | 最大重试次数,也可以设置为返回 boolean 值的函数,来动态判断是否继续重试。 | number | (error: Error, ...args: any[]) => boolean \| 3 | - | -| backoff | 避让策略,设置重试延迟时间等 | [BackoffPolicy](#backoffpolicy) | - | - | - -#### BackoffPolicy - -| 名称 | 描述 | 类型 | 默认值 | 版本 | -| ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | ------ | ---- | -| delay | 再次请求的延迟时间,单位毫秒 | number | 1000 | - | -| multiplier | 指定延迟倍数,例如把 multiplier 设置为 2、delay 为 1 秒时,第一次重试为 1 秒,第二次为 2 秒,第三次为 4 秒,以此类推 | number | 0 | - | -| startQuiver | 延迟请求的抖动起始百分比值,范围为 0-1,当只设置了 startQuiver 时,endQuiver 默认为 1,例如设置为 0.5,它将在当前延迟时间上增加 50%到 100%的随机时间,如果 endQuiver 有值,则延迟时间将增加 startQuiver 和 endQuiver 范围的随机值 | number | 0 | - | -| endQuiver | 延迟请求的抖动结束百分比值,范围为 0-1,当只设置了 endQuiver 时,startQuiver 默认为 0,例如设置为 0.5,它将在当前延迟时间上增加 0%到 50%的随机时间,如果 startQuiver 有值,则延迟时间将增加 startQuiver 和 endQuiver 范围的随机值 | number | 0 | - | - -### 响应式数据 - -继承[**useRequest**](/api/core-hooks#userequest)所有响应式数据。 - -### 操作函数 - -继承[**useRequest**](/api/core-hooks#userequest)所有操作函数。 - -| 名称 | 描述 | 函数参数 | 返回值 | 版本 | -| ---- | ------------------------------------------------------------ | -------- | ------ | ---- | -| stop | 停止重试,只在重试期间调用有效,停止后将立即触发 onFail 事件 | - | - | - | - -### 事件 - -继承[**useRequest**](/api/core-hooks#userequest)所有事件。 - -| 名称 | 描述 | 回调参数 | 版本 | -| ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | ---- | -| onRetry | 重试事件绑定,它们将在重试发起后触发 | 重试事件实例 [RetriableRetryEvent](#retriableretryevent) | - | -| onFail | 请求失败时触发,将在不再重试时触发,例如到达最大重试次数时,重试回调返回 false 时,手动调用 stop 停止重试时
注意:
1. onError 事件是在每次请求报错时都将被触发
2. 如果没有重试次数时,onError、onComplete 和 onFail 会被同时触发 | 重试事件实例 [RetriableFailEvent](#retriablefailevent) | - | - -#### RetriableRetryEvent - -继承于 alova 的 Event 事件实例。 - -| 名称 | 描述 | 类型 | 默认值 | 版本 | -| ---------- | ----------------------------- | ------ | -------- | ---- | -| retryTimes | 当前的重试次数 | number | required | - | -| retryDelay | 本次重试的延迟时间,单位为 ms | number | required | - | - -#### RetriableFailEvent - -继承于 alova 的 Event 事件实例。 - -| 名称 | 描述 | 类型 | 默认值 | 版本 | -| ---------- | -------------- | ------ | -------- | ---- | -| retryTimes | 当前的重试次数 | number | required | - | +--- +title: 请求重试策略 +sidebar_position: 120 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info 策略类型 + +use hook + +::: + +> 在使用扩展 hooks 前,确保你已熟悉了 alova 的基本使用。 + +请求失败可自动重试的 use hook,你可以将它用于重要的请求上。 + +## 示例 + +[请求重试 Demo](/tutorial/example/retriable-hook) + +## 特性 + +- 自定义重试次数或按条件判断是否需要重试; +- 重试延迟机制; +- 手动停止重试; + +## 安装 + + + + +```bash +# npm +npm install @alova/scene-vue --save +# yarn +yarn add @alova/scene-vue + +``` + + + + +```bash +# npm +npm install @alova/scene-react --save +# yarn +yarn add @alova/scene-react + +``` + + + + + +```bash +# npm +npm install @alova/scene-svelte --save +# yarn +yarn add @alova/scene-svelte + +``` + + + + +## 使用 + +### 基本用法 + +```javascript +const { + // 加载状态,在重试期间一直为true,直到重试成功或失败 + loading, + + // 响应数据 + data, + + // 请求错误信息,每次请求或重试失败都将会有error实例 + // 上一次的error实例将被覆盖 + error, + + // 每次请求或重试失败都将触发onError事件 + onError, + + // 请求重试事件,在每次重试请求发出后立即触发 + onRetry, + + // 请求重试失败事件 + // 达到最大重试次数仍未请求成功,或手动停止重试都将触发 + onFail, + + // 请求或重试成功事件 + onSuccess, + + // 每次请求或重试,无论成功或失败都将触发完成事件 + onComplete +} = useRetriableRequest(request); +``` + +`useRetriableRequest`的最大请求重试次数默认为 3,且每次将延迟 1 秒重试。同时也会默认发出请求,可以通过设置`immediate`为 false 改变行为。 + +### 设置静态的最大重试次数 + +最大重试次数表示首次请求失败后,最多重试请求的次数,期间如果请求成功的话将会停止继续重试。默认最大重试次数为 3 次,你可以通过以下方式自定义设置。 + +请求重试达到最大次数仍然未成功时,将会触发`onFail`事件并停止请求重试,如果你在失败后希望继续重试,可以调用`send`函数,此时它将进行新一轮的请求和重试。 + +```javascript +const { send } = useRetriableRequest(request, { + // ... + // highlight-start + // 设置最大重试次数为5 + retry: 5 + // highlight-end +}); +``` + +### 动态设置最大重试次数 + +可能有时候你希望通过某个条件来判断是否需要继续重试,此时你可以将`retry`设置为返回 boolean 值的函数,来动态判断是否继续重试。 + +```javascript +useRetriableRequest(request, { + // ... + // highlight-start + // 第一个参数为上一次的错误实例,从第二个参数开始为send传入的参数 + retry(error, ...args) { + // 请求超时则继续重试 + return /network timeout/i.test(error.message); + } + // highlight-end +}); +``` + +### 设置延迟时间 + +默认重试延迟时间为 1 秒,你可以通过以下方式自定义设置。 + +```javascript +useRetriableRequest(request, { + // ... + backoff: { + // highlight-start + // 设置延迟时间为2秒 + delay: 2000 + // highlight-end + } +}); +``` + +### 设置不固定的重试延迟时间 + +有时候你希望每次请求延迟时间都不是固定的,你可以按以下方式设置延迟增长倍数,延迟时间将按重试次数指数增长。 + +```javascript +useRetriableRequest(request, { + // ... + backoff: { + delay: 2000, + // highlight-start + // multiplier设置为2时,第一次重试延迟为2秒,第二次为4秒,第三次为8秒,以此类推。 + multiplier: 2 + // highlight-end + } +}); +``` + +还不够?你甚至还可以为每次延迟时间增加随机抖动值,让它看着不那么规律。 + +```javascript +useRetriableRequest(request, { + // ... + backoff: { + delay: 2000, + multiplier: 2, + // highlight-start + /** + * 延迟请求的抖动起始百分比值,范围为0-1 + * 当只设置了startQuiver时,endQuiver默认为1 + * 例如设置为0.5,它将在当前延迟时间上增加50%到100%的随机时间 + * 如果endQuiver有值,则延迟时间将增加startQuiver和endQuiver范围的随机值 + */ + startQuiver: 0.5, + + /** + * 延迟请求的抖动结束百分比值,范围为0-1 + * 当只设置了endQuiver时,startQuiver默认为0 + * 例如设置为0.8,它将在当前延迟时间上增加0%到80%的随机时间 + * 如果startQuiver有值,则延迟时间将增加startQuiver和endQuiver范围的随机值 + */ + endQuiver: 0.8; + // highlight-end + } +}); +``` + +### 手动停止重试 + +因某些情况需要手动停止重试,无论当前正在请求中,还是在等待下一次重试,你都可以使用`stop`来停止它。 + +```javascript +const { stop } = useRetriableRequest(request, { + // ... +}); + +const handleStop = () => { + stop(); +}; +``` + +## API + +### Hook 配置 + +继承[**useRequest**](/api/core-hooks#userequest)所有配置。 + +| 名称 | 描述 | 类型 | 默认值 | 版本 | +| ------- | ------------------------------------------------------------------------- | ------------------------------- | ---------------------------------------------- | ---- | +| retry | 最大重试次数,也可以设置为返回 boolean 值的函数,来动态判断是否继续重试。 | number | (error: Error, ...args: any[]) => boolean \| 3 | - | +| backoff | 避让策略,设置重试延迟时间等 | [BackoffPolicy](#backoffpolicy) | - | - | + +#### BackoffPolicy + +| 名称 | 描述 | 类型 | 默认值 | 版本 | +| ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | ------ | ---- | +| delay | 再次请求的延迟时间,单位毫秒 | number | 1000 | - | +| multiplier | 指定延迟倍数,例如把 multiplier 设置为 2、delay 为 1 秒时,第一次重试为 1 秒,第二次为 2 秒,第三次为 4 秒,以此类推 | number | 0 | - | +| startQuiver | 延迟请求的抖动起始百分比值,范围为 0-1,当只设置了 startQuiver 时,endQuiver 默认为 1,例如设置为 0.5,它将在当前延迟时间上增加 50%到 100%的随机时间,如果 endQuiver 有值,则延迟时间将增加 startQuiver 和 endQuiver 范围的随机值 | number | 0 | - | +| endQuiver | 延迟请求的抖动结束百分比值,范围为 0-1,当只设置了 endQuiver 时,startQuiver 默认为 0,例如设置为 0.5,它将在当前延迟时间上增加 0%到 50%的随机时间,如果 startQuiver 有值,则延迟时间将增加 startQuiver 和 endQuiver 范围的随机值 | number | 0 | - | + +### 响应式数据 + +继承[**useRequest**](/api/core-hooks#userequest)所有响应式数据。 + +### 操作函数 + +继承[**useRequest**](/api/core-hooks#userequest)所有操作函数。 + +| 名称 | 描述 | 函数参数 | 返回值 | 版本 | +| ---- | ------------------------------------------------------------ | -------- | ------ | ---- | +| stop | 停止重试,只在重试期间调用有效,停止后将立即触发 onFail 事件 | - | - | - | + +### 事件 + +继承[**useRequest**](/api/core-hooks#userequest)所有事件。 + +| 名称 | 描述 | 回调参数 | 版本 | +| ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | ---- | +| onRetry | 重试事件绑定,它们将在重试发起后触发 | 重试事件实例 [RetriableRetryEvent](#retriableretryevent) | - | +| onFail | 请求失败时触发,将在不再重试时触发,例如到达最大重试次数时,重试回调返回 false 时,手动调用 stop 停止重试时
注意:
1. onError 事件是在每次请求报错时都将被触发
2. 如果没有重试次数时,onError、onComplete 和 onFail 会被同时触发 | 重试事件实例 [RetriableFailEvent](#retriablefailevent) | - | + +#### RetriableRetryEvent + +继承于 alova 的 Event 事件实例。 + +| 名称 | 描述 | 类型 | 默认值 | 版本 | +| ---------- | ----------------------------- | ------ | -------- | ---- | +| retryTimes | 当前的重试次数 | number | required | - | +| retryDelay | 本次重试的延迟时间,单位为 ms | number | required | - | + +#### RetriableFailEvent + +继承于 alova 的 Event 事件实例。 + +| 名称 | 描述 | 类型 | 默认值 | 版本 | +| ---------- | -------------- | ------ | -------- | ---- | +| retryTimes | 当前的重试次数 | number | required | - | diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/13-useSSE.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/13-useSSE.md index c7fdbb8ee..fc49ad9b9 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/13-useSSE.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/13-useSSE.md @@ -1,289 +1,287 @@ ---- -title: Server-sent events发送请求 -sidebar_position: 130 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -:::info 策略类型 - -use hook - -::: - -> 在使用扩展 hooks 前,确保你已熟悉了 alova 的基本使用。 - -通过 Server-sent Events(SSE)请求,内部使用`EventSource`实现。 - -## 特性 - -- ✨ 更加简洁易用的使用方式; -- ✨ 自动管理连接; - - -## 安装 - - - - -```bash -# npm -npm install @alova/scene-vue --save -# yarn -yarn add @alova/scene-vue - -``` - - - - -```bash -# npm -npm install @alova/scene-react --save -# yarn -yarn add @alova/scene-react - -``` - - - - - -```bash -# npm -npm install @alova/scene-svelte --save -# yarn -yarn add @alova/scene-svelte - -``` - - - - -## 用法 - - - - -```typescript -import { useSSE } from '@alova/scene-vue'; - -const method = (value: string) => alova.Get('/api/source', { param: { key: value } }); -const { data, eventSource, readyState, onMessage, onError, on, send, close } = useSSE(method, { - initialData: 'initial-data' // 初始时 data 中的数据 -}); - -// connect -send('value'); - -console.log(data.value); // data 在接收到事件后更新,默认是 initialData - -// 对应 eventsource 的 message 事件 -const unbindMessage = onMessage(({ data }) => { - console.log(data); -}); - -const unbindError = onError(({ error }) => { - console.error('sse error', error); - close(); -}) - -// 在需要的时候解绑 -unbindMessage(); -unbindError(); -``` - - - - -```typescript -import { useSSE } from '@alova/scene-react'; - -const method = (value: string) => alova.Get('/api/source', { param: { key: value } }); -const { data, eventSource, readyState, onMessage, onError, on, send, close } = useSSE(method, { - initialData: 'initial-data' // 初始时 data 中的数据 -}); - -// connect -send('value'); - -console.log(data); // data 在接收到事件后更新,默认是 initialData - -// 对应 eventsource 的 message 事件 -const unbindMessage = onMessage(({ data }) => { - console.log(data); -}); - -const unbindError = onError(({ error }) => { - console.error('sse error', error); - close(); -}) - -// 在需要的时候解绑 -unbindMessage(); -unbindError(); -``` - - - - - -```typescript -import { useSSE } from '@alova/scene-svelte'; - -const method = (value: string) => alova.Get('/api/source', { param: { key: value } }); -const { data, eventSource, readyState, onMessage, onError, on, send, close } = useSSE(method, { - initialData: 'initial-data' // 初始时 data 中的数据 -}); - -// connect -send('value'); - -console.log(data); // data 在接收到事件后更新,默认是 initialData - -// 对应 eventsource 的 message 事件 -const unbindMessage = onMessage(({ data }) => { - console.log(data); -}); - -const unbindError = onError(({ error }) => { - console.error('sse error', error); - close(); -}) - -// 在需要的时候解绑 -unbindMessage(); -unbindError(); -``` - - - - -:::warning - -`useSSE` 目前只能连接到一个源。也就是说,当试图连接多个目标时,上一个连接总会被断开。 - -::: - -``` typescript -const { data, eventSource, readyState, onMessage, onError, on, send, close } = useSSE(method); - -send('value1'); -// highlight-start -send('value2'); // 这会断开上一个连接 -send('value3'); // 这也会断开上一个连接 -// highlight-end -``` - -默认情况下,不会发送请求。当然,通过设置`immediate = true`,可以省去手动 send 的一步。 - -```typescript -const { data, eventSource, readyState, onMessage, onError, on, send, close } = useSSE(method, { - // highlight-start - immediate: true - // highlight-end -}); - -// codes here... -``` - -### 绑定自定义事件 - -```typescript -const { data, readyState, onMessage, on } = useSSE(method); - -on('event-name', ({ data }) => { - console.log(data); -}); -``` - -### 全局响应拦截 - -默认情况下,响应数据受到[全局响应拦截器的捕获](/tutorial/combine-framework/response)。如果这不是你预期的行为,可以手动关闭。 - -```typescript -const { data, readyState, onMessage, on } = useSSE(method, { - // highlight-start - interceptByGlobalResponded: false // 现在数据不会被响应拦截 - // highlight-end -}); -``` - -## 类型声明 - -``` typescript -const enum SSEHookReadyState { - CONNECTING = 0, - OPEN = 1, - CLOSED = 2 -}; - -type SSEHookConfig = { - /** - * 会传给new EventSource - */ - withCredentials?: boolean; - - /** - * 是否经过alova实例的responded拦截 - * @default true - */ - interceptByGlobalResponded?: boolean; - - /** - * 初始数据 - */ - initialData?: any; - - /** - * 是否立即发起请求 - * @default false - */ - immediate?: boolean; -}; - -type SSEReturnType = { - readyState: ExportedType; - data: ExportedType; - eventSource: ExportedType; - /** - * 手动发起请求。在使用 `immediate: true` 时该方法会自动触发 - * @param sendArgs 请求参数,会传递给 method - */ - send: (...sendArgs: any[]) => Promise; - /** - * 关闭连接 - */ - close: () => void; - /** - * 注册 EventSource open 的回调函数 - * @param callback 回调函数 - * @returns 取消注册函数 - */ - onOpen(callback: SSEOnOpenTrigger): () => void; - - /** - * 注册 EventSource message 的回调函数 - * @param callback 回调函数 - * @returns 取消注册函数 - */ - onMessage(callback: SSEOnMessageTrigger): () => void; - - /** - * 注册 EventSource error 的回调函数 - * @param callback 回调函数 - * @returns 取消注册函数 - */ - onError(callback: SSEOnErrorTrigger): () => void; - - /** - * @param eventName 事件名称,默认存在 `open` | `error` | `message` - * @param handler 事件处理器 - */ - on( - eventName: string, - handler: (event: AlovaSSEMessageEvent) => void - ) => () => void; -}; -``` +--- +title: Server-sent events发送请求 +sidebar_position: 130 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info 策略类型 + +use hook + +::: + +> 在使用扩展 hooks 前,确保你已熟悉了 alova 的基本使用。 + +通过 Server-sent Events(SSE)请求,内部使用`EventSource`实现。 + +## 特性 + +- 更加简洁易用的使用方式; +- 自动管理连接; + +## 安装 + + + + +```bash +# npm +npm install @alova/scene-vue --save +# yarn +yarn add @alova/scene-vue + +``` + + + + +```bash +# npm +npm install @alova/scene-react --save +# yarn +yarn add @alova/scene-react + +``` + + + + + +```bash +# npm +npm install @alova/scene-svelte --save +# yarn +yarn add @alova/scene-svelte + +``` + + + + +## 用法 + + + + +```typescript +import { useSSE } from '@alova/scene-vue'; + +const method = (value: string) => alova.Get('/api/source', { param: { key: value } }); +const { data, eventSource, readyState, onMessage, onError, on, send, close } = useSSE(method, { + initialData: 'initial-data' // 初始时 data 中的数据 +}); + +// connect +send('value'); + +console.log(data.value); // data 在接收到事件后更新,默认是 initialData + +// 对应 eventsource 的 message 事件 +const unbindMessage = onMessage(({ data }) => { + console.log(data); +}); + +const unbindError = onError(({ error }) => { + console.error('sse error', error); + close(); +}); + +// 在需要的时候解绑 +unbindMessage(); +unbindError(); +``` + + + + +```typescript +import { useSSE } from '@alova/scene-react'; + +const method = (value: string) => alova.Get('/api/source', { param: { key: value } }); +const { data, eventSource, readyState, onMessage, onError, on, send, close } = useSSE(method, { + initialData: 'initial-data' // 初始时 data 中的数据 +}); + +// connect +send('value'); + +console.log(data); // data 在接收到事件后更新,默认是 initialData + +// 对应 eventsource 的 message 事件 +const unbindMessage = onMessage(({ data }) => { + console.log(data); +}); + +const unbindError = onError(({ error }) => { + console.error('sse error', error); + close(); +}); + +// 在需要的时候解绑 +unbindMessage(); +unbindError(); +``` + + + + +```typescript +import { useSSE } from '@alova/scene-svelte'; + +const method = (value: string) => alova.Get('/api/source', { param: { key: value } }); +const { data, eventSource, readyState, onMessage, onError, on, send, close } = useSSE(method, { + initialData: 'initial-data' // 初始时 data 中的数据 +}); + +// connect +send('value'); + +console.log(data); // data 在接收到事件后更新,默认是 initialData + +// 对应 eventsource 的 message 事件 +const unbindMessage = onMessage(({ data }) => { + console.log(data); +}); + +const unbindError = onError(({ error }) => { + console.error('sse error', error); + close(); +}); + +// 在需要的时候解绑 +unbindMessage(); +unbindError(); +``` + + + + +:::warning + +`useSSE` 目前只能连接到一个源。也就是说,当试图连接多个目标时,上一个连接总会被断开。 + +::: + +```typescript +const { data, eventSource, readyState, onMessage, onError, on, send, close } = useSSE(method); + +send('value1'); +// highlight-start +send('value2'); // 这会断开上一个连接 +send('value3'); // 这也会断开上一个连接 +// highlight-end +``` + +默认情况下,不会发送请求。当然,通过设置`immediate = true`,可以省去手动 send 的一步。 + +```typescript +const { data, eventSource, readyState, onMessage, onError, on, send, close } = useSSE(method, { + // highlight-start + immediate: true + // highlight-end +}); + +// codes here... +``` + +### 绑定自定义事件 + +```typescript +const { data, readyState, onMessage, on } = useSSE(method); + +on('event-name', ({ data }) => { + console.log(data); +}); +``` + +### 全局响应拦截 + +默认情况下,响应数据受到[全局响应拦截器的捕获](/tutorial/combine-framework/response)。如果这不是你预期的行为,可以手动关闭。 + +```typescript +const { data, readyState, onMessage, on } = useSSE(method, { + // highlight-start + interceptByGlobalResponded: false // 现在数据不会被响应拦截 + // highlight-end +}); +``` + +## 类型声明 + +```typescript +const enum SSEHookReadyState { + CONNECTING = 0, + OPEN = 1, + CLOSED = 2 +}; + +type SSEHookConfig = { + /** + * 会传给new EventSource + */ + withCredentials?: boolean; + + /** + * 是否经过alova实例的responded拦截 + * @default true + */ + interceptByGlobalResponded?: boolean; + + /** + * 初始数据 + */ + initialData?: any; + + /** + * 是否立即发起请求 + * @default false + */ + immediate?: boolean; +}; + +type SSEReturnType = { + readyState: ExportedType; + data: ExportedType; + eventSource: ExportedType; + /** + * 手动发起请求。在使用 `immediate: true` 时该方法会自动触发 + * @param sendArgs 请求参数,会传递给 method + */ + send: (...sendArgs: any[]) => Promise; + /** + * 关闭连接 + */ + close: () => void; + /** + * 注册 EventSource open 的回调函数 + * @param callback 回调函数 + * @returns 取消注册函数 + */ + onOpen(callback: SSEOnOpenTrigger): () => void; + + /** + * 注册 EventSource message 的回调函数 + * @param callback 回调函数 + * @returns 取消注册函数 + */ + onMessage(callback: SSEOnMessageTrigger): () => void; + + /** + * 注册 EventSource error 的回调函数 + * @param callback 回调函数 + * @returns 取消注册函数 + */ + onError(callback: SSEOnErrorTrigger): () => void; + + /** + * @param eventName 事件名称,默认存在 `open` | `error` | `message` + * @param handler 事件处理器 + */ + on( + eventName: string, + handler: (event: AlovaSSEMessageEvent) => void + ) => () => void; +}; +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/01-alova-mock.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/01-alova-mock.md index d53238bcb..36676f14b 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/01-alova-mock.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/01-alova-mock.md @@ -1,329 +1,329 @@ ---- -title: 模拟数据 -sidebar_position: 10 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -此 mock 插件是一个 alova 的请求适配器,与传统的 Proxy 形式不同,你可以很好地控制使用 mock 数据的使用范围,你可以控制全局范围、某一组接口范围,甚至是某一个接口的启用和禁用,这在我们实际的业务场景中是很有用的,每一次的迭代都会新增或修改一组接口,我们希望让之前的功能还是走已开发好的接口,而让新增或修改的接口走模拟数据,此时就可以将每个开发人员针对本次迭代涉及到的接口分为一组,并对它们进行开启或关闭。 - -## 特性 - -- ✨ 与 alova 无缝协作 -- ✨ 模拟请求任意分组,可控制全局、组、以及单个模拟接口的启用和禁用 -- ✨ 与 mockjs 配合使用 -- ✨ 不污染生产环境 - -## 安装 - - - - -```bash -npm install @alova/mock --save -``` - - - - -```bash -yarn add @alova/mock -``` - - - - -以下为使用流程。 - -## 使用 - -### 定义 mock 接口 - -使用`defineMock`定义一组 mock 接口,你可以在每一项模拟接口中直接指定返回响应数据,或指定为回调函数动态计算响应数据。 - -```javascript title=mockGrou1.js -import { defineMock } from '@alova/mock'; - -export default defineMock( - { - // 捕获get请求 - '/todo': [1, 2, 3, 4], - - // rest风格请求 - '/todo/{id}': ({ params }) => { - const id = params.id; - // ... - return { - title: '...', - time: '10:00' - }; - }, - - // 捕获post请求 - '[POST]/todo': ({ query, data }) => { - // ... - return { success: true }; - }, - - // 返回更详细的信息 - '[POST]/todo': ({ query, data }) => { - // ... - return { - status: 403, - statusText: 'unknown error', - responseHeaders: { - // ... - }, - body: { - success: true - } - }; - }, - - // 模拟网络错误 - '[POST]/todo': ({ query, data }) => { - throw new Error('network error'); - }, - - // key前面添加`-`,表示禁用此mock接口 - '-[DELETE]/todo/{id}': ({ params }) => { - // ... - return { success: true }; - } - }, - true -); // 第二个参数表示是否启用本组mock接口,默认为true,可以指定为false关闭 -``` - -### 创建模拟请求适配器 - -在调用`createAlova`时创建一个模拟请求适配器,并将 mock 接口传入即可完成。 - -```javascript -import GlobalFetch from 'alova/GlobalFetch'; -import { createAlovaMockAdapter } from '@alova/mock'; -import mockGroup1 from './mockGroup1'; - -// highlight-start -const mockAdapter = createAlovaMockAdapter([mockGroup1, /** ... */], { - // 全局控制是否启用mock接口,默认为true - enable: true, - - // 非模拟请求适配器,用于未匹配mock接口时发送请求 - httpAdapter: GlobalFetch(), - - // mock接口响应延迟,单位毫秒 - delay: 1000, - - // 是否打印mock接口请求信息 - mockRequestLogger: true, - - // 模拟接口回调,data为返回的模拟数据,你可以用它构造任何你想要的对象返回给alova - // 以下为默认的回调函数,它适用于使用GlobalFetch请求适配器 - // 如果你使用的是其他请求适配器,在模拟接口回调中请自定义返回适合适配器的数据结构 - onMockResponse: data => new Response(JSON.stringify(data)) -}); -// highlight-end - -export const alovaInst = createAlova({ - baseURL: 'http://xxx', - - // 使用mock请求适配器,如果需要切换适配器,请看下面的实践建议 - requestAdapter: mockAdapter, - - statesHook: /** ... */ -}); -``` - -### 路径匹配模式 - -:::info 版本要求 - -1.5.0+ - -::: - -默认情况下,在`defineMock`中定义的路径是一个 url 的完整 pathname,看以下代码片段。 - -```javascript -const alovaInst = createAlova({ - baseURL: 'https://api.alovajs.org' - // ... -}); -alovaInst.Get('/user?id=1').send(); -``` - -示例中的请求路径为`https://api.alovajs.org/user?id=1`时,它的完整 pathname 为`/user`,此时可以匹配到`defineMock`中的`/user`。 - -通常情况下这已经足够了,但是当你的 baseURL 不仅仅是一个域名时。 - -```javascript -const alovaInst = createAlova({ - baseURL: 'https://api.alovajs.org/v1/subname' - // ... -}); -alovaInst.Get('/user?id=1').send(); -``` - -这个示例中的请求路径为`https://api.alovajs.org/v1/subname/user?id=1`,mock 的匹配路径为`/v1/subname/user`,需要将 baseURL 中的`/v1/subname`也一同写上,当接口数量较多时就稍显冗余。 - -此时,你可以在`createAlovaMockAdapter`中设置`matchMode`为`methodurl`,它将只匹配 method 实例中定义的 url,例如上面的实例将会匹配到`/user?id=1`,而不再需要写 baseURL 中的部分,相对的,如果 method 实例中的 url 中带了 get 参数时,也需要将它一同写到`defineMock`的匹配路径中,就像这边的`?id=1`。 - -```javascript -createAlovaMockAdapter([mockGroup1 /** ... */], { - // ... - // highlight-start - matchMode: 'methodurl' - // highlight-end -}); -``` - -## 实践建议 - -### 按每个开发者每次版本分组接口 - -在团队开发场景下,每次版本开发时我们经常只需要对部分未开发好的接口进行模拟请求,并且对之前版本的接口使用测试环境接口,此时为了达到更好的模拟接口管理,可以以开发版本和开发者两个维度将接口分组。 - -例如有两个开发者名为 _August_、_kevin_,他们正在开发 v1.1 产品功能,他们可以这样管理模拟接口。 - -```javascript title=August-v1.1.js -import { defineMock } from '@alova/mock'; - -export default defineMock({ - '/todo': [ - /** */ - ], - '[POST]/todo': ({ data }) => { - // ... - // return ... - } - // ... -}); -``` - -```javascript title=kevin-v1.1.js -import { defineMock } from '@alova/mock'; - -export default defineMock({ - '[PUT]/todo/add': ({ data }) => { - // ... - // return ... - }, - '[DELETE]/todo/remove': ({ data }) => { - // ... - // return ... - } - // ... -}); -``` - -```javascript title=request.js -import Augustv1_1 from './August-v1.1'; -import kevinv1_1 from './kevin-v1.1'; - -const mockAdapter = createAlovaMockAdapter([Augustv1_1, kevinv1_1], { - httpAdapter: GlobalFetch(), - delay: 1000 -}); -export const alovaInst = createAlova({ - baseURL: 'http://xxx', - requestAdapter: mockAdapter - // ... -}); -``` - -### 在生产环境中排除 mock 代码 - -mock 数据一般只作用于开发环境,在生产环境下将会切换到实际的接口中,因此这段 mock 代码在生产环境就变得没有作用,此时我们可以通过环境变量的判断来排除这块代码,你只需要这样做: - -```javascript -const globalFetch = GlobalFetch(); -const mockAdapter = createAlovaMockAdapter([mockGroup1, /** ... */], { - httpAdapter: globalFetch, - delay: 1000, -}); - -export const alovaInst = createAlova({ - baseURL: 'http://xxx', - - // highlight-start - // 通过环境变量控制生产环境下,不会将mock相关代码打包进去 - requestAdapter: process.env.NODE_ENV === 'development' ? mockAdapter : globalFetch, - // highlight-end - - statesHook: /** ... */ -}); -``` - -### 与 mockjs 一同使用 - -如果你不希望自己编写模拟数据,而是使用模拟数据库(例如 mockjs)一同使用,你可以这样做。 - -```javascript -import { defineMock } from '@alova/mock'; -import Mock from 'mockjs'; - -export default defineMock({ - '/api1': Mock.mock({ - 'id|1-10000': 100 - }) -}); -``` - -## 转换模拟数据 - -**@alova/mock** 默认将响应数据包装为 Response 实例,将响应头默认包装为 Headers 实例,这是针对`GlobalFetch`进行适配的,但如果使用其他的请求适配器,就需要将模拟数据转换为相应的格式。 - -### 转换响应数据 - -你可以在`onMockResponse`字段中拦截模拟响应数据并返回转换后的响应数据以及响应头。 - -> 你也可以在 onMockResponse 中抛出一个错误,表示请求失败。 - -```javascript -const mockAdapter = createAlovaMockAdapter( - [ - /* 模拟数据 */ - ], - { - // ... - // highlight-start - onMockResponse(response, request, currentMethod) { - // response为相应数据集合,其中包含status、statusText、responseHeaders、body - // request为请求数据,其中包含query、params、headers、data - // currentMethod为当前请求的method实例 - // ... - // 返回转换后的响应数据和响应头 - return { - response: /** 响应数据 */, - headers: /** 响应头 */ - }; - } - // highlight-end - } -); -``` - -### 转换错误对象 - -你可以在`onMockError`字段中拦截错误实例并返回转换后的错误信息。 - -```javascript -const mockAdapter = createAlovaMockAdapter( - [ - /* 模拟数据 */ - ], - { - // ... - // highlight-start - onMockError(error, currentMethod) { - // error为错误实例 - // currentMethod为当前请求的method实例 - // ... - // 返回转换后的错误信息集合 - } - // highlight-end - } -); -``` +--- +title: 模拟数据 +sidebar_position: 10 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +此 mock 插件是一个 alova 的请求适配器,与传统的 Proxy 形式不同,你可以很好地控制使用 mock 数据的使用范围,你可以控制全局范围、某一组接口范围,甚至是某一个接口的启用和禁用,这在我们实际的业务场景中是很有用的,每一次的迭代都会新增或修改一组接口,我们希望让之前的功能还是走已开发好的接口,而让新增或修改的接口走模拟数据,此时就可以将每个开发人员针对本次迭代涉及到的接口分为一组,并对它们进行开启或关闭。 + +## 特性 + +- 与 alova 无缝协作 +- 模拟请求任意分组,可控制全局、组、以及单个模拟接口的启用和禁用 +- 与 mockjs 配合使用 +- 不污染生产环境 + +## 安装 + + + + +```bash +npm install @alova/mock --save +``` + + + + +```bash +yarn add @alova/mock +``` + + + + +以下为使用流程。 + +## 使用 + +### 定义 mock 接口 + +使用`defineMock`定义一组 mock 接口,你可以在每一项模拟接口中直接指定返回响应数据,或指定为回调函数动态计算响应数据。 + +```javascript title=mockGrou1.js +import { defineMock } from '@alova/mock'; + +export default defineMock( + { + // 捕获get请求 + '/todo': [1, 2, 3, 4], + + // rest风格请求 + '/todo/{id}': ({ params }) => { + const id = params.id; + // ... + return { + title: '...', + time: '10:00' + }; + }, + + // 捕获post请求 + '[POST]/todo': ({ query, data }) => { + // ... + return { success: true }; + }, + + // 返回更详细的信息 + '[POST]/todo': ({ query, data }) => { + // ... + return { + status: 403, + statusText: 'unknown error', + responseHeaders: { + // ... + }, + body: { + success: true + } + }; + }, + + // 模拟网络错误 + '[POST]/todo': ({ query, data }) => { + throw new Error('network error'); + }, + + // key前面添加`-`,表示禁用此mock接口 + '-[DELETE]/todo/{id}': ({ params }) => { + // ... + return { success: true }; + } + }, + true +); // 第二个参数表示是否启用本组mock接口,默认为true,可以指定为false关闭 +``` + +### 创建模拟请求适配器 + +在调用`createAlova`时创建一个模拟请求适配器,并将 mock 接口传入即可完成。 + +```javascript +import GlobalFetch from 'alova/GlobalFetch'; +import { createAlovaMockAdapter } from '@alova/mock'; +import mockGroup1 from './mockGroup1'; + +// highlight-start +const mockAdapter = createAlovaMockAdapter([mockGroup1, /** ... */], { + // 全局控制是否启用mock接口,默认为true + enable: true, + + // 非模拟请求适配器,用于未匹配mock接口时发送请求 + httpAdapter: GlobalFetch(), + + // mock接口响应延迟,单位毫秒 + delay: 1000, + + // 是否打印mock接口请求信息 + mockRequestLogger: true, + + // 模拟接口回调,data为返回的模拟数据,你可以用它构造任何你想要的对象返回给alova + // 以下为默认的回调函数,它适用于使用GlobalFetch请求适配器 + // 如果你使用的是其他请求适配器,在模拟接口回调中请自定义返回适合适配器的数据结构 + onMockResponse: data => new Response(JSON.stringify(data)) +}); +// highlight-end + +export const alovaInst = createAlova({ + baseURL: 'http://xxx', + + // 使用mock请求适配器,如果需要切换适配器,请看下面的实践建议 + requestAdapter: mockAdapter, + + statesHook: /** ... */ +}); +``` + +### 路径匹配模式 + +:::info 版本要求 + +1.5.0+ + +::: + +默认情况下,在`defineMock`中定义的路径是一个 url 的完整 pathname,看以下代码片段。 + +```javascript +const alovaInst = createAlova({ + baseURL: 'https://api.alovajs.org' + // ... +}); +alovaInst.Get('/user?id=1').send(); +``` + +示例中的请求路径为`https://api.alovajs.org/user?id=1`时,它的完整 pathname 为`/user`,此时可以匹配到`defineMock`中的`/user`。 + +通常情况下这已经足够了,但是当你的 baseURL 不仅仅是一个域名时。 + +```javascript +const alovaInst = createAlova({ + baseURL: 'https://api.alovajs.org/v1/subname' + // ... +}); +alovaInst.Get('/user?id=1').send(); +``` + +这个示例中的请求路径为`https://api.alovajs.org/v1/subname/user?id=1`,mock 的匹配路径为`/v1/subname/user`,需要将 baseURL 中的`/v1/subname`也一同写上,当接口数量较多时就稍显冗余。 + +此时,你可以在`createAlovaMockAdapter`中设置`matchMode`为`methodurl`,它将只匹配 method 实例中定义的 url,例如上面的实例将会匹配到`/user?id=1`,而不再需要写 baseURL 中的部分,相对的,如果 method 实例中的 url 中带了 get 参数时,也需要将它一同写到`defineMock`的匹配路径中,就像这边的`?id=1`。 + +```javascript +createAlovaMockAdapter([mockGroup1 /** ... */], { + // ... + // highlight-start + matchMode: 'methodurl' + // highlight-end +}); +``` + +## 实践建议 + +### 按每个开发者每次版本分组接口 + +在团队开发场景下,每次版本开发时我们经常只需要对部分未开发好的接口进行模拟请求,并且对之前版本的接口使用测试环境接口,此时为了达到更好的模拟接口管理,可以以开发版本和开发者两个维度将接口分组。 + +例如有两个开发者名为 _August_、_kevin_,他们正在开发 v1.1 产品功能,他们可以这样管理模拟接口。 + +```javascript title=August-v1.1.js +import { defineMock } from '@alova/mock'; + +export default defineMock({ + '/todo': [ + /** */ + ], + '[POST]/todo': ({ data }) => { + // ... + // return ... + } + // ... +}); +``` + +```javascript title=kevin-v1.1.js +import { defineMock } from '@alova/mock'; + +export default defineMock({ + '[PUT]/todo/add': ({ data }) => { + // ... + // return ... + }, + '[DELETE]/todo/remove': ({ data }) => { + // ... + // return ... + } + // ... +}); +``` + +```javascript title=request.js +import Augustv1_1 from './August-v1.1'; +import kevinv1_1 from './kevin-v1.1'; + +const mockAdapter = createAlovaMockAdapter([Augustv1_1, kevinv1_1], { + httpAdapter: GlobalFetch(), + delay: 1000 +}); +export const alovaInst = createAlova({ + baseURL: 'http://xxx', + requestAdapter: mockAdapter + // ... +}); +``` + +### 在生产环境中排除 mock 代码 + +mock 数据一般只作用于开发环境,在生产环境下将会切换到实际的接口中,因此这段 mock 代码在生产环境就变得没有作用,此时我们可以通过环境变量的判断来排除这块代码,你只需要这样做: + +```javascript +const globalFetch = GlobalFetch(); +const mockAdapter = createAlovaMockAdapter([mockGroup1, /** ... */], { + httpAdapter: globalFetch, + delay: 1000, +}); + +export const alovaInst = createAlova({ + baseURL: 'http://xxx', + + // highlight-start + // 通过环境变量控制生产环境下,不会将mock相关代码打包进去 + requestAdapter: process.env.NODE_ENV === 'development' ? mockAdapter : globalFetch, + // highlight-end + + statesHook: /** ... */ +}); +``` + +### 与 mockjs 一同使用 + +如果你不希望自己编写模拟数据,而是使用模拟数据库(例如 mockjs)一同使用,你可以这样做。 + +```javascript +import { defineMock } from '@alova/mock'; +import Mock from 'mockjs'; + +export default defineMock({ + '/api1': Mock.mock({ + 'id|1-10000': 100 + }) +}); +``` + +## 转换模拟数据 + +**@alova/mock** 默认将响应数据包装为 Response 实例,将响应头默认包装为 Headers 实例,这是针对`GlobalFetch`进行适配的,但如果使用其他的请求适配器,就需要将模拟数据转换为相应的格式。 + +### 转换响应数据 + +你可以在`onMockResponse`字段中拦截模拟响应数据并返回转换后的响应数据以及响应头。 + +> 你也可以在 onMockResponse 中抛出一个错误,表示请求失败。 + +```javascript +const mockAdapter = createAlovaMockAdapter( + [ + /* 模拟数据 */ + ], + { + // ... + // highlight-start + onMockResponse(response, request, currentMethod) { + // response为相应数据集合,其中包含status、statusText、responseHeaders、body + // request为请求数据,其中包含query、params、headers、data + // currentMethod为当前请求的method实例 + // ... + // 返回转换后的响应数据和响应头 + return { + response: /** 响应数据 */, + headers: /** 响应头 */ + }; + } + // highlight-end + } +); +``` + +### 转换错误对象 + +你可以在`onMockError`字段中拦截错误实例并返回转换后的错误信息。 + +```javascript +const mockAdapter = createAlovaMockAdapter( + [ + /* 模拟数据 */ + ], + { + // ... + // highlight-start + onMockError(error, currentMethod) { + // error为错误实例 + // currentMethod为当前请求的method实例 + // ... + // 返回转换后的错误信息集合 + } + // highlight-end + } +); +``` diff --git a/versioned_docs/version-2.x/tutorial/05-strategy/02-usePagination.md b/versioned_docs/version-2.x/tutorial/05-strategy/02-usePagination.md index 231485512..4246e09a9 100644 --- a/versioned_docs/version-2.x/tutorial/05-strategy/02-usePagination.md +++ b/versioned_docs/version-2.x/tutorial/05-strategy/02-usePagination.md @@ -1,727 +1,733 @@ ---- -title: Pagination request strategy -sidebar_position: 20 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -:::info stragety type - -use hook - -::: - -> Before using extension hooks, make sure you are familiar with basic usage of alova. - -A hook designed for paging scenarios, which can help you automatically manage paging data, preload data, reduce unnecessary data refresh, improve fluency by 300%, and reduce coding difficulty by 50%\*\*. You can use it in the two paging scenarios of pull-down loading and page number flipping. This hook provides a wealth of features to help your application create better performance and more convenient paging functions. - -## Example - -[page list](/tutorial/example/paginated-list) - -[Pull down to load more](/tutorial/example/load-more) - -## Features - -- ✨ Rich and comprehensive paging status; -- ✨ Rich and comprehensive pagination events; -- ✨ Change page, pageSize to automatically get specified paging data; -- ✨Data caching, no need to repeatedly request list data of the same parameters; -- ✨ Front and back pages are preloaded, no waiting for page turning; -- ✨Search condition monitoring automatically reacquires pages; -- ✨ Support adding, editing and deleting list data; -- ✨ Support refreshing the data of the specified page without reset; -- ✨ Request-level search anti-shake, no need to maintain by yourself; - -## Install - - - - -```bash -# npm -npm install @alova/scene-vue --save -#yarn -yarn add @alova/scene-vue - -``` - - - - -```bash -# npm -npm install @alova/scene-react --save -#yarn -yarn add @alova/scene-react - -``` - - - - - -```bash -# npm -npm install @alova/scene-svelte --save -#yarn -yarn add @alova/scene-svelte - -``` - - - - -## Usage - -### Display list data - - - - -```html - - - -``` - - - - -```jsx -import { queryStudents } from './api.js'; -import { usePagination } from '@alova/scene-react'; - -const App = () => { - const { - // loading state - loading, - - // list data - data, - - // is it the last page - // This parameter can be used to determine whether it needs to be loaded during pull-down loading - isLastPage, - - // The current page number, changing this page number will automatically trigger the request - page: [page, setPage], - - // Number of data items per page - pageSize: [page, setPageSize], - - // number of paging pages - pageCount, - - // total amount of data - total - } = usePagination( - // Method instance acquisition function, it will receive page and pageSize, and return a Method instance - (page, pageSize) => queryStudents(page, pageSize), - { - // Initial data before the request (data format returned by the interface) - initialData: { - total: 0, - data: [] - }, - initialPage: 1, // initial page number, default is 1 - initialPageSize: 10 // The initial number of data items per page, the default is 10 - } - ); - - // Turn to the previous page, the request will be sent automatically after the page value changes - const handlePrevPage = () => { - setPage(value => value - 1); - }; - - // Turn to the next page, the request will be sent automatically after the page value changes - const handleNextPage = () => { - setPage(value => value + 1); - }; - - // Change the number of pages, the request will be sent automatically after the pageSize value is changed - const handleSetPageSize = () => { - setPageSize(20); - }; - - return ( -
- {data.map(item => ( -
- {item.name} -
- ))} - - - - There are {pageCount} pages - A total of {total} pieces of data -
- ); -}; -``` - -
- - -```html - - -{#each $data as item} -
- {item.name} -
-{/each} - - - -There are {pageCount} pages -A total of {total} pieces of data -``` - -
-
- -### Specify pagination data - -The data structure returned by each paging data interface is different, so we need to tell `usePagination` the list data and the total number of items separately, so as to help us manage the paging data. - -Suppose the data format returned by your paging interface is as follows: - -```typescript -interface PaginationData { - totalNumber: number; - list: any[]; -} -``` - -At this point, you need to return the list data and the total number of items in the form of a function. - -```javascript -usePagination((page, pageSize) => queryStudents(page, pageSize), { - //... - // highlight-start - total: response => response.totalNumber, - data: response => response.list - // highlight-end -}); -``` - -If you don't specify the total and data callback functions, they will get data in the following ways by default. - -```javascript -const total = response => response.total; -const data = response => response.data; -``` - -:::warning Caution - -The data callback function must return a list of data, indicating the data set used in paging, and total is mainly used to calculate the current page number. If no number is returned in the total callback function, it will pass whether the number of requested lists is less than the pageSize value To determine whether the current page is the last page, which is generally used for pull-down loading. - -::: - -### Enable append mode - -By default, the original list data will be replaced when the page is turned, and the append mode will append the data of the next page to the bottom of the current list when the page is turned. A common usage scenario is to pull down to load more. - -```javascript -usePagination((page, pageSize) => queryStudents(page, pageSize), { - //... - // highlight-start - append: true - // highlight-end -}); -``` - -### Preload adjacent page data - -In order to provide a better experience for pagination, when the previous and next pages of the current page meet the conditions, it will be automatically preloaded, so that when the user turns the page, the data can be displayed directly without waiting. This is the default behavior. If you don't want to preload the data of adjacent pages, you can turn it off in the following way. - -```javascript -usePagination((page, pageSize) => queryStudents(page, pageSize), { - //... - // highlight-start - preloadPreviousPage: false, // turn off preloading previous page data - preloadNextPage: false // turn off preloading next page data - // highlight-end -}); -``` - -:::warning preloading trigger conditions - -When preloading switch is turned on, the next page will not be loaded blindly. The following two conditions need to be met: - -1. Preloading is based on cache of alova. The method instance used for pagination must enable chache. By default, get requests will have 5 minutes of memory cache. If it is a non-get request or the cache is turned off globally, you also need to set the cache in this Method. In the instance, set `localCache` separately to enable cache. -2. Based on the `total` and `pageSize` parameters, it is determined that there is still data on the next page. - -::: - -In addition to `onSuccess, onError, onComplete` request events, when preloading is triggered, you can also know the preloading status through `fetching`, and you can also listen to preloading request events through `onFetchSuccess, onFetchError, onFetchComplete`. - -```javascript -const { - // preload state - fetching, - - // preload success event binding function - onFetchSuccess, - - // preload error event binding function - onFetchError, - - // Preloading complete event binding function - onFetchComplete -} = usePagination((page, pageSize) => queryStudents(page, pageSize), { - //... -}); -``` - -### Listening filter conditions - -In many cases, the list needs to be filtered by conditions. At this time, the re-request can be triggered through the status monitoring of `usePagination`, which is the same as `useWatcher` provided by alova. - -For example, filter by student name, student grade. - - - - -```html - - - -``` - - - - -```jsx -import { queryStudents } from './api.js'; -import { usePagination } from '@alova/scene-react'; - -const App = () => { - // search condition status - const [studentName, setStudentName] = useState(''); - const [clsName, setClsName] = useState(''); - const { - //... - } = usePagination( - (page, pageSize) => queryStudents(page, pageSize, studentName, clsName), - { - //... - // highlight-start - watchingStates: [studentName, clsName] - // highlight-end - } - ); - - return ( - // highlight-start - setStudentName(target.value)} /> - - // highlight-end - //... - ); -}; -``` - - - - -```html - - - - - - - -``` - - - - -Same as `useWatcher`, you can also implement request debounce by specifying `debounce`, for details, please refer to [useWatcher's debounce parameter setting](/api/core-hooks#usewatcher). - -```javascript -usePagination((page, pageSize) => queryStudents(page, pageSize, studentName, clsName), { - //... - // highlight-start - debounce: 300 // Anti-shake parameters, in milliseconds, can also be set as an array to set the anti-shake time separately for watchingStates - // highlight-end -}); -``` - -It should be noted that `debounce` is achieved by request debounce in [**useWatcher**](/api/core-hooks#usewatcher). **At the end of the monitoring state, there are two hidden monitoring states of page and pageSize, which can also be set by debounce. ** - -For example, when `watchingStates` is set to `[studentName, clsName]`, `[studentName, clsName, page, pageSize]` will be monitored internally, so if you need to set anti-shake for page and pageSize, you can specify ` [0, 0, 500, 500]`. - -### Close initialization request - -By default, `usePagination` will initiate a request during initialization, but you can also use `immediate` to turn it off, and then pass the `send` function, or change `page` or `pageSize`, and `watchingStates`, etc. state to initiate the request. - -```javascript -usePagination((page, pageSize) => queryStudents(page, pageSize, studentName, clsName), { - //... - // highlight-start - immediate: false - // highlight-end -}); -``` - -## List action functions - -usePagination provides a fully functional list action function, which can achieve the same effect as the re-request list without re-requesting the list, which greatly improves the interactive experience of the page. The specific function description continues to look down! - -### Insert list item - -You can use it to insert list items to any position in the list, and it will remove the last item after insertion to ensure the same effect as re-requesting the current page data. - -```typescript -/** - * Insert data - * If no index is passed in, it will be inserted at the front by default - * If a list item is passed in, it will be inserted after the list item, and an error will be thrown if the list item is not in the list data - * @param item insert item - * @param indexOrItem insertion position (index) - */ -declare function insert(item: LD[number], indexOrItem?: number | LD[number]): void; -``` - -The following is an example of returning to the first page and then inserting list items in **non-append mode** (page number flipping scenario): - -```javascript -page.value = 1; -nextTick(() => { - insert(newItem, 0); -}); -``` - -The following is an example of scrolling to the top after inserting a list item in **append mode** (drop-down loading scene): - -```javascript -insert(newItem, 0); -nextTick(() => { - window.scrollTo(0, {}); -}); -``` - -You can also specify the second parameter of `insert` as a list item. When the same reference of this list item is found, the insert item will be inserted after this list item. - -```javascript -insert(newItem, afterItem); -``` - -:::warning Caution - -In order to make the data correct, the insert function call will clear all caches. - -::: - -### Remove list item - -In the case that the next page has a cache, it will use the cache of the next page to add to the end of the list item after removing an item, so as to ensure the same effect as re-requesting the data of the current page. In **append mode** and Behave the same in **non-append mode**. - -```typescript -/** - * Remove data - * If a list item is passed in, the list item will be removed, and an error will be thrown if the list item is not in the list data - * @param position index or list item to remove - */ -declare function remove(position: number | LD[number]): void; -``` - -You can also specify the second parameter of `remove` as a list item, and when the same reference of this list item is found, this list item will be removed. - -But in the following two cases, it will re-initiate the request to refresh the data of the corresponding page: - -1. The next page is not cached -2. The data that exceeds the next page cache list item is continuously called synchronously, and the cache data is not enough to supplement the current page list. - -:::warning Caution - -In order to make the data correct, the remove function call will clear all caches. - -::: - -### Update data items - -Use this function when you want to update list items. - -```typescript -/** - * Replace data - * If the position passed in is a list item, this list item will be replaced, if the list item is not in the list data, an error will be thrown - * @param item replacement item - * @param position replacement position (index) or list item - */ -declare function replace(item: LD extends any[] ? LD[number] : any, position: number | LD[number]): void; -``` - -You can also specify the second parameter of `replace` as a list item, which will be replaced when an identical reference to the list item is found. - -### Refresh the data of the specified page - -When you do not want to update the list items locally after the data operation, but re-request the data on the server side, you can use refresh to refresh the data on any page, without resetting the list data and letting the user start browsing from the first page again. - -```typescript -/** - * Refresh the specified page number data, this function will ignore the cache to force the send request - * If no page number is passed in, the current page will be refreshed - * If a list item is passed in, the page where the list item is located will be refreshed - * @param pageOrItemPage Refreshed page number or list item - */ -declare function refresh(pageOrItemPage?: number | LD[number]): void; -``` - -In append mode, you can specify the parameter of `refresh` as a list item. When the same reference of this list item is found, the data of the page number where this list item is located will be refreshed. - -### Manually update list data - -Use the `update` function to update responsive data, which is similar to [useRequest's update](/tutorial/combine-framework/use-request), the only difference is that when calling `update` to update `data`, the list data is updated, while non-response data. This is useful when manually clearing list data without reissuing the request. - -```typescript -// case list data -update({ - data: [] -}); -``` - -### Reset list - -It will clear all caches and reload the first page. - -```typescript -/** - * Reload the list from the first page and clear the cache - */ -declare function reload(): void; -``` - -## Limitation - -**Cache placeholder mode** are temporarily disabled. - -## API - -### Hook configuration - -Inherit all configurations of [**useWatcher**](/api/core-hooks#usewatcher). - -| Name | Description | Type | Default | Version | -| ------------------- | --------------------------------------------------------------------- | ------------------------- | -------------------------- | ------- | -| initialPage | initial page number | number | 1 | - | -| initialPageSize | Initial number of data items per page | number | 10 | - | -| watchingStates | state monitoring trigger request, implemented using useWatcher | any[] | [page, pageSize] | - | -| debounce | The debounce parameter of state monitoring, implemented by useWatcher | number \| number[] | - | - | -| append | Whether to enable append mode | boolean | false | - | -| data | Array data specifying pagination | (response: any) => any[] | response => response.data | - | -| total | specify the total amount of data | (response: any) => number | response => response.total | - | -| preloadPreviousPage | whether to preload previous page data | boolean | true | - | -| preloadNextPage | whether to preload next page data | boolean | true | - | - -### Responsive data - -Inherit all responsive data from [**useWatcher**](/api/core-hooks#usewatcher). - -| Name | Description | Type | Version | -| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ------- | -| page | Current page number, determined by initialPage | number | - | -| pageSize | The current number of pages, determined by initialPageSize | number | - | -| data | paging list array data, obtained from data configuration | any[] | - | -| total | The total amount of data, obtained from total configuration, can be empty | number | - | -| pageCount | The total number of pages, calculated from total and pageSize | number | - | -| isLastPage | Whether the current page is the last page, if pageCount has a value, it will be obtained by comparing pageCount and page, otherwise it will be obtained by whether the length of the list data is less than pagSize | number | - | -| fetching | whether data is being preloaded | boolean | - | - -### Action function - -Inherit all action functions of [**useWatcher**](/api/core-hooks#usewatcher). - -| name | description | function parameters | return value | version | -| ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | ------- | -| refresh | Refresh the data of the specified page number, this function will ignore the forced sending request of the cache, in the append mode, the list item can be passed in to indicate the page number where the list item is refreshed | pageOrItemPage: Refreshed page number or list item | - | - | -| insert | Insert a piece of data. If no index is passed in, it will be inserted at the front by default. If a list item is passed in, it will be inserted after the list item. If the list item is not in the list data, an error will be thrown | 1. item: insert item
2. indexOrItem: insert position (index) or list item, default is 0 | - | - | -| remove | Remove a piece of data. When a number is passed in, it means the removed index. When the position is passed in a list item, the list item will be removed. If the list item is not in the list data, an error will be thrown | position : remove position (index) or list item | - | - | -| replace | Replace a piece of data. When the second parameter is passed in a number, it means the replacement index. A negative number means counting from the end. When the position passed in is a list item, the list item will be replaced. If the list item is not in the list data An error will be thrown | 1. item: replacement item
2. position: replacement position (index) or list item, when a negative number is passed in, it means counting from the end | - | - | -| reload | Clear the data and re-request the first page of data | - | - | - | -| update | Update state data, same as alova's use hook, but update list data when updating data field | newFrontStates: new state data object | - | - | - -### Event - -Inherit all events from [**useWatcher**](/api/core-hooks#usewatcher). - -| Name | Description | Callback Parameters | Version | -| --------------- | ---------------------------------------------- | ------------------------------------ | ------- | -| onFetchSuccess | fetch success callback binding function | event: alova success event object | - | -| onFetchError | callback binding function for fetch failure | event: alova failure event object | - | -| onFetchComplete | callback binding function for fetch completion | event: alova completion event object | - | +--- +title: Pagination request strategy +sidebar_position: 20 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info stragety type + +use hook + +::: + +> Before using extension hooks, make sure you are familiar with basic usage of alova. + +A hook designed for paging scenarios, which can help you automatically manage paging data, preload data, reduce unnecessary data refresh, improve fluency by 300%, and reduce coding difficulty by 50%\*\*. You can use it in the two paging scenarios of pull-down loading and page number flipping. This hook provides a wealth of features to help your application create better performance and more convenient paging functions. + +## Example + +[page list](/tutorial/example/paginated-list) + +[Pull down to load more](/tutorial/example/load-more) + +## Features + +- Rich and comprehensive paging status; +- Rich and comprehensive pagination events; +- Change page, pageSize to automatically get specified paging data; +- Data caching, no need to repeatedly request list data of the same parameters; +- Front and back pages are preloaded, no waiting for page turning; +- Search condition monitoring automatically reacquires pages; +- Support adding, editing and deleting list data; +- Support refreshing the data of the specified page without reset; +- Request-level search anti-shake, no need to maintain by yourself; + +## Install + + + + +```bash +# npm +npm install @alova/scene-vue --save +#yarn +yarn add @alova/scene-vue + +``` + + + + +```bash +# npm +npm install @alova/scene-react --save +#yarn +yarn add @alova/scene-react + +``` + + + + + +```bash +# npm +npm install @alova/scene-svelte --save +#yarn +yarn add @alova/scene-svelte + +``` + + + + +## Usage + +### Display list data + + + + +```html + + + +``` + + + + +```jsx +import { queryStudents } from './api.js'; +import { usePagination } from '@alova/scene-react'; + +const App = () => { + const { + // loading state + loading, + + // list data + data, + + // is it the last page + // This parameter can be used to determine whether it needs to be loaded during pull-down loading + isLastPage, + + // The current page number, changing this page number will automatically trigger the request + page: [page, setPage], + + // Number of data items per page + pageSize: [page, setPageSize], + + // number of paging pages + pageCount, + + // total amount of data + total + } = usePagination( + // Method instance acquisition function, it will receive page and pageSize, and return a Method instance + (page, pageSize) => queryStudents(page, pageSize), + { + // Initial data before the request (data format returned by the interface) + initialData: { + total: 0, + data: [] + }, + initialPage: 1, // initial page number, default is 1 + initialPageSize: 10 // The initial number of data items per page, the default is 10 + } + ); + + // Turn to the previous page, the request will be sent automatically after the page value changes + const handlePrevPage = () => { + setPage(value => value - 1); + }; + + // Turn to the next page, the request will be sent automatically after the page value changes + const handleNextPage = () => { + setPage(value => value + 1); + }; + + // Change the number of pages, the request will be sent automatically after the pageSize value is changed + const handleSetPageSize = () => { + setPageSize(20); + }; + + return ( +
+ {data.map(item => ( +
+ {item.name} +
+ ))} + + + + There are {pageCount} pages + A total of {total} pieces of data +
+ ); +}; +``` + +
+ + +```html + + +{#each $data as item} +
+ {item.name} +
+{/each} + + + +There are {pageCount} pages +A total of {total} pieces of data +``` + +
+
+ +### Specify pagination data + +The data structure returned by each paging data interface is different, so we need to tell `usePagination` the list data and the total number of items separately, so as to help us manage the paging data. + +Suppose the data format returned by your paging interface is as follows: + +```typescript +interface PaginationData { + totalNumber: number; + list: any[]; +} +``` + +At this point, you need to return the list data and the total number of items in the form of a function. + +```javascript +usePagination((page, pageSize) => queryStudents(page, pageSize), { + //... + // highlight-start + total: response => response.totalNumber, + data: response => response.list + // highlight-end +}); +``` + +If you don't specify the total and data callback functions, they will get data in the following ways by default. + +```javascript +const total = response => response.total; +const data = response => response.data; +``` + +:::warning Caution + +The data callback function must return a list of data, indicating the data set used in paging, and total is mainly used to calculate the current page number. If no number is returned in the total callback function, it will pass whether the number of requested lists is less than the pageSize value To determine whether the current page is the last page, which is generally used for pull-down loading. + +::: + +### Enable append mode + +By default, the original list data will be replaced when the page is turned, and the append mode will append the data of the next page to the bottom of the current list when the page is turned. A common usage scenario is to pull down to load more. + +```javascript +usePagination((page, pageSize) => queryStudents(page, pageSize), { + //... + // highlight-start + append: true + // highlight-end +}); +``` + +### Preload adjacent page data + +In order to provide a better experience for pagination, when the previous and next pages of the current page meet the conditions, it will be automatically preloaded, so that when the user turns the page, the data can be displayed directly without waiting. This is the default behavior. If you don't want to preload the data of adjacent pages, you can turn it off in the following way. + +```javascript +usePagination((page, pageSize) => queryStudents(page, pageSize), { + //... + // highlight-start + preloadPreviousPage: false, // turn off preloading previous page data + preloadNextPage: false // turn off preloading next page data + // highlight-end +}); +``` + +:::warning preloading trigger conditions + +When preloading switch is turned on, the next page will not be loaded blindly. The following two conditions need to be met: + +1. Preloading is based on cache of alova. The method instance used for pagination must enable chache. By default, get requests will have 5 minutes of memory cache. If it is a non-get request or the cache is turned off globally, you also need to set the cache in this Method. In the instance, set `localCache` separately to enable cache. +2. Based on the `total` and `pageSize` parameters, it is determined that there is still data on the next page. + +::: + +In addition to `onSuccess, onError, onComplete` request events, when preloading is triggered, you can also know the preloading status through `fetching`, and you can also listen to preloading request events through `onFetchSuccess, onFetchError, onFetchComplete`. + +```javascript +const { + // preload state + fetching, + + // preload success event binding function + onFetchSuccess, + + // preload error event binding function + onFetchError, + + // Preloading complete event binding function + onFetchComplete +} = usePagination((page, pageSize) => queryStudents(page, pageSize), { + //... +}); +``` + +### Listening filter conditions + +In many cases, the list needs to be filtered by conditions. At this time, the re-request can be triggered through the status monitoring of `usePagination`, which is the same as `useWatcher` provided by alova. + +For example, filter by student name, student grade. + + + + +```html + + + +``` + + + + +```jsx +import { queryStudents } from './api.js'; +import { usePagination } from '@alova/scene-react'; + +const App = () => { + // search condition status + const [studentName, setStudentName] = useState(''); + const [clsName, setClsName] = useState(''); + const { + //... + } = usePagination( + (page, pageSize) => queryStudents(page, pageSize, studentName, clsName), + { + //... + // highlight-start + watchingStates: [studentName, clsName] + // highlight-end + } + ); + + return ( + // highlight-start + setStudentName(target.value)} /> + + // highlight-end + //... + ); +}; +``` + + + + +```html + + + + + + + +``` + + + + +Same as `useWatcher`, you can also implement request debounce by specifying `debounce`, for details, please refer to [useWatcher's debounce parameter setting](/api/core-hooks#usewatcher). + +```javascript +usePagination((page, pageSize) => queryStudents(page, pageSize, studentName, clsName), { + //... + // highlight-start + debounce: 300 // Anti-shake parameters, in milliseconds, can also be set as an array to set the anti-shake time separately for watchingStates + // highlight-end +}); +``` + +It should be noted that `debounce` is achieved by request debounce in [**useWatcher**](/api/core-hooks#usewatcher). **At the end of the monitoring state, there are two hidden monitoring states of page and pageSize, which can also be set by debounce. ** + +For example, when `watchingStates` is set to `[studentName, clsName]`, `[studentName, clsName, page, pageSize]` will be monitored internally, so if you need to set anti-shake for page and pageSize, you can specify ` [0, 0, 500, 500]`. + +### Close initialization request + +By default, `usePagination` will initiate a request during initialization, but you can also use `immediate` to turn it off, and then pass the `send` function, or change `page` or `pageSize`, and `watchingStates`, etc. state to initiate the request. + +```javascript +usePagination((page, pageSize) => queryStudents(page, pageSize, studentName, clsName), { + //... + // highlight-start + immediate: false + // highlight-end +}); +``` + +## List action functions + +usePagination provides a fully functional list action function, which can achieve the same effect as the re-request list without re-requesting the list, which greatly improves the interactive experience of the page. The specific function description continues to look down! + +### Insert list item + +You can use it to insert list items to any position in the list, and it will remove the last item after insertion to ensure the same effect as re-requesting the current page data. + +```typescript +/** + * Insert data + * If no index is passed in, it will be inserted at the front by default + * If a list item is passed in, it will be inserted after the list item, and an error will be thrown if the list item is not in the list data + * @param item insert item + * @param indexOrItem insertion position (index) + */ +declare function insert(item: LD[number], indexOrItem?: number | LD[number]): void; +``` + +The following is an example of returning to the first page and then inserting list items in **non-append mode** (page number flipping scenario): + +```javascript +page.value = 1; +nextTick(() => { + insert(newItem, 0); +}); +``` + +The following is an example of scrolling to the top after inserting a list item in **append mode** (drop-down loading scene): + +```javascript +insert(newItem, 0); +nextTick(() => { + window.scrollTo(0, {}); +}); +``` + +You can also specify the second parameter of `insert` as a list item. When the same reference of this list item is found, the insert item will be inserted after this list item. + +```javascript +insert(newItem, afterItem); +``` + +:::warning Caution + +In order to make the data correct, the insert function call will clear all caches. + +::: + +### Remove list item + +In the case that the next page has a cache, it will use the cache of the next page to add to the end of the list item after removing an item, so as to ensure the same effect as re-requesting the data of the current page. In **append mode** and Behave the same in **non-append mode**. + +```typescript +/** + * Remove data + * If a list item is passed in, the list item will be removed, and an error will be thrown if the list item is not in the list data + * @param position index or list item to remove + */ +declare function remove(position: number | LD[number]): void; +``` + +You can also specify the second parameter of `remove` as a list item, and when the same reference of this list item is found, this list item will be removed. + +But in the following two cases, it will re-initiate the request to refresh the data of the corresponding page: + +1. The next page is not cached +2. The data that exceeds the next page cache list item is continuously called synchronously, and the cache data is not enough to supplement the current page list. + +:::warning Caution + +In order to make the data correct, the remove function call will clear all caches. + +::: + +### Update data items + +Use this function when you want to update list items. + +```typescript +/** + * Replace data + * If the position passed in is a list item, this list item will be replaced, if the list item is not in the list data, an error will be thrown + * @param item replacement item + * @param position replacement position (index) or list item + */ +declare function replace( + item: LD extends any[] ? LD[number] : any, + position: number | LD[number] +): void; +``` + +You can also specify the second parameter of `replace` as a list item, which will be replaced when an identical reference to the list item is found. + +### Refresh the data of the specified page + +When you do not want to update the list items locally after the data operation, but re-request the data on the server side, you can use refresh to refresh the data on any page, without resetting the list data and letting the user start browsing from the first page again. + +```typescript +/** + * Refresh the specified page number data, this function will ignore the cache to force the send request + * If no page number is passed in, the current page will be refreshed + * If a list item is passed in, the page where the list item is located will be refreshed + * @param pageOrItemPage Refreshed page number or list item + */ +declare function refresh(pageOrItemPage?: number | LD[number]): void; +``` + +In append mode, you can specify the parameter of `refresh` as a list item. When the same reference of this list item is found, the data of the page number where this list item is located will be refreshed. + +### Manually update list data + +Use the `update` function to update responsive data, which is similar to [useRequest's update](/tutorial/combine-framework/use-request), the only difference is that when calling `update` to update `data`, the list data is updated, while non-response data. This is useful when manually clearing list data without reissuing the request. + +```typescript +// case list data +update({ + data: [] +}); +``` + +### Reset list + +It will clear all caches and reload the first page. + +```typescript +/** + * Reload the list from the first page and clear the cache + */ +declare function reload(): void; +``` + +## Limitation + +**Cache placeholder mode** are temporarily disabled. + +## API + +### Hook configuration + +Inherit all configurations of [**useWatcher**](/api/core-hooks#usewatcher). + +| Name | Description | Type | Default | Version | +| ------------------- | --------------------------------------------------------------------- | ------------------------- | -------------------------- | ------- | +| initialPage | initial page number | number | 1 | - | +| initialPageSize | Initial number of data items per page | number | 10 | - | +| watchingStates | state monitoring trigger request, implemented using useWatcher | any[] | [page, pageSize] | - | +| debounce | The debounce parameter of state monitoring, implemented by useWatcher | number \| number[] | - | - | +| append | Whether to enable append mode | boolean | false | - | +| data | Array data specifying pagination | (response: any) => any[] | response => response.data | - | +| total | specify the total amount of data | (response: any) => number | response => response.total | - | +| preloadPreviousPage | whether to preload previous page data | boolean | true | - | +| preloadNextPage | whether to preload next page data | boolean | true | - | + +### Responsive data + +Inherit all responsive data from [**useWatcher**](/api/core-hooks#usewatcher). + +| Name | Description | Type | Version | +| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ------- | +| page | Current page number, determined by initialPage | number | - | +| pageSize | The current number of pages, determined by initialPageSize | number | - | +| data | paging list array data, obtained from data configuration | any[] | - | +| total | The total amount of data, obtained from total configuration, can be empty | number | - | +| pageCount | The total number of pages, calculated from total and pageSize | number | - | +| isLastPage | Whether the current page is the last page, if pageCount has a value, it will be obtained by comparing pageCount and page, otherwise it will be obtained by whether the length of the list data is less than pagSize | number | - | +| fetching | whether data is being preloaded | boolean | - | + +### Action function + +Inherit all action functions of [**useWatcher**](/api/core-hooks#usewatcher). + +| name | description | function parameters | return value | version | +| ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | ------- | +| refresh | Refresh the data of the specified page number, this function will ignore the forced sending request of the cache, in the append mode, the list item can be passed in to indicate the page number where the list item is refreshed | pageOrItemPage: Refreshed page number or list item | - | - | +| insert | Insert a piece of data. If no index is passed in, it will be inserted at the front by default. If a list item is passed in, it will be inserted after the list item. If the list item is not in the list data, an error will be thrown | 1. item: insert item
2. indexOrItem: insert position (index) or list item, default is 0 | - | - | +| remove | Remove a piece of data. When a number is passed in, it means the removed index. When the position is passed in a list item, the list item will be removed. If the list item is not in the list data, an error will be thrown | position : remove position (index) or list item | - | - | +| replace | Replace a piece of data. When the second parameter is passed in a number, it means the replacement index. A negative number means counting from the end. When the position passed in is a list item, the list item will be replaced. If the list item is not in the list data An error will be thrown | 1. item: replacement item
2. position: replacement position (index) or list item, when a negative number is passed in, it means counting from the end | - | - | +| reload | Clear the data and re-request the first page of data | - | - | - | +| update | Update state data, same as alova's use hook, but update list data when updating data field | newFrontStates: new state data object | - | - | + +### Event + +Inherit all events from [**useWatcher**](/api/core-hooks#usewatcher). + +| Name | Description | Callback Parameters | Version | +| --------------- | ---------------------------------------------- | ------------------------------------ | ------- | +| onFetchSuccess | fetch success callback binding function | event: alova success event object | - | +| onFetchError | callback binding function for fetch failure | event: alova failure event object | - | +| onFetchComplete | callback binding function for fetch completion | event: alova completion event object | - | diff --git a/versioned_docs/version-2.x/tutorial/05-strategy/03-useForm.md b/versioned_docs/version-2.x/tutorial/05-strategy/03-useForm.md index ccf08e333..07717e3a2 100644 --- a/versioned_docs/version-2.x/tutorial/05-strategy/03-useForm.md +++ b/versioned_docs/version-2.x/tutorial/05-strategy/03-useForm.md @@ -1,525 +1,525 @@ ---- -title: Form submit strategy -sidebar_position: 30 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -:::info strategy type - -use hook - -::: - -> Before using extension hooks, make sure you are familiar with the basic usage of alova. - -A hook designed for form submission. Through this hook, you can easily implement form drafts and multi-page (multi-step) forms. In addition, it also provides common functions such as form reset. - -## Example - -[Form Submission Demo](/tutorial/example/form-hook) - -## Features - -- ✨ draft form; -- ✨ Multi-page (multi-step) forms; -- ✨ Form submission automatically resets data; -- ✨Reset form data manually; - -## Install - - - - -```bash -# npm -npm install @alova/scene-vue --save -#yarn -yarn add @alova/scene-vue - -``` - - - - -```bash -# npm -npm install @alova/scene-react --save -#yarn -yarn add @alova/scene-react - -``` - - - - - -```bash -# npm -npm install @alova/scene-svelte --save -#yarn -yarn add @alova/scene-svelte - -``` - - - - -## Usage - -### Basic usage - -Demonstrates basic use of form hooks. - - - - -```html - - - -``` - - - - -```jsx -import { formSubmit } from './api.js'; -import { useForm } from '@alova/scene-react'; - -const App = () => { - const { - // submit status - loading: submitting, - - // Responsive form data, the content is determined by initialForm - form, - - // submit data function - send: submit, - - // update form item - updateForm, - - // Submit successful callback binding - onSuccess, - - // Submit failure callback binding - onError, - - // Submit completed callback binding - onComplete - } = useForm( - formData => { - // Form data can be converted and submitted here - return formSubmit(formData); - }, - { - // Initialize form data - initialForm: { - name: '', - cls: '1' - } - } - ); - - // submit form data - const handleSubmit = () => { - // Validate form data... - submit(); - }; - - return ( -
- updateForm({ name: target.value })} - /> - - -
- ); -}; -``` - -
- - -```html - - - - - -``` - - -
- -`useForm` will not request by default, and the request will be sent after calling `send`. At the same time, the callback function of `useForm` will pass in the latest form data. If you need to convert the data before submitting, you can convert it here, or Can be converted in the `formSubmit` function. - -:::warning Caution - -1. `initialForm` is to set the initial form data, `initialData` is to set the initial response data, pay attention to the distinction; -2. `updateForm` is to update the form data, and `update` is to update the response data, pay attention to the distinction; - -::: - -The above example only shows a simple form submission function, there is no difference between ordinary form submissions, but `useForm` also implements more practical functions, let us continue to look down. - -### Submit auto reset form - -Many times, we need to reset the form data after the form is submitted. We always need to manually reassign values one by one when implementing it ourselves, and `useForm` can help us do it automatically. - -```javascript -useForm(submitData, { - //... - // highlight-start - // Set this parameter to true to automatically reset the form data after submission - resetAfterSubmitting: true - // highlight-end -}); -``` - -If you need to manually reset the form data, you can also do it by calling the `reset` function. - -```javascript -const { - // highlight-start - // form reset function - reset - // highlight-end -} = useForm(submitData, { - //... -}); - -// highlight-start -const handleReset = () => { - reset(); -}; -// highlight-end -``` - -### Update form data - -When editing a form, we need to display the data of the original form. At this time, we can use `updateForm` to asynchronously update the form data. - -```javascript -const { - // ... - updateForm -} = useForm(submitData, { - // ... - { - //Initialize form data - initialForm: { - name: '', - cls: '1' - } - } -}); - -// Request form data and update it to the form -const { onSuccess } = useRequest(getData); -onSuccess(({ data }) => { - updateForm({ - name: data.name, - cls: data.cls - }); -}); -``` - -### Form draft - -`useForm` also provides a form draft function, even if the page is refreshed before the data is reset, the form data can be restored. The principle is to use the storage adapter on the alova instance to persist the form data. You only need to set `store` to true when using it. - -```javascript -useForm(submitData, { - //... - // highlight-start - // Turn on persistence to save data. After setting to true, uncommitted data will be persisted in real time - store: true - // highlight-end -}); -``` - -Before the data is persisted, `JSON.stringify` will be called to convert it into a JSON string. By default, the form data will be serialized when it is persisted. `useForm` has built-in `Date` and `RegExp` instances , which will be useful when using timepickers. - -In the form data only involves `Date` and `RegEYou don’t need to do more for xp` instances, but if there are other non-JSON data, such as `moment` instances, we need to customize the serializer, but don’t worry, the custom serializer is very simple, the following will show the settings A `moment` serializer. - -```javascript -import moment from 'moment'; -const momentSerializer = { - // forward is called when serializing - // Need to judge whether it is a moment instance, if it is not the target value, return undefined, indicating that it will not be processed - forward: data => moment.isMoment(data) ? data.valueOf() : undefined, - - // backward is called during deserialization, data is the value returned in forward - backward: timestamp => moment(timestamp); -}; - -useForm( - submitData, - { - store: { - enable: true, - serializers: { - moment: momentSerializer - } - } - } -); -``` - -### Multi-page/multi-step forms - -Many times we encounter situations where form items are divided into multiple pages, or filled in multiple steps, and submitted in a unified manner at the end, such as multi-step user registration, questionnaire filling, etc., and forms with multiple steps may have interdependence Relationship, if realized by itself will bring some trouble. And `useForm` realizes form data sharing, you can get the same form data in different pages or components, which solves the problem of multi-step form data dependence, and does not need to summarize form data when submitting, and can submit directly. - -When using, you need to set the id through `useForm`, and you can share the same form data between different pages with the same id. For example, we have a form that needs to go through 3 steps to fill out the form, and they will go through component A, component B, and component C respectively. - -``` -Component A -> Component B -> Component C -``` - -At this point, we can initialize the form data inside component A: - -```javascript title=Component A -const returnStates = useForm(submitData, { - initialForm: { - step1Input: '', - step2Input: '', - step3Input: '' - }, - // highlight-start - id: 'testForm' - // highlight-end -}); -const { form, send } = returnStates; -``` - -In component B and component C, you can directly pass in the id in the first parameter to get the shared data in component A. - -```javascript title=Component B, Component C -const returnStates = useForm('testForm'); -const { form, send } = returnStates; -``` - -The `returnStates` returned by id in components B and C are the same reference as the `returnStates` in component A. You can use the same `form`, or you can call `send` in any component to submit the form data uniformly. - -**additional** - -When obtaining shared data by directly specifying the id in components B and C, the id must first initialize the form data, just like in component A, otherwise `the form data of id {1} is not initial` will be thrown mistake. If your multi-step form is not in a certain order, but in random order according to certain conditions, for example: - -```bash -# possible order 1 -Component B -> Component A -> Component C - -# possible order 2 -Component A -> Component C -> Component B - -# possible order 3 -Component C -> Component A -> Component B - -#... -``` - -In this case, you can use `useForm` in component B, C like component A. - -```javascript title=Component B, Component C -const returnStates = useForm(submitData, { - initialForm: { - step1Input: '', - step2Input: '', - step3Input: '' - }, - id: 'testForm' -}); -``` - -In this way, no matter which component is rendered first, the form with the id of testForm can be initialized, and the subsequent components will first use the initialized form data when encountering the id of testForm, and will not initialize again. This way you can initialize form data inside any component. - -> More detailed multi-step forms can also be experienced and viewed in [Form Submission Demo](/tutorial/example/form-hook). - -### Conditional filter - -`useForm` can also be used in the filtering form used in data filtering scenarios, for example, if you want to search city information by city name, you can set `immediate=true`, it will start querying data at initialization, and then In the operation, call `send` to repeatedly query the data. - -```javascript -const { send: searchData } = useForm(queryCity, { - initialForm: { - cityName: '' - }, - immediate: true -}); -``` - -:::warning Conditional Restrictions - -In conditional filtering scenarios, `useForm` is more suitable for non-paginated list conditional queries. If you need to perform conditional queries in paginated lists, it is recommended to use [Pagination Request Strategy (usePagination)](/tutorial/strategy/usePagination). - -::: - -## API - -### Hook configuration - -Inherit all configurations from [**useRequest**](/api/core-hooks#userequest). - -| Name | Description | Type | Default | Version | -| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- | ------- | ------- | -| initialForm | initial form data | any | - | - | -| id | form id, the data data of the same id is the same reference, which can be used to share the same form data in multi-page forms. Single page form does not need to specify id | string \| number | - | - | -| store | Whether to save data persistently, after setting to true, uncommitted data will be persisted in real time | boolean \| [StoreDetailConfig](#storedetailconfig) \| false | - | -| resetAfterSubmiting | reset data after submission | boolean | false | - | - -### Responsive data - -Inherit all responsive data from [**useRequest**](/api/core-hooks#userequest). - -| Name | Description | Type | Version | -| ---- | ----------------------------------- | ---- | ------- | -| form | form data,determined by initialForm | any | - | - -#### StoreDetailConfig - -| Name | Description | Type | Default | Version | -| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | -------- | ------- | -| enable | Whether to enable persistent data | boolean | required | - | -| serializers | A collection of custom serializers, built-in serializers:
1. The date serializer is used to convert dates
2. The regexp serializer is used to convert regular expressions
Yes Override the built-in serializer by setting the serializer with the same name | Record\ | - | - | - -#### DataSerializer - -| Name | Description | Type | Default | Versionthis | -| -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------- | -------- | ----------- | -| forward | Serialization function, when serializing in forward, it needs to judge whether it is the specified data, and return the converted data, otherwise return undefined or not return | (data: any) => any \| undefined \| void | required | - | -| backward | deserialization function, deserialization data directly | (data: any) => any \| undefined \| void | required | - | - -### Action function - -Inherit all action functions of [**useRequest**](/api/core-hooks#userequest). - -| name | description | function parameters | return value | version | -| ---------- | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------ | ------------ | ------- | -| updateForm | Update one or more form data | newForm: Partial\ \| (oldForm: F) => F)
F is `initialForm` type | - | - | -| reset | Reset to initialized data, if there is persistent data, it will also be cleared | - | - | - | - -### Event - -Inherit all events from [**useRequest**](/api/core-hooks#userequest). - -| Name | Description | Callback Parameters | Version | -| --------- | ----------------------------------------------- | ------------------- | ------- | -| onRestore | Triggered after the persistent data is restored | - | - | +--- +title: Form submit strategy +sidebar_position: 30 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info strategy type + +use hook + +::: + +> Before using extension hooks, make sure you are familiar with the basic usage of alova. + +A hook designed for form submission. Through this hook, you can easily implement form drafts and multi-page (multi-step) forms. In addition, it also provides common functions such as form reset. + +## Example + +[Form Submission Demo](/tutorial/example/form-hook) + +## Features + +- draft form; +- Multi-page (multi-step) forms; +- Form submission automatically resets data; +- Reset form data manually; + +## Install + + + + +```bash +# npm +npm install @alova/scene-vue --save +#yarn +yarn add @alova/scene-vue + +``` + + + + +```bash +# npm +npm install @alova/scene-react --save +#yarn +yarn add @alova/scene-react + +``` + + + + + +```bash +# npm +npm install @alova/scene-svelte --save +#yarn +yarn add @alova/scene-svelte + +``` + + + + +## Usage + +### Basic usage + +Demonstrates basic use of form hooks. + + + + +```html + + + +``` + + + + +```jsx +import { formSubmit } from './api.js'; +import { useForm } from '@alova/scene-react'; + +const App = () => { + const { + // submit status + loading: submitting, + + // Responsive form data, the content is determined by initialForm + form, + + // submit data function + send: submit, + + // update form item + updateForm, + + // Submit successful callback binding + onSuccess, + + // Submit failure callback binding + onError, + + // Submit completed callback binding + onComplete + } = useForm( + formData => { + // Form data can be converted and submitted here + return formSubmit(formData); + }, + { + // Initialize form data + initialForm: { + name: '', + cls: '1' + } + } + ); + + // submit form data + const handleSubmit = () => { + // Validate form data... + submit(); + }; + + return ( +
+ updateForm({ name: target.value })} + /> + + +
+ ); +}; +``` + +
+ + +```html + + + + + +``` + + +
+ +`useForm` will not request by default, and the request will be sent after calling `send`. At the same time, the callback function of `useForm` will pass in the latest form data. If you need to convert the data before submitting, you can convert it here, or Can be converted in the `formSubmit` function. + +:::warning Caution + +1. `initialForm` is to set the initial form data, `initialData` is to set the initial response data, pay attention to the distinction; +2. `updateForm` is to update the form data, and `update` is to update the response data, pay attention to the distinction; + +::: + +The above example only shows a simple form submission function, there is no difference between ordinary form submissions, but `useForm` also implements more practical functions, let us continue to look down. + +### Submit auto reset form + +Many times, we need to reset the form data after the form is submitted. We always need to manually reassign values one by one when implementing it ourselves, and `useForm` can help us do it automatically. + +```javascript +useForm(submitData, { + //... + // highlight-start + // Set this parameter to true to automatically reset the form data after submission + resetAfterSubmitting: true + // highlight-end +}); +``` + +If you need to manually reset the form data, you can also do it by calling the `reset` function. + +```javascript +const { + // highlight-start + // form reset function + reset + // highlight-end +} = useForm(submitData, { + //... +}); + +// highlight-start +const handleReset = () => { + reset(); +}; +// highlight-end +``` + +### Update form data + +When editing a form, we need to display the data of the original form. At this time, we can use `updateForm` to asynchronously update the form data. + +```javascript +const { + // ... + updateForm +} = useForm(submitData, { + // ... + { + //Initialize form data + initialForm: { + name: '', + cls: '1' + } + } +}); + +// Request form data and update it to the form +const { onSuccess } = useRequest(getData); +onSuccess(({ data }) => { + updateForm({ + name: data.name, + cls: data.cls + }); +}); +``` + +### Form draft + +`useForm` also provides a form draft function, even if the page is refreshed before the data is reset, the form data can be restored. The principle is to use the storage adapter on the alova instance to persist the form data. You only need to set `store` to true when using it. + +```javascript +useForm(submitData, { + //... + // highlight-start + // Turn on persistence to save data. After setting to true, uncommitted data will be persisted in real time + store: true + // highlight-end +}); +``` + +Before the data is persisted, `JSON.stringify` will be called to convert it into a JSON string. By default, the form data will be serialized when it is persisted. `useForm` has built-in `Date` and `RegExp` instances , which will be useful when using timepickers. + +In the form data only involves `Date` and `RegEYou don’t need to do more for xp` instances, but if there are other non-JSON data, such as `moment` instances, we need to customize the serializer, but don’t worry, the custom serializer is very simple, the following will show the settings A `moment` serializer. + +```javascript +import moment from 'moment'; +const momentSerializer = { + // forward is called when serializing + // Need to judge whether it is a moment instance, if it is not the target value, return undefined, indicating that it will not be processed + forward: data => moment.isMoment(data) ? data.valueOf() : undefined, + + // backward is called during deserialization, data is the value returned in forward + backward: timestamp => moment(timestamp); +}; + +useForm( + submitData, + { + store: { + enable: true, + serializers: { + moment: momentSerializer + } + } + } +); +``` + +### Multi-page/multi-step forms + +Many times we encounter situations where form items are divided into multiple pages, or filled in multiple steps, and submitted in a unified manner at the end, such as multi-step user registration, questionnaire filling, etc., and forms with multiple steps may have interdependence Relationship, if realized by itself will bring some trouble. And `useForm` realizes form data sharing, you can get the same form data in different pages or components, which solves the problem of multi-step form data dependence, and does not need to summarize form data when submitting, and can submit directly. + +When using, you need to set the id through `useForm`, and you can share the same form data between different pages with the same id. For example, we have a form that needs to go through 3 steps to fill out the form, and they will go through component A, component B, and component C respectively. + +``` +Component A -> Component B -> Component C +``` + +At this point, we can initialize the form data inside component A: + +```javascript title=Component A +const returnStates = useForm(submitData, { + initialForm: { + step1Input: '', + step2Input: '', + step3Input: '' + }, + // highlight-start + id: 'testForm' + // highlight-end +}); +const { form, send } = returnStates; +``` + +In component B and component C, you can directly pass in the id in the first parameter to get the shared data in component A. + +```javascript title=Component B, Component C +const returnStates = useForm('testForm'); +const { form, send } = returnStates; +``` + +The `returnStates` returned by id in components B and C are the same reference as the `returnStates` in component A. You can use the same `form`, or you can call `send` in any component to submit the form data uniformly. + +**additional** + +When obtaining shared data by directly specifying the id in components B and C, the id must first initialize the form data, just like in component A, otherwise `the form data of id {1} is not initial` will be thrown mistake. If your multi-step form is not in a certain order, but in random order according to certain conditions, for example: + +```bash +# possible order 1 +Component B -> Component A -> Component C + +# possible order 2 +Component A -> Component C -> Component B + +# possible order 3 +Component C -> Component A -> Component B + +#... +``` + +In this case, you can use `useForm` in component B, C like component A. + +```javascript title=Component B, Component C +const returnStates = useForm(submitData, { + initialForm: { + step1Input: '', + step2Input: '', + step3Input: '' + }, + id: 'testForm' +}); +``` + +In this way, no matter which component is rendered first, the form with the id of testForm can be initialized, and the subsequent components will first use the initialized form data when encountering the id of testForm, and will not initialize again. This way you can initialize form data inside any component. + +> More detailed multi-step forms can also be experienced and viewed in [Form Submission Demo](/tutorial/example/form-hook). + +### Conditional filter + +`useForm` can also be used in the filtering form used in data filtering scenarios, for example, if you want to search city information by city name, you can set `immediate=true`, it will start querying data at initialization, and then In the operation, call `send` to repeatedly query the data. + +```javascript +const { send: searchData } = useForm(queryCity, { + initialForm: { + cityName: '' + }, + immediate: true +}); +``` + +:::warning Conditional Restrictions + +In conditional filtering scenarios, `useForm` is more suitable for non-paginated list conditional queries. If you need to perform conditional queries in paginated lists, it is recommended to use [Pagination Request Strategy (usePagination)](/tutorial/strategy/usePagination). + +::: + +## API + +### Hook configuration + +Inherit all configurations from [**useRequest**](/api/core-hooks#userequest). + +| Name | Description | Type | Default | Version | +| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- | ------- | ------- | +| initialForm | initial form data | any | - | - | +| id | form id, the data data of the same id is the same reference, which can be used to share the same form data in multi-page forms. Single page form does not need to specify id | string \| number | - | - | +| store | Whether to save data persistently, after setting to true, uncommitted data will be persisted in real time | boolean \| [StoreDetailConfig](#storedetailconfig) \| false | - | +| resetAfterSubmiting | reset data after submission | boolean | false | - | + +### Responsive data + +Inherit all responsive data from [**useRequest**](/api/core-hooks#userequest). + +| Name | Description | Type | Version | +| ---- | ----------------------------------- | ---- | ------- | +| form | form data,determined by initialForm | any | - | + +#### StoreDetailConfig + +| Name | Description | Type | Default | Version | +| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | -------- | ------- | +| enable | Whether to enable persistent data | boolean | required | - | +| serializers | A collection of custom serializers, built-in serializers:
1. The date serializer is used to convert dates
2. The regexp serializer is used to convert regular expressions
Yes Override the built-in serializer by setting the serializer with the same name | Record\ | - | - | + +#### DataSerializer + +| Name | Description | Type | Default | Versionthis | +| -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------- | -------- | ----------- | +| forward | Serialization function, when serializing in forward, it needs to judge whether it is the specified data, and return the converted data, otherwise return undefined or not return | (data: any) => any \| undefined \| void | required | - | +| backward | deserialization function, deserialization data directly | (data: any) => any \| undefined \| void | required | - | + +### Action function + +Inherit all action functions of [**useRequest**](/api/core-hooks#userequest). + +| name | description | function parameters | return value | version | +| ---------- | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------ | ------------ | ------- | +| updateForm | Update one or more form data | newForm: Partial\ \| (oldForm: F) => F)
F is `initialForm` type | - | - | +| reset | Reset to initialized data, if there is persistent data, it will also be cleared | - | - | - | + +### Event + +Inherit all events from [**useRequest**](/api/core-hooks#userequest). + +| Name | Description | Callback Parameters | Version | +| --------- | ----------------------------------------------- | ------------------- | ------- | +| onRestore | Triggered after the persistent data is restored | - | - | diff --git a/versioned_docs/version-2.x/tutorial/05-strategy/04-tokenAuthentication.md b/versioned_docs/version-2.x/tutorial/05-strategy/04-tokenAuthentication.md index 7d0e480bb..68ccecbe9 100644 --- a/versioned_docs/version-2.x/tutorial/05-strategy/04-tokenAuthentication.md +++ b/versioned_docs/version-2.x/tutorial/05-strategy/04-tokenAuthentication.md @@ -1,550 +1,583 @@ ---- -title: Token authentication interceptor -sidebar_position: 40 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -:::info - -Policy type: Interceptor - -Version requirements: v1.3.0+ - -::: - -> Before using extension hooks, make sure you are familiar with the basic use of alova. - -Token authentication interceptor provides unified management of token-based login, logout, token assignment, and token refresh, and supports silent token refresh. - -## Features - -- ✨ Unified maintenance of all codes for token identity authentication, including login, logout, token assignment, token refresh, etc.; -- ✨ Supports verification of token expiration on the client and server, and refreshes the token without any warning; -- ✨ Requests that rely on tokens automatically wait for the token refresh to complete before requesting; -- ✨ Set request ID with metadata; -- ✨ Automatically release visitor requests that do not rely on tokens; - -## Install - - - - -```bash -#npm -npm install @alova/scene-vue --save -# yarn -yarn add @alova/scene-vue - -``` - - - - -```bash -#npm -npm install @alova/scene-react --save -# yarn -yarn add @alova/scene-react - -``` - - - - - -```bash -#npm -npm install @alova/scene-svelte --save -# yarn -yarn add @alova/scene-svelte - -``` - - - - -:::info - -All the following interceptors are optional, just choose the ones you want to use. - -::: - -## Bind Token authentication interceptor - -Token identity authentication is completed through global interceptors, which provide `createClientTokenAuthentication` and `createServerTokenAuthentication` for client- and server-based identity authentication respectively. - -- Client-based identity authentication: means judging whether the token has expired from the client, such as the token expiration time obtained during login; -- Server-based identity authentication: It indicates whether the token has expired based on the status returned from the server. For example, when `status` is 401, it means it has expired; - -### Bind client-based authentication interceptor - -```javascript -import { createClientTokenAuthentication } from '@alova/scene-*'; -import { createAlova } from 'alova'; - -const { onAuthRequired, onResponseRefreshToken } = createClientTokenAuthentication({ - // ... -}); - -const alovaInstance = createAlova({ - // ... - beforeRequest: onAuthRequired(method => { - // ...interceptor before original request - }), - responded: onResponseRefreshToken((response, method) => { - //...original response success interceptor - return response.json(); - }) -}); -``` - -In `onResponseRefreshToken`, you can also bind response error and completion interceptors, which is the same as the original usage. - -```javascript -createAlova({ - // ... - // highlight-start - responded: onResponseRefreshToken({ - onSuccess: (response, method) => { - //...original response success interceptor - }, - onError: (error, method) => { - //...original response error interceptor - }, - onComplete: method => { - //...original response completion interceptor - } - }) - // highlight-end -}); -``` - -If you don't need to set an interceptor, you don't need to pass in the interceptor function. - -```javascript -createAlova({ - //... - // highlight-start - beforeRequest: onAuthRequired(), - responded: onResponseRefreshToken() - // highlight-end -}); -``` - -### Bind server-based authentication interceptor - -Same as client-based usage - -```javascript -import { createServerTokenAuthentication } from '@alova/scene-*'; -import { createAlova } from 'alova'; - -const { onAuthRequired, onResponseRefreshToken } = createServerTokenAuthentication({ - // ... -}); - -const alovaInstance = createAlova({ - // ... - beforeRequest: onAuthRequired(method => { - // ...interceptor before original request - }), - responded: onResponseRefreshToken((response, method) => { - //...original response success interceptor - return response.json(); - }) -}); -``` - -:::warning - -When you use the `GlobalFetch` adapter, you may encounter the problem `TypeError: Failed to execute 'json' on 'Response': body stream already read`. This is because the `body stream` of `Response` can only be accessed once, you can `response.clone().json()` to solve it. - -::: - -## Refresh Token silently on the client - -Just set `refreshToken` and specify whether the token expires, and call the function to refresh the token. When the token refresh is completed, all requests that rely on the token will wait for the token refresh to complete. - -```javascript -createClientTokenAuthentication({ - refreshToken: { - // Triggered before the request, the method parameter will be received and a boolean will be returned to indicate whether the token has expired. - isExpired: method => { - return tokenExpireTime < Date.now(); - }, - - // Triggered when the token expires, trigger the refresh token in this function - handler: async method => { - const { token, refresh_token } = await refreshToken(); - localStorage.setItem('token', token); - localStorage.setItem('refresh_token', refresh_token); - } - } -}); -``` - -In order for the `refreshToken` request to pass smoothly, the `authRole` needs to be identified as `refreshToken` through metadata. - -> For more information about metadata, go to [method metadata](/tutorial/getting-started/method-metadata). - -```javascript -export const refreshToken = () => { - const method = alovaInstance.Get('/refresh_token'); - method.meta = { - authRole: 'refreshToken' - }; - return method; -}; -``` - -## Refresh Token silently on the server side - -The same as refreshing the token silently on the client, just specify whether the token expires and call the function to refresh the token. When the token refresh is completed, all requests that rely on the token will wait for the token refresh to complete. - -### Processed in request success interceptor - -When using `GlobalFetch`, as long as the server returns the response data, the response success interceptor will be triggered. At this time, we need to handle the token refresh in the response success interceptor. - -```javascript -createServerTokenAuthentication({ - refreshTokenOnSuccess: { - // Triggered when responding, the response and method can be obtained, and a boolean is returned to indicate whether the token has expired. - // When the server returns 401, it means the token has expired. - isExpired: (response, method) => { - return response.status === 401; - }, - - // Triggered when the token expires, trigger the refresh token in this function - handler: async (response, method) => { - const { token, refresh_token } = await refreshToken(); - localStorage.setItem('token', token); - localStorage.setItem('refresh_token', refresh_token); - } - } -}); -``` - -### Handled in request error interceptor - -When using the `axios` interceptor, if the server returns a status code other than `200/300`, the response error interceptor will be triggered. At this time, we need to handle the token refresh in the response error interceptor. - -```javascript -createServerTokenAuthentication({ - refreshTokenOnError: { - // Triggered when responding, error and method can be obtained, and boolean is returned to indicate whether the token has expired. - // When the server returns 401, it means the token has expired. - isExpired: (error, method) => { - return error.response.status === 401; - }, - - // Triggered when the token expires, trigger the refresh token in this function - handler: async (error, method) => { - const { token, refresh_token } = await refreshToken(); - localStorage.setItem('token', token); - localStorage.setItem('refresh_token', refresh_token); - } - } -}); -``` - -In order for the `refreshToken` request to pass smoothly, the `authRole` needs to be identified as `refreshToken` through metadata. - -> For more information about metadata, please go to [method metadata](/tutorial/getting-started/method-metadata). - -```javascript -export const refreshToken = () => { - const method = alovaInstance.Get('/refresh_token'); - method.meta = { - authRole: 'refreshToken' - }; - return method; -}; -``` - -## Release visitor request - -Some interfaces do not need to rely on token authentication. We call them "guest requests". At this time, we can set their metadata to `authRole: null` to bypass the front-end interception and allow them to send requests and receive responses smoothly. - -```javascript -export const requestTokenNotRequired = () => { - const method = alovaInstance.Get('/token_not_required'); - method.meta = { - authRole: null - }; - return method; -}; -``` - -## Login interception - -In the identity authentication interceptor, you can also intercept login requests and save login information in the interceptor to achieve the purpose of unified maintenance of identity authentication codes. - -First identify the metadata of the login request as `authRole: 'login'`. - -```javascript -export const login = () => { - const method = alovaInstance.Get('/login'); - method.meta = { - authRole: 'login' - }; - return method; -}; -``` - -Then save the login information in the login interceptor. - -```javascript -createClientTokenAuthentication({ - login(response, method) { - localStorage.setItem('token', response.token); - localStorage.setItem('refresh_token', response.refresh_token); - } -}); -``` - -> The login interceptor usage of `createServerTokenAuthentication` is the same. - -## Assign token - -Usually, we will append token to the request information in `beforeRequest`. The `assignToken` callback function is provided in the Token authentication interceptor for assigning tokens. It will filter guest requests and login requests and trigger them before the requests. It can also achieve the purpose of unified maintenance of identity authentication codes. - -```javascript -createClientTokenAuthentication({ - assignToken: method => { - method.config.headers.Authorization = localStorage.getItem('token')}; - } -}); -``` - -> The usage of assignToken callback function of `createServerTokenAuthentication` is the same. - -## Logout interception - -When your logout also requires calling the interface, you can also intercept the logout request and clear the login information. - -First identify the logout request metadata as `authRole: 'logout'`. - -```javascript -export const logout = () => { - const method = alovaInstance.Get('/logout'); - method.meta = { - authRole: 'logout' - }; - return method; -}; -``` - -Then clear the login information in the logout interceptor. - -```javascript -createClientTokenAuthentication({ - logout(response, method) { - localStorage.removeItem('token'); - localStorage.removeItem('refresh_token'); - } -}); -``` - -> The login interceptor usage of `createServerTokenAuthentication` is the same. - -## Custom identification identity - -The above metadata identities are actually the default identities. If you need to customize the identity, you can set it as follows. - -### token refresh identity - -```javascript -createClientTokenAuthentication({ - refreshToken: { - // highlight-start - metaMatches: { - refreshToken: true - } - // highlight-end - // ... - } -}); -``` - -```javascript -createServerTokenAuthentication({ - refreshTokenOnSuccess: { - // highlight-start - metaMatches: { - refreshToken: true - } - // highlight-end - // ... - }, - refreshTokenOnError: { - // highlight-start - metaMatches: { - refreshToken: true - } - // highlight-end - // ... - } -}); -``` - -Then, requests with `refreshToken: true` in the metadata will be identified as `refreshToken`. - -```javascript -export const refreshToken = () => { - const method = alovaInstance.Get('/refresh_token'); - method.meta = { - refreshToken: true - }; - return method; -}; -``` - -### Visitor identify - -```javascript -createClientTokenAuthentication({ - visitorMeta: { - isVisitor: true - } -}); -``` - -Then, requests with `isVisitor: true` in the metadata will be identified as visitors. - -```javascript -export const requestTokenNotRequired = () => { - const method = alovaInstance.Get('/token_not_required'); - method.meta = { - isVisitor: true - }; - return method; -}; -``` - -### Login identity - -```javascript -createClientTokenAuthentication({ - login: { - // highlight-start - metaMatches: { - login: true - }, - // highlight-end - handler(response, method) { - //Login interceptor - } - } -}); -``` - -Then, requests with `login: true` in the metadata will be identified as `login` identity. - -```javascript -export const login = () => { - const method = alovaInstance.Get('/login'); - method.meta = { - login: true - }; - return method; -}; -``` - -### Log out identity - -```javascript -createClientTokenAuthentication({ - logout: { - // highlight-start - metaMatches: { - logout: true - }, - // highlight-end - handler(response, method) { - //Logout interceptor - } - } -}); -``` - -Then, requests with `logout: true` in the metadata will be identified as `logout`. - -```javascript -export const logout = () => { - const method = alovaInstance.Get('/logout'); - method.meta = { - logout: true - }; - return method; -}; -``` - -> The login interceptor usage of `createServerTokenAuthentication` is the same. - -## Typescript - -By default, `createClientServerTokenAuthentication` and `createServerTokenAuthentication` are adapted to the `GlobalFetch` request adapter, you can only specify the type of `StatesHook`, as follows: - -```typescript -// highlight-start -const { onAuthRequired, onResponseRefreshToken } = createClientTokenAuthentication({ - // highlight-end - //... -}); - -const alovaInstance = createAlova({ - // ... - beforeRequest: onAuthRequired(method => { - // The type of method is Method - }), - responded: onResponseRefreshToken((response, method) => { - //The response type is Response - return response.json(); - }) -}); -``` - -If you are not using the `GlobalFetch` request adapter, You also need to specify the type of request adapter, which is also simple. - -The following is an example of the axios request adapter. Specify the request adapter type in `createClientTokenAuthentication`. - -```typescript -import { axiosRequestAdapter } from '@alova/adapter-axios'; - -// highlight-start -const { onAuthRequired, onResponseRefreshToken } = createClientTokenAuthentication< - typeof ReactHook, - typeof axiosRequestAdapter ->({ - // highlight-end - //... -}); -const alovaInstance = createAlova({ - //... - // highlight-start - beforeRequest: onAuthRequired(method => { - // The type of method is Method - // highlight-end - }), - // highlight-start - responded: onResponseRefreshToken((response, method) => { - //The response type is AxiosResponse - // highlight-end - return response.data; - }) -}); -``` - -The server-based Token authentication interceptor is used in the same way. - -```typescript -import { axiosRequestAdapter } from '@alova/adapter-axios'; - -// highlight-start -createServerTokenAuthentication({ - // highlight-end - //... -}); -``` +--- +title: Token authentication interceptor +sidebar_position: 40 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info + +Policy type: Interceptor + +Version requirements: v1.3.0+ + +::: + +> Before using extension hooks, make sure you are familiar with the basic use of alova. + +Token authentication interceptor provides unified management of token-based login, logout, token assignment, and token refresh, and supports silent token refresh. + +## Features + +- Unified maintenance of all codes for token identity authentication, including login, logout, token assignment, token refresh, etc.; +- Supports verification of token expiration on the client and server, and refreshes the token without any warning; +- Requests that rely on tokens automatically wait for the token refresh to complete before requesting; +- Set request ID with metadata; +- Automatically release visitor requests that do not rely on tokens; + +## Install + + + + +```bash +#npm +npm install @alova/scene-vue --save +# yarn +yarn add @alova/scene-vue + +``` + + + + +```bash +#npm +npm install @alova/scene-react --save +# yarn +yarn add @alova/scene-react + +``` + + + + + +```bash +#npm +npm install @alova/scene-svelte --save +# yarn +yarn add @alova/scene-svelte + +``` + + + + +:::info + +All the following interceptors are optional, just choose the ones you want to use. + +::: + +## Bind Token authentication interceptor + +Token identity authentication is completed through global interceptors, which provide `createClientTokenAuthentication` and `createServerTokenAuthentication` for client- and server-based identity authentication respectively. + +- Client-based identity authentication: means judging whether the token has expired from the client, such as the token expiration time obtained during login; +- Server-based identity authentication: It indicates whether the token has expired based on the status returned from the server. For example, when `status` is 401, it means it has expired; + +### Bind client-based authentication interceptor + +```javascript +import { createClientTokenAuthentication } from '@alova/scene-*'; +import { createAlova } from 'alova'; + +const { onAuthRequired, onResponseRefreshToken } = createClientTokenAuthentication({ + // ... +}); + +const alovaInstance = createAlova({ + // ... + beforeRequest: onAuthRequired(method => { + // ...interceptor before original request + }), + responded: onResponseRefreshToken((response, method) => { + //...original response success interceptor + return response.json(); + }) +}); +``` + +In `onResponseRefreshToken`, you can also bind response error and completion interceptors, which is the same as the original usage. + +```javascript +createAlova({ + // ... + // highlight-start + responded: onResponseRefreshToken({ + onSuccess: (response, method) => { + //...original response success interceptor + }, + onError: (error, method) => { + //...original response error interceptor + }, + onComplete: method => { + //...original response completion interceptor + } + }) + // highlight-end +}); +``` + +If you don't need to set an interceptor, you don't need to pass in the interceptor function. + +```javascript +createAlova({ + //... + // highlight-start + beforeRequest: onAuthRequired(), + responded: onResponseRefreshToken() + // highlight-end +}); +``` + +### Bind server-based authentication interceptor + +Same as client-based usage + +```javascript +import { createServerTokenAuthentication } from '@alova/scene-*'; +import { createAlova } from 'alova'; + +const { onAuthRequired, onResponseRefreshToken } = createServerTokenAuthentication({ + // ... +}); + +const alovaInstance = createAlova({ + // ... + beforeRequest: onAuthRequired(method => { + // ...interceptor before original request + }), + responded: onResponseRefreshToken((response, method) => { + //...original response success interceptor + return response.json(); + }) +}); +``` + +:::warning + +When you use the `GlobalFetch` adapter, you may encounter the problem `TypeError: Failed to execute 'json' on 'Response': body stream already read`. This is because the `body stream` of `Response` can only be accessed once, you can `response.clone().json()` to solve it. + +::: + +## Refresh Token silently on the client + +Just set `refreshToken` and specify whether the token expires, and call the function to refresh the token. When the token refresh is completed, all requests that rely on the token will wait for the token refresh to complete. + +```javascript +createClientTokenAuthentication({ + refreshToken: { + // Triggered before the request, the method parameter will be received and a boolean will be returned to indicate whether the token has expired. + isExpired: method => { + return tokenExpireTime < Date.now(); + }, + + // Triggered when the token expires, trigger the refresh token in this function + handler: async method => { + try { + const { token, refresh_token } = await refreshToken(); + localStorage.setItem('token', token); + localStorage.setItem('refresh_token', refresh_token); + } catch (error) { + // redirect to login page once token refresh failed. + location.href = '/login'; + // and must throw error. + throw error; + } + } + } +}); +``` + +:::warning Attention + +1. In order for the `refreshToken` request to pass smoothly, the `authRole` needs to be marked as `refreshToken` through metadata. +2. If the token refresh fails, an error must be thrown to prevent the failed API from retrying and the waiting APIs from continuing the request. + +::: + +> For more information about metadata, go to [method metadata](/tutorial/getting-started/method-metadata). + +```javascript +export const refreshToken = () => { + const method = alovaInstance.Get('/refresh_token'); + method.meta = { + authRole: 'refreshToken' + }; + return method; +}; +``` + +## Refresh Token silently on the server side + +The same as refreshing the token silently on the client, just specify whether the token expires and call the function to refresh the token. When the token refresh is completed, all requests that rely on the token will wait for the token refresh to complete. + +### Processed in request success interceptor + +When using `GlobalFetch`, as long as the server returns the response data, the response success interceptor will be triggered. At this time, we need to handle the token refresh in the response success interceptor. + +```javascript +createServerTokenAuthentication({ + refreshTokenOnSuccess: { + // Triggered when responding, the response and method can be obtained, and a boolean is returned to indicate whether the token has expired. + // When the server returns 401, it means the token has expired. + isExpired: (response, method) => { + return response.status === 401; + }, + + // Triggered when the token expires, trigger the refresh token in this function + handler: async (response, method) => { + try { + const { token, refresh_token } = await refreshToken(); + localStorage.setItem('token', token); + localStorage.setItem('refresh_token', refresh_token); + } catch (error) { + // redirect to login page once token refresh failed. + location.href = '/login'; + // and must throw error. + throw error; + } + } + } +}); +``` + +### Handled in request error interceptor + +When using the `axios` interceptor, if the server returns a status code other than `200/300`, the response error interceptor will be triggered. At this time, we need to handle the token refresh in the response error interceptor. + +```javascript +createServerTokenAuthentication({ + refreshTokenOnError: { + // Triggered when responding, error and method can be obtained, and boolean is returned to indicate whether the token has expired. + // When the server returns 401, it means the token has expired. + isExpired: (error, method) => { + return error.response.status === 401; + }, + + // Triggered when the token expires, trigger the refresh token in this function + handler: async (error, method) => { + try { + const { token, refresh_token } = await refreshToken(); + localStorage.setItem('token', token); + localStorage.setItem('refresh_token', refresh_token); + } catch (error) { + // redirect to login page once token refresh failed. + location.href = '/login'; + // and must throw error. + throw error; + } + } + } +}); +``` + +:::warning Attention + +1. In order for the `refreshToken` request to pass smoothly, the `authRole` needs to be marked as `refreshToken` through metadata. +2. If the token refresh fails, an error must be thrown to prevent the failed API from retrying and the waiting APIs from continuing the request. + +::: + +> For more information about metadata, please go to [method metadata](/tutorial/getting-started/method-metadata). + +```javascript +export const refreshToken = () => { + const method = alovaInstance.Get('/refresh_token'); + method.meta = { + authRole: 'refreshToken' + }; + return method; +}; +``` + +## Release visitor request + +Some interfaces do not need to rely on token authentication. We call them "guest requests". At this time, we can set their metadata to `authRole: null` to bypass the front-end interception and allow them to send requests and receive responses smoothly. + +```javascript +export const requestTokenNotRequired = () => { + const method = alovaInstance.Get('/token_not_required'); + method.meta = { + authRole: null + }; + return method; +}; +``` + +## Login interception + +In the identity authentication interceptor, you can also intercept login requests and save login information in the interceptor to achieve the purpose of unified maintenance of identity authentication codes. + +First identify the metadata of the login request as `authRole: 'login'`. + +```javascript +export const login = () => { + const method = alovaInstance.Get('/login'); + method.meta = { + authRole: 'login' + }; + return method; +}; +``` + +Then save the login information in the login interceptor. + +```javascript +createClientTokenAuthentication({ + login(response, method) { + localStorage.setItem('token', response.token); + localStorage.setItem('refresh_token', response.refresh_token); + } +}); +``` + +> The login interceptor usage of `createServerTokenAuthentication` is the same. + +## Assign token + +Usually, we will append token to the request information in `beforeRequest`. The `assignToken` callback function is provided in the Token authentication interceptor for assigning tokens. It will filter guest requests and login requests and trigger them before the requests. It can also achieve the purpose of unified maintenance of identity authentication codes. + +```javascript +createClientTokenAuthentication({ + assignToken: method => { + method.config.headers.Authorization = localStorage.getItem('token')}; + } +}); +``` + +> The usage of assignToken callback function of `createServerTokenAuthentication` is the same. + +## Logout interception + +When your logout also requires calling the interface, you can also intercept the logout request and clear the login information. + +First identify the logout request metadata as `authRole: 'logout'`. + +```javascript +export const logout = () => { + const method = alovaInstance.Get('/logout'); + method.meta = { + authRole: 'logout' + }; + return method; +}; +``` + +Then clear the login information in the logout interceptor. + +```javascript +createClientTokenAuthentication({ + logout(response, method) { + localStorage.removeItem('token'); + localStorage.removeItem('refresh_token'); + } +}); +``` + +> The login interceptor usage of `createServerTokenAuthentication` is the same. + +## Custom identification identity + +The above metadata identities are actually the default identities. If you need to customize the identity, you can set it as follows. + +### token refresh identity + +```javascript +createClientTokenAuthentication({ + refreshToken: { + // highlight-start + metaMatches: { + refreshToken: true + } + // highlight-end + // ... + } +}); +``` + +```javascript +createServerTokenAuthentication({ + refreshTokenOnSuccess: { + // highlight-start + metaMatches: { + refreshToken: true + } + // highlight-end + // ... + }, + refreshTokenOnError: { + // highlight-start + metaMatches: { + refreshToken: true + } + // highlight-end + // ... + } +}); +``` + +Then, requests with `refreshToken: true` in the metadata will be identified as `refreshToken`. + +```javascript +export const refreshToken = () => { + const method = alovaInstance.Get('/refresh_token'); + method.meta = { + refreshToken: true + }; + return method; +}; +``` + +### Visitor identify + +```javascript +createClientTokenAuthentication({ + visitorMeta: { + isVisitor: true + } +}); +``` + +Then, requests with `isVisitor: true` in the metadata will be identified as visitors. + +```javascript +export const requestTokenNotRequired = () => { + const method = alovaInstance.Get('/token_not_required'); + method.meta = { + isVisitor: true + }; + return method; +}; +``` + +### Login identity + +```javascript +createClientTokenAuthentication({ + login: { + // highlight-start + metaMatches: { + login: true + }, + // highlight-end + handler(response, method) { + //Login interceptor + } + } +}); +``` + +Then, requests with `login: true` in the metadata will be identified as `login` identity. + +```javascript +export const login = () => { + const method = alovaInstance.Get('/login'); + method.meta = { + login: true + }; + return method; +}; +``` + +### Log out identity + +```javascript +createClientTokenAuthentication({ + logout: { + // highlight-start + metaMatches: { + logout: true + }, + // highlight-end + handler(response, method) { + //Logout interceptor + } + } +}); +``` + +Then, requests with `logout: true` in the metadata will be identified as `logout`. + +```javascript +export const logout = () => { + const method = alovaInstance.Get('/logout'); + method.meta = { + logout: true + }; + return method; +}; +``` + +> The login interceptor usage of `createServerTokenAuthentication` is the same. + +## Typescript + +By default, `createClientServerTokenAuthentication` and `createServerTokenAuthentication` are adapted to the `GlobalFetch` request adapter, you can only specify the type of `StatesHook`, as follows: + +```typescript +// highlight-start +const { onAuthRequired, onResponseRefreshToken } = createClientTokenAuthentication< + typeof VueHook +>({ + // highlight-end + //... +}); + +const alovaInstance = createAlova({ + // ... + beforeRequest: onAuthRequired(method => { + // The type of method is Method + }), + responded: onResponseRefreshToken((response, method) => { + //The response type is Response + return response.json(); + }) +}); +``` + +If you are not using the `GlobalFetch` request adapter, You also need to specify the type of request adapter, which is also simple. + +The following is an example of the axios request adapter. Specify the request adapter type in `createClientTokenAuthentication`. + +```typescript +import { axiosRequestAdapter } from '@alova/adapter-axios'; + +// highlight-start +const { onAuthRequired, onResponseRefreshToken } = createClientTokenAuthentication< + typeof ReactHook, + typeof axiosRequestAdapter +>({ + // highlight-end + //... +}); +const alovaInstance = createAlova({ + //... + // highlight-start + beforeRequest: onAuthRequired(method => { + // The type of method is Method + // highlight-end + }), + // highlight-start + responded: onResponseRefreshToken((response, method) => { + //The response type is AxiosResponse + // highlight-end + return response.data; + }) +}); +``` + +The server-based Token authentication interceptor is used in the same way. + +```typescript +import { axiosRequestAdapter } from '@alova/adapter-axios'; + +// highlight-start +createServerTokenAuthentication({ + // highlight-end + //... +}); +``` diff --git a/versioned_docs/version-2.x/tutorial/05-strategy/06-useAutoRequest.md b/versioned_docs/version-2.x/tutorial/05-strategy/06-useAutoRequest.md index 0f457373c..0ba9f1eae 100644 --- a/versioned_docs/version-2.x/tutorial/05-strategy/06-useAutoRequest.md +++ b/versioned_docs/version-2.x/tutorial/05-strategy/06-useAutoRequest.md @@ -1,168 +1,171 @@ ---- -title: Automatically refetch data -sidebar_position: 60 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -:::info strategy type - -use hook - -::: - -> Before using extension hooks, make sure you are familiar with the basic use of alova. - -Automatically fetch data through browser events or polling, allowing the interface to display the newest data. - -## Features - -- ✨ Supports refetching the newest data in scenarios such as browser focus, tab switching, network reconnection, polling requests, etc; -- ✨ Supports request throttling, only one request will be sent if triggered multiple times in a short period of time; -- ✨ Support custom event listening functions to adapt to usage scenarios in non-browser environments; - -## Install - - - - -```bash -#npm -npm install @alova/scene-vue --save -# yarn -yarn add @alova/scene-vue - -``` - - - - -```bash -#npm -npm install @alova/scene-react --save -# yarn -yarn add @alova/scene-react - -``` - - - - - -```bash -#npm -npm install @alova/scene-svelte --save -# yarn -yarn add @alova/scene-svelte - -``` - - - - -## Basic usage - -By default, useHook`useAutoRequest` that automatically fetches data will automatically fetch the newest data when browser is visible, hidden, focused, and the network is reconnected, and will automatically cancel the listening event when the component is uninstalled. - -```javascript -import { useAutoRequest } from '@alova/scene-*'; - -const { loading, data, error } = useAutoRequest(() => method()); -``` - -The return value of `useAutoRequest` is the same as [useRequest](/api/core-hooks#userequest). - -In addition to supporting all configuration parameters of [useRequest](/api/core-hooks#userequest), it also supports automatically fetched configuration parameters. You can turn on or off some events through the following configuration, or modify request throttling events. - -```javascript -const { loading, data, error, onSuccess, onError, onComplete } = useAutoRequest(() => method(), { - /** - * Browser display hide trigger - * @default true - */ - enableVisibility: true, - - /** - * Triggered by browser focus - * @default true - */ - enableFocus: true, - - /** - * Triggered by network reconnection - * @default true - */ - enableNetwork: true, - - /** - *Throttling time, only one request will be sent if triggered multiple times within a certain period, unit ms - * @default 1000 - */ - throttle: 1000, - - /** - * The time of polling request, effective when set greater than 0, unit ms - * @default 0 - */ - pollingTime: 2000 - - //Other parameters are the same as useRequest... -}); -``` - -:::warning caching advice - -It is recommended to turn off the cache of the corresponding request when using `useAutoRequest`, because when the cache is set, the cache will be hit when the automatic request is triggered and the newest data cannot be obtained. Please read [Cache Mode](/tutorial/cache/mode) for details. - -::: - -## Custom listening functions - -The above 4 methods of automatically fetching data are implemented by listening browser's events by default. When users use it in a non-browser environment, you may need to customize the listening function. This function receives the notification request function and useHook config as parameters, and returns a cancel listening function. -. - -The following is an example of custom listening function in `react-native`: - -### Network reconnection custom function - -```javascript -import NetInfo from '@react-native-community/netinfo'; -useAutoRequest.onNetwork = (notify, config) => { - const unsubscribe = NetInfo.addEventListener(({ isConnected }) => { - isConnected && notify(); - }); - return unsubscribe; -}; -``` - -### Polling custom function - -```javascript -useAutoRequest.onPolling = (notify, config) => { - const timer = setInterval(notify, config.pollingTime); - return () => clearInterval(timer); -}; -``` - -### App switching custom function - -```javascript -import { AppState, Text } from 'react-native'; -useAutoRequest.onVisibility = (notify, config) => { - const subscription = AppState.addEventListener('change', state => { - state === 'active' && notify(); - }); - return () => subscription.remove(); -}; -``` - -### App focus custom function - -Since the App doesn't have a focus event, it can be set to an empty function to avoid throwing error. - -```javascript -useAutoRequest.onFocus = (notify, config) => { - return () => {}; -}; -``` +--- +title: Automatically refetch data +sidebar_position: 60 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info strategy type + +use hook + +::: + +> Before using extension hooks, make sure you are familiar with the basic use of alova. + +Automatically fetch data through browser events or polling, allowing the interface to display the newest data. + +## Features + +- Supports refetching the newest data in scenarios such as browser focus, tab switching, network reconnection, polling requests, etc; +- Supports request throttling, only one request will be sent if triggered multiple times in a short period of time; +- Support custom event listening functions to adapt to usage scenarios in non-browser environments; + +## Install + + + + +```bash +#npm +npm install @alova/scene-vue --save +# yarn +yarn add @alova/scene-vue + +``` + + + + +```bash +#npm +npm install @alova/scene-react --save +# yarn +yarn add @alova/scene-react + +``` + + + + + +```bash +#npm +npm install @alova/scene-svelte --save +# yarn +yarn add @alova/scene-svelte + +``` + + + + +## Basic usage + +By default, useHook`useAutoRequest` that automatically fetches data will automatically fetch the newest data when browser is visible, hidden, focused, and the network is reconnected, and will automatically cancel the listening event when the component is uninstalled. + +```javascript +import { useAutoRequest } from '@alova/scene-*'; + +const { loading, data, error } = useAutoRequest(() => method()); +``` + +The return value of `useAutoRequest` is the same as [useRequest](/api/core-hooks#userequest). + +In addition to supporting all configuration parameters of [useRequest](/api/core-hooks#userequest), it also supports automatically fetched configuration parameters. You can turn on or off some events through the following configuration, or modify request throttling events. + +```javascript +const { loading, data, error, onSuccess, onError, onComplete } = useAutoRequest( + () => method(), + { + /** + * Browser display hide trigger + * @default true + */ + enableVisibility: true, + + /** + * Triggered by browser focus + * @default true + */ + enableFocus: true, + + /** + * Triggered by network reconnection + * @default true + */ + enableNetwork: true, + + /** + *Throttling time, only one request will be sent if triggered multiple times within a certain period, unit ms + * @default 1000 + */ + throttle: 1000, + + /** + * The time of polling request, effective when set greater than 0, unit ms + * @default 0 + */ + pollingTime: 2000 + + //Other parameters are the same as useRequest... + } +); +``` + +:::warning caching advice + +It is recommended to turn off the cache of the corresponding request when using `useAutoRequest`, because when the cache is set, the cache will be hit when the automatic request is triggered and the newest data cannot be obtained. Please read [Cache Mode](/tutorial/cache/mode) for details. + +::: + +## Custom listening functions + +The above 4 methods of automatically fetching data are implemented by listening browser's events by default. When users use it in a non-browser environment, you may need to customize the listening function. This function receives the notification request function and useHook config as parameters, and returns a cancel listening function. +. + +The following is an example of custom listening function in `react-native`: + +### Network reconnection custom function + +```javascript +import NetInfo from '@react-native-community/netinfo'; +useAutoRequest.onNetwork = (notify, config) => { + const unsubscribe = NetInfo.addEventListener(({ isConnected }) => { + isConnected && notify(); + }); + return unsubscribe; +}; +``` + +### Polling custom function + +```javascript +useAutoRequest.onPolling = (notify, config) => { + const timer = setInterval(notify, config.pollingTime); + return () => clearInterval(timer); +}; +``` + +### App switching custom function + +```javascript +import { AppState, Text } from 'react-native'; +useAutoRequest.onVisibility = (notify, config) => { + const subscription = AppState.addEventListener('change', state => { + state === 'active' && notify(); + }); + return () => subscription.remove(); +}; +``` + +### App focus custom function + +Since the App doesn't have a focus event, it can be set to an empty function to avoid throwing error. + +```javascript +useAutoRequest.onFocus = (notify, config) => { + return () => {}; +}; +``` diff --git a/versioned_docs/version-2.x/tutorial/05-strategy/08-useCaptcha.md b/versioned_docs/version-2.x/tutorial/05-strategy/08-useCaptcha.md index 67115c91c..e2ad31223 100644 --- a/versioned_docs/version-2.x/tutorial/05-strategy/08-useCaptcha.md +++ b/versioned_docs/version-2.x/tutorial/05-strategy/08-useCaptcha.md @@ -1,213 +1,219 @@ ---- -title: send captcha -sidebar_position: 40 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -:::info strategy type - -use hook - -::: - -> Before using extension hooks, make sure you are familiar with the basic usage of alova. - -The verification code sending hook saves you the trouble of developing the verification code sending function. - -## Example - -[Send Captcha Demo](/tutorial/example/captcha-send) - -## Features - -- ✨ The countdown will start automatically after the verification code is sent; -- ✨ Custom countdown seconds; -- ✨ Verification code sending limit; - -## Install - - - - -```bash -# npm -npm install @alova/scene-vue --save -#yarn -yarn add @alova/scene-vue - -``` - - - - -```bash -# npm -npm install @alova/scene-react --save -#yarn -yarn add @alova/scene-react - -``` - - - - - -```bash -# npm -npm install @alova/scene-svelte --save -#yarn -yarn add @alova/scene-svelte - -``` - - - - -## use - -## Basic usage - -Demonstrates basic use of form hooks. - - - - -```html - - - -``` - - - - -```jsx -import { useState } from 'react'; -import { apiSendCaptcha } from './api.js'; -import { useCaptcha } from '@alova/scene-react'; - -const App = () => { - const [mobile, setMobile] = ref(''); - const { - // send status - loading: sending, - - // Call sendCaptcha to request the interface to send the verification code - send: sendCaptcha - } = useCaptcha(() => apiSendCaptcha(mobile)); - - return ( -
- setMobile(target.value)} - /> - -
- ); -}; -``` - -
- - -```html - - - - -``` - - -
- -By default, after the verification code is successfully sent, it will count down for 60 seconds, and calling `send` when the countdown is not over will throw an error. - -### Custom countdown seconds - -You can also customize the countdown seconds - -```javascript -useCaptcha(() => apiSendCaptcha(mobile.value), { - //... - // highlight-start - // Set the countdown to 20 seconds - initialCountdown: 20 - // highlight-end -}); -``` - -## API - -### Hook configuration - -Inherit all configurations of [**useRequest**](/api/core-hooks#userequest) except `immediate`, `immediate` in `useCaptcha` has been hard-coded to false. - -| Name | Description | Type | Default | Version | -| ---------------- | ---------------------------------------------------------------------------------------------------------- | ------ | ------- | ------- | -| initialCountdown | Initial countdown, when the verification code is sent successfully, it will start countdown with this data | number | 60 | - | - -### Responsive data - -Inherit all responsive data from [**useRequest**](/api/core-hooks#userequest). - -| Name | Description | Type | Version | -| --------- | ----------------------------------------------------------------------------------------------------------- | ------ | ------- | -| countdown | The current countdown, -1 per second, the verification code can only be sent again after the countdown ends | number | - | - -### Action function - -Inherit all action functions of [**useRequest**](/api/core-hooks#userequest). - -| name | description | function parameters | return value | version | -| ---- | ---------------------------------------------------------------------------- | ------------------------------- | ------------------- | ------- | -| send | Send a request, when the countdown is not over, the call will throw an error | Consistent with useRequest.send | Promise\ | - | - -### Event - -Inherit all events from [**useRequest**](/api/core-hooks#userequest). +--- +title: send captcha +sidebar_position: 40 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info strategy type + +use hook + +::: + +> Before using extension hooks, make sure you are familiar with the basic usage of alova. + +The verification code sending hook saves you the trouble of developing the verification code sending function. + +## Example + +[Send Captcha Demo](/tutorial/example/captcha-send) + +## Features + +- The countdown will start automatically after the verification code is sent; +- Custom countdown seconds; +- Verification code sending limit; + +## Install + + + + +```bash +# npm +npm install @alova/scene-vue --save +#yarn +yarn add @alova/scene-vue + +``` + + + + +```bash +# npm +npm install @alova/scene-react --save +#yarn +yarn add @alova/scene-react + +``` + + + + + +```bash +# npm +npm install @alova/scene-svelte --save +#yarn +yarn add @alova/scene-svelte + +``` + + + + +## use + +## Basic usage + +Demonstrates basic use of form hooks. + + + + +```html + + + +``` + + + + +```jsx +import { useState } from 'react'; +import { apiSendCaptcha } from './api.js'; +import { useCaptcha } from '@alova/scene-react'; + +const App = () => { + const [mobile, setMobile] = ref(''); + const { + // send status + loading: sending, + + // Call sendCaptcha to request the interface to send the verification code + send: sendCaptcha + } = useCaptcha(() => apiSendCaptcha(mobile)); + + return ( +
+ setMobile(target.value)} + /> + +
+ ); +}; +``` + +
+ + +```html + + + + +``` + + +
+ +By default, after the verification code is successfully sent, it will count down for 60 seconds, and calling `send` when the countdown is not over will throw an error. + +### Custom countdown seconds + +You can also customize the countdown seconds + +```javascript +useCaptcha(() => apiSendCaptcha(mobile.value), { + //... + // highlight-start + // Set the countdown to 20 seconds + initialCountdown: 20 + // highlight-end +}); +``` + +## API + +### Hook configuration + +Inherit all configurations of [**useRequest**](/api/core-hooks#userequest) except `immediate`, `immediate` in `useCaptcha` has been hard-coded to false. + +| Name | Description | Type | Default | Version | +| ---------------- | ---------------------------------------------------------------------------------------------------------- | ------ | ------- | ------- | +| initialCountdown | Initial countdown, when the verification code is sent successfully, it will start countdown with this data | number | 60 | - | + +### Responsive data + +Inherit all responsive data from [**useRequest**](/api/core-hooks#userequest). + +| Name | Description | Type | Version | +| --------- | ----------------------------------------------------------------------------------------------------------- | ------ | ------- | +| countdown | The current countdown, -1 per second, the verification code can only be sent again after the countdown ends | number | - | + +### Action function + +Inherit all action functions of [**useRequest**](/api/core-hooks#userequest). + +| name | description | function parameters | return value | version | +| ---- | ---------------------------------------------------------------------------- | ------------------------------- | ------------------- | ------- | +| send | Send a request, when the countdown is not over, the call will throw an error | Consistent with useRequest.send | Promise\ | - | + +### Event + +Inherit all events from [**useRequest**](/api/core-hooks#userequest). diff --git a/versioned_docs/version-2.x/tutorial/05-strategy/09-actionDelegationMiddleware.md b/versioned_docs/version-2.x/tutorial/05-strategy/09-actionDelegationMiddleware.md index 73436b2c2..c548c74b6 100644 --- a/versioned_docs/version-2.x/tutorial/05-strategy/09-actionDelegationMiddleware.md +++ b/versioned_docs/version-2.x/tutorial/05-strategy/09-actionDelegationMiddleware.md @@ -1,244 +1,244 @@ ---- -title: Cross components to trigger request -sidebar_position: 50 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -:::info strategy type - -middleware - -::: - -> Before using extension hooks, make sure you are familiar with the basic usage of alova. - -In the past, if you want to trigger a request in another component in one component, you need to save the data in the Store and complete it by dispatching Action. Now, you can use this middleware to **remove the limitation of component hierarchy**, and quickly trigger any request action function in any component. - -For example, after updating the menu data in a component, you can re-trigger the re-request of the side menu bar to refresh the data. When the list data is manipulated, the list update is triggered. - -## Example - -[Cross-component trigger request Demo](/tutorial/example/action-delegation-middleware) - -## Features - -- ✨Delegate the action function of any use hook in alova; -- ✨ Trigger the delegated action function anywhere; - -## Install - - - - -```bash -# npm -npm install @alova/scene-vue --save -#yarn -yarn add @alova/scene-vue - -``` - - - - -```bash -# npm -npm install @alova/scene-react --save -#yarn -yarn add @alova/scene-react - -``` - - - - - -```bash -# npm -npm install @alova/scene-svelte --save -#yarn -yarn add @alova/scene-svelte - -``` - - - - -## use - -### Basic usage - -> Take vue3 as an example, the usage is the same in react and svelte. - -Use `actionDelegationMiddleware` in component A to delegate the action function of `useRequest`. - -```javascript title=Component A -import { actionDelegationMiddleware } from '@alova/scene-vue'; - -useRequest(queryTodo, { - //... - middleware: actionDelegationMiddleware('actionName') -}); -``` - -In any component (such as component B), pass in the specified delegate name through `accessAction` to trigger the action function of `useRequest` in component A. - -```javascript title=Component B -import { accessAction } from '@alova/scene-vue'; - -accessAction('actionName', delegatedActions => { - // Call the send function in component A - delegatedActions.send(); - - // Call the abort function in component A - delegatedActions.abort(); -}); -``` - -:::info note - -1. All use hooks in alova support action function delegation, but the functions delegated by different use hooks are different. -2. When using `actionDelegationMiddleware`, the delegate name can be passed in strings, numbers, and symbol values. - -::: - -### Batch trigger action function - -In the above example, we use `accessAction` to trigger the action function of a use hook, but in fact, delegates with the same name will not override each other, but will be stored in a group, and we can use this name to trigger them at the same time The delegated function. - -```javascript title=Component C -import { actionDelegationMiddleware } from '@alova/scene-vue'; - -useRequest(queryTodo, { - //... - middleware: actionDelegationMiddleware('actionName1') -}); -``` - -```javascript title=Component D -import { actionDelegationMiddleware } from '@alova/scene-vue'; - -useRequest(queryTodo, { - //... - middleware: actionDelegationMiddleware('actionName1') -}); -``` - -```javascript title=Component E -import { accessAction } from '@alova/scene-vue'; - -// Because the delegate hooks of component C and component D will be matched, the callback function will be executed twice -accessAction('actionName1', delegatedActions => { - // Call the send function in component C and component D - delegatedActions.send(); - - // Call the abort function in component C and component D - delegatedActions.abort(); -}); -``` - -At the same time, regular expressions can also be used in `accessAction` to trigger batches of action functions whose delegate names meet the conditions - -```javascript title=Component F -import { actionDelegationMiddleware } from '@alova/scene-vue'; - -useRequest(queryTodo, { - //... - middleware: actionDelegationMiddleware('prefix_name1') -}); -``` - -```javascript title=Component G -import { actionDelegationMiddleware } from '@alova/scene-vue'; - -useRequest(queryTodo, { - //... - middleware: actionDelegationMiddleware('prefix_name2') -}); -``` - -```javascript title=Component H -import { accessAction } from '@alova/scene-vue'; - -// Because the delegate hooks of component F and component G will be matched, the callback function will be executed twice -accessAction(/^prefix_/, delegatedActions => { - // Call the send function in component F and component G - delegatedActions.send(); - - // Call the abort function in component F and component G - delegatedActions.abort(); -}); -``` - -## Action function delegation list - -Although the action functions delegated by most hooks are the same as the action functions themselves, this is not absolute. The following is the action function delegation list of each hook. - -### useRequest - -| name | description | function parameters | return value | version | -| ------ | ------------------------------------------------------- | ------------------- | ------------ | ------- | -| send | Same as [useRequset](/api/core-hooks#userequest).send | | | - | -| abort | Same as [useRequset](/api/core-hooks#userequest).abort | | | - | -| update | Same as [useRequset](/api/core-hooks#userequest).update | | | - | - -### useWatcher - -Same as [useRequest delegate list](#userequest). - -### useFetcher - -| name | description | function parameters | return value | version | -| ------ | ------------------------------------------------------- | ------------------- | ------------ | ------- | -| fetch | Same as [useFetcher](/api/core-hooks#usefetcher).fetch | | | - | -| abort | Same as [useFetcher](/api/core-hooks#usefetcher).abort | | | - | -| update | Same as [useFetcher](/api/core-hooks#usefetcher).update | | | - | - -### usePagination - -| name | description | function parameters | return value | version | -| -------- | -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | ------------------------------------------ | ------- | -| refresh | For details, see [usePagination action function](/tutorial/strategy/usePagination#api) | | | - | -| insert | For details, see [usePagination action function](/tutorial/strategy/usePagination#api) | | | - | -| remove | For details, see [usePagination action function](/tutorial/strategy/usePagination#api) | | | - | -| replace | For details, see [usePagination action function](/tutorial/strategy/usePagination#api) | | | - | -| reload | For details, see [usePagination action function](/tutorial/strategy/usePagination#api) | | | - | -| update | For details, see [usePagination action function](/tutorial/strategy/usePagination#api) | | | - | -| getState | Get paging related data by name | stateKey: 'page' \| 'pageSize' \| 'data' \| 'pageCount' \| 'total' \| 'isLastPage' | Corresponding to the value of the statekey | - | - -### useSQRequest - -Same as [useRequest delegate list](#userequest). - -### useForm - -| name | description | function parameters | return value | version | -| ---------- | -------------------------------------------------------------------------- | ------------------- | ------------ | ------- | -| updateForm | For details, see [useForm action function](/tutorial/strategy/useForm#api) | | | - | -| reset | For details, see [useForm action function](/tutorial/strategy/useForm#api) | | | - | -| send | Same as [useRequset](/api/core-hooks#userequest).send | | | - | -| abort | Same as [useRequset](/api/core-hooks#userequest).abort | | | - | -| update | Same as [useRequset](/api/core-hooks#userequest).update | | | - | - -### useCaptcha - -Same as [useRequest delegate list](#userequest). - -### useRetriableRequest - -| name | description | function parameters | return value | version | -| ------ | ------------------------------------------------------------------------------------------------- | ------------------- | ------------ | ------- | -| stop | See [useRetriableRequest action function](/tutorial/strategy/useRetriableRequest#api) for details | | | - | -| send | Same as [useRequset](/api/core-hooks#userequest).send | | | - | -| abort | Same as [useRequset](/api/core-hooks#userequest).abort | | | - | -| update | Same as [useRequset](/api/core-hooks#userequest).update | | | - | - -### useSerialRequest - -Same as [useRequest delegate list](#userequest). - -### useSerialWatcher - -Same as [useRequest delegate list](#userequest). +--- +title: Cross components to trigger request +sidebar_position: 50 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info strategy type + +middleware + +::: + +> Before using extension hooks, make sure you are familiar with the basic usage of alova. + +In the past, if you want to trigger a request in another component in one component, you need to save the data in the Store and complete it by dispatching Action. Now, you can use this middleware to **remove the limitation of component hierarchy**, and quickly trigger any request action function in any component. + +For example, after updating the menu data in a component, you can re-trigger the re-request of the side menu bar to refresh the data. When the list data is manipulated, the list update is triggered. + +## Example + +[Cross-component trigger request Demo](/tutorial/example/action-delegation-middleware) + +## Features + +- Delegate the action function of any use hook in alova; +- Trigger the delegated action function anywhere; + +## Install + + + + +```bash +# npm +npm install @alova/scene-vue --save +#yarn +yarn add @alova/scene-vue + +``` + + + + +```bash +# npm +npm install @alova/scene-react --save +#yarn +yarn add @alova/scene-react + +``` + + + + + +```bash +# npm +npm install @alova/scene-svelte --save +#yarn +yarn add @alova/scene-svelte + +``` + + + + +## use + +### Basic usage + +> Take vue3 as an example, the usage is the same in react and svelte. + +Use `actionDelegationMiddleware` in component A to delegate the action function of `useRequest`. + +```javascript title=Component A +import { actionDelegationMiddleware } from '@alova/scene-vue'; + +useRequest(queryTodo, { + //... + middleware: actionDelegationMiddleware('actionName') +}); +``` + +In any component (such as component B), pass in the specified delegate name through `accessAction` to trigger the action function of `useRequest` in component A. + +```javascript title=Component B +import { accessAction } from '@alova/scene-vue'; + +accessAction('actionName', delegatedActions => { + // Call the send function in component A + delegatedActions.send(); + + // Call the abort function in component A + delegatedActions.abort(); +}); +``` + +:::info note + +1. All use hooks in alova support action function delegation, but the functions delegated by different use hooks are different. +2. When using `actionDelegationMiddleware`, the delegate name can be passed in strings, numbers, and symbol values. + +::: + +### Batch trigger action function + +In the above example, we use `accessAction` to trigger the action function of a use hook, but in fact, delegates with the same name will not override each other, but will be stored in a group, and we can use this name to trigger them at the same time The delegated function. + +```javascript title=Component C +import { actionDelegationMiddleware } from '@alova/scene-vue'; + +useRequest(queryTodo, { + //... + middleware: actionDelegationMiddleware('actionName1') +}); +``` + +```javascript title=Component D +import { actionDelegationMiddleware } from '@alova/scene-vue'; + +useRequest(queryTodo, { + //... + middleware: actionDelegationMiddleware('actionName1') +}); +``` + +```javascript title=Component E +import { accessAction } from '@alova/scene-vue'; + +// Because the delegate hooks of component C and component D will be matched, the callback function will be executed twice +accessAction('actionName1', delegatedActions => { + // Call the send function in component C and component D + delegatedActions.send(); + + // Call the abort function in component C and component D + delegatedActions.abort(); +}); +``` + +At the same time, regular expressions can also be used in `accessAction` to trigger batches of action functions whose delegate names meet the conditions + +```javascript title=Component F +import { actionDelegationMiddleware } from '@alova/scene-vue'; + +useRequest(queryTodo, { + //... + middleware: actionDelegationMiddleware('prefix_name1') +}); +``` + +```javascript title=Component G +import { actionDelegationMiddleware } from '@alova/scene-vue'; + +useRequest(queryTodo, { + //... + middleware: actionDelegationMiddleware('prefix_name2') +}); +``` + +```javascript title=Component H +import { accessAction } from '@alova/scene-vue'; + +// Because the delegate hooks of component F and component G will be matched, the callback function will be executed twice +accessAction(/^prefix_/, delegatedActions => { + // Call the send function in component F and component G + delegatedActions.send(); + + // Call the abort function in component F and component G + delegatedActions.abort(); +}); +``` + +## Action function delegation list + +Although the action functions delegated by most hooks are the same as the action functions themselves, this is not absolute. The following is the action function delegation list of each hook. + +### useRequest + +| name | description | function parameters | return value | version | +| ------ | ------------------------------------------------------- | ------------------- | ------------ | ------- | +| send | Same as [useRequset](/api/core-hooks#userequest).send | | | - | +| abort | Same as [useRequset](/api/core-hooks#userequest).abort | | | - | +| update | Same as [useRequset](/api/core-hooks#userequest).update | | | - | + +### useWatcher + +Same as [useRequest delegate list](#userequest). + +### useFetcher + +| name | description | function parameters | return value | version | +| ------ | ------------------------------------------------------- | ------------------- | ------------ | ------- | +| fetch | Same as [useFetcher](/api/core-hooks#usefetcher).fetch | | | - | +| abort | Same as [useFetcher](/api/core-hooks#usefetcher).abort | | | - | +| update | Same as [useFetcher](/api/core-hooks#usefetcher).update | | | - | + +### usePagination + +| name | description | function parameters | return value | version | +| -------- | -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | ------------------------------------------ | ------- | +| refresh | For details, see [usePagination action function](/tutorial/strategy/usePagination#api) | | | - | +| insert | For details, see [usePagination action function](/tutorial/strategy/usePagination#api) | | | - | +| remove | For details, see [usePagination action function](/tutorial/strategy/usePagination#api) | | | - | +| replace | For details, see [usePagination action function](/tutorial/strategy/usePagination#api) | | | - | +| reload | For details, see [usePagination action function](/tutorial/strategy/usePagination#api) | | | - | +| update | For details, see [usePagination action function](/tutorial/strategy/usePagination#api) | | | - | +| getState | Get paging related data by name | stateKey: 'page' \| 'pageSize' \| 'data' \| 'pageCount' \| 'total' \| 'isLastPage' | Corresponding to the value of the statekey | - | + +### useSQRequest + +Same as [useRequest delegate list](#userequest). + +### useForm + +| name | description | function parameters | return value | version | +| ---------- | -------------------------------------------------------------------------- | ------------------- | ------------ | ------- | +| updateForm | For details, see [useForm action function](/tutorial/strategy/useForm#api) | | | - | +| reset | For details, see [useForm action function](/tutorial/strategy/useForm#api) | | | - | +| send | Same as [useRequset](/api/core-hooks#userequest).send | | | - | +| abort | Same as [useRequset](/api/core-hooks#userequest).abort | | | - | +| update | Same as [useRequset](/api/core-hooks#userequest).update | | | - | + +### useCaptcha + +Same as [useRequest delegate list](#userequest). + +### useRetriableRequest + +| name | description | function parameters | return value | version | +| ------ | ------------------------------------------------------------------------------------------------- | ------------------- | ------------ | ------- | +| stop | See [useRetriableRequest action function](/tutorial/strategy/useRetriableRequest#api) for details | | | - | +| send | Same as [useRequset](/api/core-hooks#userequest).send | | | - | +| abort | Same as [useRequset](/api/core-hooks#userequest).abort | | | - | +| update | Same as [useRequset](/api/core-hooks#userequest).update | | | - | + +### useSerialRequest + +Same as [useRequest delegate list](#userequest). + +### useSerialWatcher + +Same as [useRequest delegate list](#userequest). diff --git a/versioned_docs/version-2.x/tutorial/05-strategy/10-useSerialRequest.md b/versioned_docs/version-2.x/tutorial/05-strategy/10-useSerialRequest.md index 1e5c6e5b8..bade49f1f 100644 --- a/versioned_docs/version-2.x/tutorial/05-strategy/10-useSerialRequest.md +++ b/versioned_docs/version-2.x/tutorial/05-strategy/10-useSerialRequest.md @@ -1,144 +1,144 @@ ---- -title: useRequest with serial -sidebar_position: 60 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -:::info strategy type - -use hook - -::: - -> Before using extension hooks, make sure you are familiar with the basic usage of alova. - -This use hook is more concise and easy to use than [serial request in best practice](/tutorial/best-practice/skills), with unified loading status, error, and callback functions. - -## Features - -- ✨ A more concise and easy-to-use serial method; -- ✨Unified request status and callback function; -- ✨ send function can trigger serial execution of multiple requests; - -## Example - -[serial request](/tutorial/example/serial-request) - -## Install - - - - -```bash -# npm -npm install @alova/scene-vue --save -#yarn -yarn add @alova/scene-vue - -``` - - - - -```bash -# npm -npm install @alova/scene-react --save -#yarn -yarn add @alova/scene-react - -``` - - - - - -```bash -# npm -npm install @alova/scene-svelte --save -#yarn -yarn add @alova/scene-svelte - -``` - - - - -## Usage - -### Basic usage - -Same usage as `useRequest`, except that the first parameter is changed to an array of handlers executed serially, and each handler will receive the response data of the previous request. - -```javascript -const { - // Serial loading status, all requests will be changed to false - loading, - - // The response data of the last request - data, - - // Any request error will record the error message here - error, - - // Manually send a serial request - send, - - // serial request success callback binding function - onSuccess, - - // Serial request error callback binding function, any request error will trigger it - onError, - - // Serial request completion callback binding function - onComplete -} = useSerialRequest( - [ - // args is the parameter passed in by the send function - (...args) => request1(args), - - // Starting from the second handler, the first parameter is the response data of the previous request, and args is received from the second - (response1, ...args) => request2(response1, args), - (response2, ...args) => request3(response2, args) - ], - { - immediate: false - } -); - -// emit request manually and pass arguments -send(1, 2, 3); -``` - -It is worth noting that the first item in the handler array can also be specified as a method instance, and the second item must be a function. - -```javascript -useSerialRequest([ - methodInstance, - (response1, ...args) => request2(response1, args), - (response2, ...args) => request3(response2, args) -]); -``` - -### Request error - -When any of the serial requests is wrong, `onError` will be triggered, and its `event.method` will point to the method instance of the request error. - -## API - -### Hook configuration - -Inherit all configurations from [**useRequest**](/api/core-hooks#userequest). - -### Responsive data - -Inherit all responsive data from [**useRequest**](/api/core-hooks#userequest). - -### Action function - -Inherit all action functions of [**useRequest**](/api/core-hooks#userequest). - -### Event - -Inherit all events from [**useRequest**](/api/core-hooks#userequest). +--- +title: useRequest with serial +sidebar_position: 60 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info strategy type + +use hook + +::: + +> Before using extension hooks, make sure you are familiar with the basic usage of alova. + +This use hook is more concise and easy to use than [serial request in best practice](/tutorial/best-practice/skills), with unified loading status, error, and callback functions. + +## Features + +- A more concise and easy-to-use serial method; +- Unified request status and callback function; +- send function can trigger serial execution of multiple requests; + +## Example + +[serial request](/tutorial/example/serial-request) + +## Install + + + + +```bash +# npm +npm install @alova/scene-vue --save +#yarn +yarn add @alova/scene-vue + +``` + + + + +```bash +# npm +npm install @alova/scene-react --save +#yarn +yarn add @alova/scene-react + +``` + + + + + +```bash +# npm +npm install @alova/scene-svelte --save +#yarn +yarn add @alova/scene-svelte + +``` + + + + +## Usage + +### Basic usage + +Same usage as `useRequest`, except that the first parameter is changed to an array of handlers executed serially, and each handler will receive the response data of the previous request. + +```javascript +const { + // Serial loading status, all requests will be changed to false + loading, + + // The response data of the last request + data, + + // Any request error will record the error message here + error, + + // Manually send a serial request + send, + + // serial request success callback binding function + onSuccess, + + // Serial request error callback binding function, any request error will trigger it + onError, + + // Serial request completion callback binding function + onComplete +} = useSerialRequest( + [ + // args is the parameter passed in by the send function + (...args) => request1(args), + + // Starting from the second handler, the first parameter is the response data of the previous request, and args is received from the second + (response1, ...args) => request2(response1, args), + (response2, ...args) => request3(response2, args) + ], + { + immediate: false + } +); + +// emit request manually and pass arguments +send(1, 2, 3); +``` + +It is worth noting that the first item in the handler array can also be specified as a method instance, and the second item must be a function. + +```javascript +useSerialRequest([ + methodInstance, + (response1, ...args) => request2(response1, args), + (response2, ...args) => request3(response2, args) +]); +``` + +### Request error + +When any of the serial requests is wrong, `onError` will be triggered, and its `event.method` will point to the method instance of the request error. + +## API + +### Hook configuration + +Inherit all configurations from [**useRequest**](/api/core-hooks#userequest). + +### Responsive data + +Inherit all responsive data from [**useRequest**](/api/core-hooks#userequest). + +### Action function + +Inherit all action functions of [**useRequest**](/api/core-hooks#userequest). + +### Event + +Inherit all events from [**useRequest**](/api/core-hooks#userequest). diff --git a/versioned_docs/version-2.x/tutorial/05-strategy/11-useSerialWatcher.md b/versioned_docs/version-2.x/tutorial/05-strategy/11-useSerialWatcher.md index beeacda34..ab30b621e 100644 --- a/versioned_docs/version-2.x/tutorial/05-strategy/11-useSerialWatcher.md +++ b/versioned_docs/version-2.x/tutorial/05-strategy/11-useSerialWatcher.md @@ -1,145 +1,145 @@ ---- -title: useWatcher with serial -sidebar_position: 70 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -:::info policy type - -use hook - -::: - -> Before using extension hooks, make sure you are familiar with the basic usage of alova. - -Status update triggers a set of serial requests, which is more concise and easy to use than [serial request in best practice](/tutorial/best-practice/skills), with unified loading status, error, and callback functions. - -## Features - -- ✨ A more concise and easy-to-use serial method; -- ✨Unified request status and callback function; -- ✨Status update triggers serial execution of multiple requests; - -## Example - -[serial request](/tutorial/example/serial-request) - -## Install - - - - -```bash -# npm -npm install @alova/scene-vue --save -#yarn -yarn add @alova/scene-vue - -``` - - - - -```bash -# npm -npm install @alova/scene-react --save -#yarn -yarn add @alova/scene-react - -``` - - - - - -```bash -# npm -npm install @alova/scene-svelte --save -#yarn -yarn add @alova/scene-svelte - -``` - - - - -## use - -### Basic usage - -Same usage as `useWatcher`, except that the first parameter is changed to an array of handlers executed serially, and each handler will receive the response data of the previous request. - -```javascript -const { - // Serial loading status, all requests will be changed to false - loading, - - // The response data of the last request - data, - - // Any request error will record the error message here - error, - - // Manually send a serial request - send, - - // serial request success callback binding function - onSuccess, - - // Serial request error callback binding function, any request error will trigger it - onError, - - // Serial request completion callback binding function - onComplete -} = useSerialWatcher( - [ - // args is the parameter passed in by the send function - (...args) => request1(args), - - // Starting from the second handler, the first parameter is the response data of the previous request, and args is received from the second - (response1, ...args) => request2(response1, args), - (response2, ...args) => request3(response2, args) - ], - [watchedState1, watchedState2], - { - immediate: true - } -); - -// Manually trigger the request and pass parameters -send(1, 2, 3); -``` - -It is worth noting that the first item in the handler array can also be specified as a method instance, and the second item must be a function. - -```javascript -useSerialRequest([ - methodInstance, - (response1, ...args) => request2(response1, args), - (response2, ...args) => request3(response2, args) -]); -``` - -### Request error - -When any of the serial requests is wrong, `onError` will be triggered, and its `event.method` will point to the method instance of the request error. - -## API - -### Hook configuration - -Inherit all configurations of [**useWatcher**](/api/core-hooks#usewatcher). - -### Responsive data - -Inherit all responsive data from [**useWatcher**](/api/core-hooks#usewatcher). - -### Action function - -Inherit all action functions of [**useWatcher**](/api/core-hooks#usewatcher). - -### Event - -Inherit all events from [**useWatcher**](/api/core-hooks#usewatcher). +--- +title: useWatcher with serial +sidebar_position: 70 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info policy type + +use hook + +::: + +> Before using extension hooks, make sure you are familiar with the basic usage of alova. + +Status update triggers a set of serial requests, which is more concise and easy to use than [serial request in best practice](/tutorial/best-practice/skills), with unified loading status, error, and callback functions. + +## Features + +- A more concise and easy-to-use serial method; +- Unified request status and callback function; +- Status update triggers serial execution of multiple requests; + +## Example + +[serial request](/tutorial/example/serial-request) + +## Install + + + + +```bash +# npm +npm install @alova/scene-vue --save +#yarn +yarn add @alova/scene-vue + +``` + + + + +```bash +# npm +npm install @alova/scene-react --save +#yarn +yarn add @alova/scene-react + +``` + + + + + +```bash +# npm +npm install @alova/scene-svelte --save +#yarn +yarn add @alova/scene-svelte + +``` + + + + +## use + +### Basic usage + +Same usage as `useWatcher`, except that the first parameter is changed to an array of handlers executed serially, and each handler will receive the response data of the previous request. + +```javascript +const { + // Serial loading status, all requests will be changed to false + loading, + + // The response data of the last request + data, + + // Any request error will record the error message here + error, + + // Manually send a serial request + send, + + // serial request success callback binding function + onSuccess, + + // Serial request error callback binding function, any request error will trigger it + onError, + + // Serial request completion callback binding function + onComplete +} = useSerialWatcher( + [ + // args is the parameter passed in by the send function + (...args) => request1(args), + + // Starting from the second handler, the first parameter is the response data of the previous request, and args is received from the second + (response1, ...args) => request2(response1, args), + (response2, ...args) => request3(response2, args) + ], + [watchedState1, watchedState2], + { + immediate: true + } +); + +// Manually trigger the request and pass parameters +send(1, 2, 3); +``` + +It is worth noting that the first item in the handler array can also be specified as a method instance, and the second item must be a function. + +```javascript +useSerialRequest([ + methodInstance, + (response1, ...args) => request2(response1, args), + (response2, ...args) => request3(response2, args) +]); +``` + +### Request error + +When any of the serial requests is wrong, `onError` will be triggered, and its `event.method` will point to the method instance of the request error. + +## API + +### Hook configuration + +Inherit all configurations of [**useWatcher**](/api/core-hooks#usewatcher). + +### Responsive data + +Inherit all responsive data from [**useWatcher**](/api/core-hooks#usewatcher). + +### Action function + +Inherit all action functions of [**useWatcher**](/api/core-hooks#usewatcher). + +### Event + +Inherit all events from [**useWatcher**](/api/core-hooks#usewatcher). diff --git a/versioned_docs/version-2.x/tutorial/05-strategy/12-useRetriableRequest.md b/versioned_docs/version-2.x/tutorial/05-strategy/12-useRetriableRequest.md index d6fba2230..301fdcb95 100644 --- a/versioned_docs/version-2.x/tutorial/05-strategy/12-useRetriableRequest.md +++ b/versioned_docs/version-2.x/tutorial/05-strategy/12-useRetriableRequest.md @@ -1,269 +1,269 @@ ---- -title: retriable request -sidebar_position: 80 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -:::info strategy type - -use hook - -::: - -> Before using extension hooks, make sure you are familiar with the basic usage of alova. - -A use hook that can automatically retry a request failure, you can use it for important requests. - -## Example - -[Request Retry Demo](/tutorial/example/retriable-hook) - -## Features - -- ✨Customize the number of retries or judge whether retry is required according to the conditions; -- ✨ Retry delay mechanism; -- ✨ Manually stop retrying; - -## Install - - - - -```bash -# npm -npm install @alova/scene-vue --save -#yarn -yarn add @alova/scene-vue - -``` - - - - -```bash -# npm -npm install @alova/scene-react --save -#yarn -yarn add @alova/scene-react - -``` - - - - - -```bash -# npm -npm install @alova/scene-svelte --save -#yarn -yarn add @alova/scene-svelte - -``` - - - - -## use - -### Basic usage - -```javascript -const { - // Loading status, always true during retry, until retry succeeds or fails - loading, - - // response data - data, - - // Request error information, every time a request or retry fails, there will be an error instance - // The last error instance will be overwritten - error, - - // Every time a request or retry fails, the onError event will be triggered - onError, - - // Request retry event, triggered immediately after each retry request is issued - onRetry, - - // request retry failure event - // The request is not successful after reaching the maximum number of retries, or manually stopping retries will trigger - onFail, - - // request or retry success event - onSuccess, - - // Every request or retry, regardless of success or failure, will trigger the completion event - onComplete -} = useRetriableRequest(request); -``` - -The maximum number of request retries for `useRetriableRequest` defaults to 3, and each retry will be delayed by 1 second. It will also make a request by default, you can change the behavior by setting `immediate` to false. - -### Set the static maximum number of retries - -The maximum number of retries indicates the maximum number of times to retry the request after the first request fails. During this period, if the request succeeds, it will stop continuing to retry. The default maximum number of retries is 3, and you can customize the settings in the following ways. - -When the request reaches the maximum number of retries and still fails, the `onFail` event will be triggered and the request retry will stop. If you want to continue to retry after the failure, you can call the `send` function, and it will perform a new round request and retry. - -```javascript -const { send } = useRetriableRequest(request, { - //... - // highlight-start - // Set the maximum number of retries to 5 - retry: 5 - // highlight-end -}); -``` - -### Dynamically set the maximum number of retries - -Maybe sometimes you want to use a certain condition to determine whether to continue to retry. At this time, you can set `retry` as a function that returns a boolean value to dynamically determine whether to continue to retry. - -```javascript -useRetriableRequest(request, { - //... - // highlight-start - // The first parameter is the last error instance, and the parameters passed in from the second parameter to send - retry(error, ...args) { - // If the request times out, continue to retry - return /network timeout/i.test(error.message); - } - // highlight-end -}); -``` - -### Set delay time - -The default retry delay time is 1 second, you can customize the setting in the following ways. - -```javascript -useRetriableRequest(request, { - //... - backoff: { - // highlight-start - // Set the delay time to 2 seconds - delay: 2000 - // highlight-end - } -}); -``` - -### Set an unfixed retry delay time - -Sometimes you want that the delay time of each request is not fixed, you can set the delay growth multiple in the following way, and the delay time will increase exponentially according to the number of retries. - -```javascript -useRetriableRequest(request, { - //... - backoff: { - delay: 2000, - // highlight-start - // When the multiplier is set to 2, the first retry delay is 2 seconds, the second is 4 seconds, the third is 8 seconds, and so on. - multiplier: 2 - // highlight-end - } -}); -``` - -not enough? You can even add a random jitter value to each delay to make it look less regular. - -```javascript -useRetriableRequest(request, { - //... - backoff: { - delay: 2000, - multiplier: 2, - // highlight-start - /** - * The initial jitter percentage value of the delay request, the range is 0-1 - * When only startQuiver is set, endQuiver defaults to 1 - * For example set to 0.5, it will add 50% to 100% random time on the current delay time - * If endQuiver has a value, the delay time will be increased by a random value in the range of startQuiver and endQuiver - */ - startQuiver: 0.5, - - /** - * The jitter end percentage value of the delayed request, the range is 0-1 - * When only endQuiver is set, startQuiver defaults to 0 - * For example set to 0.8, it will add a random time from 0% to 80% on the current delay time - * If startQuiver has a value, the delay time will increase the random value in the range of startQuiver and endQuiver - */ - endQuiver: 0.8; - // highlight-end - } -}); -``` - -### Manually stop retrying - -In some cases, you need to manually stop the retry, whether you are currently requesting or waiting for the next retry, you can use `stop` to stop it. - -```javascript -const { stop } = useRetriableRequest(request, { - //... -}); - -const handleStop = () => { - stop(); -}; -``` - -## API - -### Hook configuration - -Inherit all configurations from [**useRequest**](/api/core-hooks#userequest). - -| Name | Description | Type | Default | Version | -| ------- | -------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- | ----------------------------------------- | ------- | --- | -| retry | The maximum number of retries can also be set as a function returning a boolean value to dynamically determine whether to continue to retry. | number | (error: Error, ...args: any[]) => boolean | 3 | - | -| backoff | Backoff policy, set retry delay time, etc. | [BackoffPolicy](#backoffpolicy) | - | - | - -### BackoffPolicy - -| Name | Description | Type | Default | Version | -| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | ------- | ------- | -| delay | Delay time for another request, in milliseconds | number | 1000 | - | -| multiplier | Specify the delay multiplier, for example, when multiplier is set to 2 and delay is 1 second, the first retry is 1 second, the second is 2 seconds, the third is 4 seconds, and so on | number | 0 | - | -| startQuiver | The initial jitter percentage value of the delay request, ranging from 0-1. When only startQuiver is set, endQuiver defaults to 1. For example, if it is set to 0.5, it will increase the current delay time by 50% to 100% randomly Time, if endQuiver has a value, the delay time will be increased by a random value in the range of startQuiver and endQuiver | number | 0 | - | -| endQuiver | The jitter end percentage value of the delayed request, the range is 0-1, when onlyWhen endQuiver is set, startQuiver defaults to 0. For example, if it is set to 0.5, it will add a random time from 0% to 50% to the current delay time. If startQuiver has a value, the delay time will increase the random value in the range of startQuiver and endQuiver | number | 0 | - | - -### Responsive data - -Inherit all responsive data from [**useRequest**](/api/core-hooks#userequest). - -### Action function - -Inherit all action functions of [**useRequest**](/api/core-hooks#userequest). - -| name | description | function parameters | return value | version | -| ---- | ------------------------------------------------------------------------------------------------------------------ | ------------------- | ------------ | ------- | -| stop | Stop retrying, it is only valid during retrying, and the onFail event will be triggered immediately after stopping | - | - | - | - -### Event - -Inherit all events from [**useRequest**](/api/core-hooks#userequest). - -| Name | Description | Callback Parameters | Version | -| ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | ------- | -| onRetry | Retry event bindings, they will fire after a retry is initiated | Retry event instance [RetriableRetryEvent](#retriableretryevent) | - | -| onFail | Triggered when the request fails. It will be triggered when no more retries are made. For example, when the maximum number of retries is reached, when the retry callback returns false, manually call stop to stop retrying
Note:
1 The .onError event will be triggered every time an error is reported.
2. If there are no retries, onError, onComplete and onFail will be triggered at the same time | Retry event instance [RetriableFailEvent](#retriablefailevent) | - | - -#### RetriableRetryEvent - -Event event instance inherited from alova. - -| Name | Description | Type | Default | Version | -| ---------- | ----------------------------------- | ------ | -------- | ------- | -| retryTimes | current retry times | number | required | - | -| retryDelay | The delay time of this retry, in ms | number | required | - | - -#### RetriableFailEvent - -Event event instance inherited from alova. - -| Name | Description | Type | Default | Version | -| ---------- | ------------------- | ------ | -------- | ------- | -| retryTimes | current retry times | number | required | - | +--- +title: retriable request +sidebar_position: 80 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info strategy type + +use hook + +::: + +> Before using extension hooks, make sure you are familiar with the basic usage of alova. + +A use hook that can automatically retry a request failure, you can use it for important requests. + +## Example + +[Request Retry Demo](/tutorial/example/retriable-hook) + +## Features + +- Customize the number of retries or judge whether retry is required according to the conditions; +- Retry delay mechanism; +- Manually stop retrying; + +## Install + + + + +```bash +# npm +npm install @alova/scene-vue --save +#yarn +yarn add @alova/scene-vue + +``` + + + + +```bash +# npm +npm install @alova/scene-react --save +#yarn +yarn add @alova/scene-react + +``` + + + + + +```bash +# npm +npm install @alova/scene-svelte --save +#yarn +yarn add @alova/scene-svelte + +``` + + + + +## use + +### Basic usage + +```javascript +const { + // Loading status, always true during retry, until retry succeeds or fails + loading, + + // response data + data, + + // Request error information, every time a request or retry fails, there will be an error instance + // The last error instance will be overwritten + error, + + // Every time a request or retry fails, the onError event will be triggered + onError, + + // Request retry event, triggered immediately after each retry request is issued + onRetry, + + // request retry failure event + // The request is not successful after reaching the maximum number of retries, or manually stopping retries will trigger + onFail, + + // request or retry success event + onSuccess, + + // Every request or retry, regardless of success or failure, will trigger the completion event + onComplete +} = useRetriableRequest(request); +``` + +The maximum number of request retries for `useRetriableRequest` defaults to 3, and each retry will be delayed by 1 second. It will also make a request by default, you can change the behavior by setting `immediate` to false. + +### Set the static maximum number of retries + +The maximum number of retries indicates the maximum number of times to retry the request after the first request fails. During this period, if the request succeeds, it will stop continuing to retry. The default maximum number of retries is 3, and you can customize the settings in the following ways. + +When the request reaches the maximum number of retries and still fails, the `onFail` event will be triggered and the request retry will stop. If you want to continue to retry after the failure, you can call the `send` function, and it will perform a new round request and retry. + +```javascript +const { send } = useRetriableRequest(request, { + //... + // highlight-start + // Set the maximum number of retries to 5 + retry: 5 + // highlight-end +}); +``` + +### Dynamically set the maximum number of retries + +Maybe sometimes you want to use a certain condition to determine whether to continue to retry. At this time, you can set `retry` as a function that returns a boolean value to dynamically determine whether to continue to retry. + +```javascript +useRetriableRequest(request, { + //... + // highlight-start + // The first parameter is the last error instance, and the parameters passed in from the second parameter to send + retry(error, ...args) { + // If the request times out, continue to retry + return /network timeout/i.test(error.message); + } + // highlight-end +}); +``` + +### Set delay time + +The default retry delay time is 1 second, you can customize the setting in the following ways. + +```javascript +useRetriableRequest(request, { + //... + backoff: { + // highlight-start + // Set the delay time to 2 seconds + delay: 2000 + // highlight-end + } +}); +``` + +### Set an unfixed retry delay time + +Sometimes you want that the delay time of each request is not fixed, you can set the delay growth multiple in the following way, and the delay time will increase exponentially according to the number of retries. + +```javascript +useRetriableRequest(request, { + //... + backoff: { + delay: 2000, + // highlight-start + // When the multiplier is set to 2, the first retry delay is 2 seconds, the second is 4 seconds, the third is 8 seconds, and so on. + multiplier: 2 + // highlight-end + } +}); +``` + +not enough? You can even add a random jitter value to each delay to make it look less regular. + +```javascript +useRetriableRequest(request, { + //... + backoff: { + delay: 2000, + multiplier: 2, + // highlight-start + /** + * The initial jitter percentage value of the delay request, the range is 0-1 + * When only startQuiver is set, endQuiver defaults to 1 + * For example set to 0.5, it will add 50% to 100% random time on the current delay time + * If endQuiver has a value, the delay time will be increased by a random value in the range of startQuiver and endQuiver + */ + startQuiver: 0.5, + + /** + * The jitter end percentage value of the delayed request, the range is 0-1 + * When only endQuiver is set, startQuiver defaults to 0 + * For example set to 0.8, it will add a random time from 0% to 80% on the current delay time + * If startQuiver has a value, the delay time will increase the random value in the range of startQuiver and endQuiver + */ + endQuiver: 0.8; + // highlight-end + } +}); +``` + +### Manually stop retrying + +In some cases, you need to manually stop the retry, whether you are currently requesting or waiting for the next retry, you can use `stop` to stop it. + +```javascript +const { stop } = useRetriableRequest(request, { + //... +}); + +const handleStop = () => { + stop(); +}; +``` + +## API + +### Hook configuration + +Inherit all configurations from [**useRequest**](/api/core-hooks#userequest). + +| Name | Description | Type | Default | Version | +| ------- | -------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- | ----------------------------------------- | ------- | --- | +| retry | The maximum number of retries can also be set as a function returning a boolean value to dynamically determine whether to continue to retry. | number | (error: Error, ...args: any[]) => boolean | 3 | - | +| backoff | Backoff policy, set retry delay time, etc. | [BackoffPolicy](#backoffpolicy) | - | - | + +### BackoffPolicy + +| Name | Description | Type | Default | Version | +| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | ------- | ------- | +| delay | Delay time for another request, in milliseconds | number | 1000 | - | +| multiplier | Specify the delay multiplier, for example, when multiplier is set to 2 and delay is 1 second, the first retry is 1 second, the second is 2 seconds, the third is 4 seconds, and so on | number | 0 | - | +| startQuiver | The initial jitter percentage value of the delay request, ranging from 0-1. When only startQuiver is set, endQuiver defaults to 1. For example, if it is set to 0.5, it will increase the current delay time by 50% to 100% randomly Time, if endQuiver has a value, the delay time will be increased by a random value in the range of startQuiver and endQuiver | number | 0 | - | +| endQuiver | The jitter end percentage value of the delayed request, the range is 0-1, when onlyWhen endQuiver is set, startQuiver defaults to 0. For example, if it is set to 0.5, it will add a random time from 0% to 50% to the current delay time. If startQuiver has a value, the delay time will increase the random value in the range of startQuiver and endQuiver | number | 0 | - | + +### Responsive data + +Inherit all responsive data from [**useRequest**](/api/core-hooks#userequest). + +### Action function + +Inherit all action functions of [**useRequest**](/api/core-hooks#userequest). + +| name | description | function parameters | return value | version | +| ---- | ------------------------------------------------------------------------------------------------------------------ | ------------------- | ------------ | ------- | +| stop | Stop retrying, it is only valid during retrying, and the onFail event will be triggered immediately after stopping | - | - | - | + +### Event + +Inherit all events from [**useRequest**](/api/core-hooks#userequest). + +| Name | Description | Callback Parameters | Version | +| ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | ------- | +| onRetry | Retry event bindings, they will fire after a retry is initiated | Retry event instance [RetriableRetryEvent](#retriableretryevent) | - | +| onFail | Triggered when the request fails. It will be triggered when no more retries are made. For example, when the maximum number of retries is reached, when the retry callback returns false, manually call stop to stop retrying
Note:
1 The .onError event will be triggered every time an error is reported.
2. If there are no retries, onError, onComplete and onFail will be triggered at the same time | Retry event instance [RetriableFailEvent](#retriablefailevent) | - | + +#### RetriableRetryEvent + +Event event instance inherited from alova. + +| Name | Description | Type | Default | Version | +| ---------- | ----------------------------------- | ------ | -------- | ------- | +| retryTimes | current retry times | number | required | - | +| retryDelay | The delay time of this retry, in ms | number | required | - | + +#### RetriableFailEvent + +Event event instance inherited from alova. + +| Name | Description | Type | Default | Version | +| ---------- | ------------------- | ------ | -------- | ------- | +| retryTimes | current retry times | number | required | - | diff --git a/versioned_docs/version-2.x/tutorial/05-strategy/13-useSSE.md b/versioned_docs/version-2.x/tutorial/05-strategy/13-useSSE.md index 77d2f05dc..37eb9ac40 100644 --- a/versioned_docs/version-2.x/tutorial/05-strategy/13-useSSE.md +++ b/versioned_docs/version-2.x/tutorial/05-strategy/13-useSSE.md @@ -1,289 +1,287 @@ ---- -title: request by server-send events -sidebar_position: 120 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -:::info strategy type - -use hook - -::: - -> Before using extension hooks, make sure you are familiar with the basic usage of alova. - -A use hook that can automatically retry a request failure, you can use it for important requests. - -## Features - -- ✨ Simpler and easier-to-use usage. -- ✨ Automatic connection management. - -## Install - - - - -```bash -# npm -npm install @alova/scene-vue --save -#yarn -yarn add @alova/scene-vue - -``` - - - - -```bash -# npm -npm install @alova/scene-react --save -#yarn -yarn add @alova/scene-react - -``` - - - - - -```bash -# npm -npm install @alova/scene-svelte --save -#yarn -yarn add @alova/scene-svelte - -``` - - - - - -## Usage - - - - -```typescript -import { useSSE } from '@alova/scene-vue'; - -const method = (value: string) => alova.Get('/api/source', { param: { key: value } }); -const { data, eventSource, readyState, onMessage, onError, on, send, close } = useSSE(method, { - initialData: 'initial-data' // Data initially in `data` -}); - -// Connect -send('value'); - -console.log(data.value); // Data is updated after receiving an event, by default it is `initialData` - -// Corresponds to the `message` event of `eventsource` -const unbindMessage = onMessage(({ data }) => { - console.log(data); -}); - -const unbindError = onError(({ error }) => { - console.error('sse error', error); - close(); -}); - -// Unbind when needed -unbindMessage(); -unbindError(); -``` - - - - -```typescript -import { useSSE } from '@alova/scene-react'; - -const method = (value: string) => alova.Get('/api/source', { param: { key: value } }); -const { data, eventSource, readyState, onMessage, onError, on, send, close } = useSSE(method, { - initialData: 'initial-data' // Data initially in `data` -}); - -// Connect -send('value'); - -console.log(data); // Data is updated after receiving an event, by default it is `initialData` - -// Corresponds to the `message` event of `eventsource` -const unbindMessage = onMessage(({ data }) => { - console.log(data); -}); - -const unbindError = onError(({ error }) => { - console.error('sse error', error); - close(); -}); - -// Unbind when needed -unbindMessage(); -unbindError(); -``` - - - - - -```typescript -import { useSSE } from '@alova/scene-svelte'; - -const method = (value: string) => alova.Get('/api/source', { param: { key: value } }); -const { data, eventSource, readyState, onMessage, onError, on, send, close } = useSSE(method, { - initialData: 'initial-data' // Data initially in `data` -}); - -// Connect -send('value'); - -console.log(data); // Data is updated after receiving an event, by default it is `initialData` - -// Corresponds to the `message` event of `eventsource` -const unbindMessage = onMessage(({ data }) => { - console.log(data); -}); - -const unbindError = onError(({ error }) => { - console.error('sse error', error); - close(); -}); - -// Unbind when needed -unbindMessage(); -unbindError(); -``` - - - - -:::warning - -Currently, `useSSE` can only connect to one source. This means that when attempting to connect to multiple targets, the previous connection will always be terminated. - -::: - -``` typescript -const { data, eventSource, readyState, onMessage, onError, on, send, close } = useSSE(method); - -send('value1'); -// highlight-start -send('value2'); // This will terminate the previous connection -send('value3'); // This will also terminate the previous connection -// highlight-end -``` - -By default, no request is sent. However, by setting immediate = true, you can skip the manual send step. - -```typescript -const { data, eventSource, readyState, onMessage, onError, on, send, close } = useSSE(method, { - // highlight-start - immediate: true - // highlight-end -}); - -// codes here... -``` - -### Binding Custom Events - -```typescript -const { data, readyState, onMessage, on } = useSSE(method); - -on('event-name', ({ data }) => { - console.log(data); -}); -``` - -### Global Response Interception - -By default, the response data is captured by [the global response interceptors](/tutorial/combine-framework/response). If this is not the desired behavior, you can manually disable it. - -```typescript -const { data, readyState, onMessage, on } = useSSE(method, { - // highlight-start - interceptByGlobalResponded: false // Now the data will not be intercepted by the response interceptor - // highlight-end -}); -``` - -## Type Declaration - -``` typescript -const enum SSEHookReadyState { - CONNECTING = 0, - OPEN = 1, - CLOSED = 2 -}; - -type SSEHookConfig = { - /** - * Passed to new EventSource - */ - withCredentials?: boolean; - - /** - * Whether to be intercepted by the global responded interceptor of Alova instance - * @default true - */ - interceptByGlobalResponded?: boolean; - - /** - * Initial data - */ - initialData?: any; - - /** - * Whether to initiate the request immediately - * @default false - */ - immediate?: boolean; -}; - -type SSEReturnType = { - readyState: ExportedType; - data: ExportedType; - eventSource: ExportedType; - /** - * Manually initiate the request. When `immediate: true` is used, this method is triggered automatically. - * @param sendArgs Request parameters passed to the method - */ - send: (...sendArgs: any[]) => Promise; - /** - * Close the connection - */ - close: () => void; - /** - * Register a callback function for the EventSource 'open' event - * @param callback Callback function - * @returns Function to unregister the callback - */ - onOpen(callback: SSEOnOpenTrigger): () => void; - - /** - * Register a callback function for the EventSource 'message' event - * @param callback Callback function - * @returns Function to unregister the callback - */ - onMessage(callback: SSEOnMessageTrigger): () => void; - - /** - * Register a callback function for the EventSource 'error' event - * @param callback Callback function - * @returns Function to unregister the callback - */ - onError(callback: SSEOnErrorTrigger): () => void; - - /** - * @param eventName Event name, defaults to 'open' | 'error' | 'message' - * @param handler Event handler - */ - on( - eventName: string, - handler: (event: AlovaSSEMessageEvent) => void - ) => () => void; -}; -``` +--- +title: request by server-send events +sidebar_position: 120 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info strategy type + +use hook + +::: + +> Before using extension hooks, make sure you are familiar with the basic usage of alova. + +A use hook that can automatically retry a request failure, you can use it for important requests. + +## Features + +- Simpler and easier-to-use usage. +- Automatic connection management. + +## Install + + + + +```bash +# npm +npm install @alova/scene-vue --save +#yarn +yarn add @alova/scene-vue + +``` + + + + +```bash +# npm +npm install @alova/scene-react --save +#yarn +yarn add @alova/scene-react + +``` + + + + + +```bash +# npm +npm install @alova/scene-svelte --save +#yarn +yarn add @alova/scene-svelte + +``` + + + + +## Usage + + + + +```typescript +import { useSSE } from '@alova/scene-vue'; + +const method = (value: string) => alova.Get('/api/source', { param: { key: value } }); +const { data, eventSource, readyState, onMessage, onError, on, send, close } = useSSE(method, { + initialData: 'initial-data' // Data initially in `data` +}); + +// Connect +send('value'); + +console.log(data.value); // Data is updated after receiving an event, by default it is `initialData` + +// Corresponds to the `message` event of `eventsource` +const unbindMessage = onMessage(({ data }) => { + console.log(data); +}); + +const unbindError = onError(({ error }) => { + console.error('sse error', error); + close(); +}); + +// Unbind when needed +unbindMessage(); +unbindError(); +``` + + + + +```typescript +import { useSSE } from '@alova/scene-react'; + +const method = (value: string) => alova.Get('/api/source', { param: { key: value } }); +const { data, eventSource, readyState, onMessage, onError, on, send, close } = useSSE(method, { + initialData: 'initial-data' // Data initially in `data` +}); + +// Connect +send('value'); + +console.log(data); // Data is updated after receiving an event, by default it is `initialData` + +// Corresponds to the `message` event of `eventsource` +const unbindMessage = onMessage(({ data }) => { + console.log(data); +}); + +const unbindError = onError(({ error }) => { + console.error('sse error', error); + close(); +}); + +// Unbind when needed +unbindMessage(); +unbindError(); +``` + + + + +```typescript +import { useSSE } from '@alova/scene-svelte'; + +const method = (value: string) => alova.Get('/api/source', { param: { key: value } }); +const { data, eventSource, readyState, onMessage, onError, on, send, close } = useSSE(method, { + initialData: 'initial-data' // Data initially in `data` +}); + +// Connect +send('value'); + +console.log(data); // Data is updated after receiving an event, by default it is `initialData` + +// Corresponds to the `message` event of `eventsource` +const unbindMessage = onMessage(({ data }) => { + console.log(data); +}); + +const unbindError = onError(({ error }) => { + console.error('sse error', error); + close(); +}); + +// Unbind when needed +unbindMessage(); +unbindError(); +``` + + + + +:::warning + +Currently, `useSSE` can only connect to one source. This means that when attempting to connect to multiple targets, the previous connection will always be terminated. + +::: + +```typescript +const { data, eventSource, readyState, onMessage, onError, on, send, close } = useSSE(method); + +send('value1'); +// highlight-start +send('value2'); // This will terminate the previous connection +send('value3'); // This will also terminate the previous connection +// highlight-end +``` + +By default, no request is sent. However, by setting immediate = true, you can skip the manual send step. + +```typescript +const { data, eventSource, readyState, onMessage, onError, on, send, close } = useSSE(method, { + // highlight-start + immediate: true + // highlight-end +}); + +// codes here... +``` + +### Binding Custom Events + +```typescript +const { data, readyState, onMessage, on } = useSSE(method); + +on('event-name', ({ data }) => { + console.log(data); +}); +``` + +### Global Response Interception + +By default, the response data is captured by [the global response interceptors](/tutorial/combine-framework/response). If this is not the desired behavior, you can manually disable it. + +```typescript +const { data, readyState, onMessage, on } = useSSE(method, { + // highlight-start + interceptByGlobalResponded: false // Now the data will not be intercepted by the response interceptor + // highlight-end +}); +``` + +## Type Declaration + +```typescript +const enum SSEHookReadyState { + CONNECTING = 0, + OPEN = 1, + CLOSED = 2 +}; + +type SSEHookConfig = { + /** + * Passed to new EventSource + */ + withCredentials?: boolean; + + /** + * Whether to be intercepted by the global responded interceptor of Alova instance + * @default true + */ + interceptByGlobalResponded?: boolean; + + /** + * Initial data + */ + initialData?: any; + + /** + * Whether to initiate the request immediately + * @default false + */ + immediate?: boolean; +}; + +type SSEReturnType = { + readyState: ExportedType; + data: ExportedType; + eventSource: ExportedType; + /** + * Manually initiate the request. When `immediate: true` is used, this method is triggered automatically. + * @param sendArgs Request parameters passed to the method + */ + send: (...sendArgs: any[]) => Promise; + /** + * Close the connection + */ + close: () => void; + /** + * Register a callback function for the EventSource 'open' event + * @param callback Callback function + * @returns Function to unregister the callback + */ + onOpen(callback: SSEOnOpenTrigger): () => void; + + /** + * Register a callback function for the EventSource 'message' event + * @param callback Callback function + * @returns Function to unregister the callback + */ + onMessage(callback: SSEOnMessageTrigger): () => void; + + /** + * Register a callback function for the EventSource 'error' event + * @param callback Callback function + * @returns Function to unregister the callback + */ + onError(callback: SSEOnErrorTrigger): () => void; + + /** + * @param eventName Event name, defaults to 'open' | 'error' | 'message' + * @param handler Event handler + */ + on( + eventName: string, + handler: (event: AlovaSSEMessageEvent) => void + ) => () => void; +}; +``` diff --git a/versioned_docs/version-2.x/tutorial/08-request-adapter/01-alova-mock.md b/versioned_docs/version-2.x/tutorial/08-request-adapter/01-alova-mock.md index e668d17c3..612823839 100644 --- a/versioned_docs/version-2.x/tutorial/08-request-adapter/01-alova-mock.md +++ b/versioned_docs/version-2.x/tutorial/08-request-adapter/01-alova-mock.md @@ -1,331 +1,331 @@ ---- -title: Mock data -sidebar_position: 10 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -This mock plug-in is an alova request adapter. Different from the traditional Proxy form, you can control the scope of use of mock data. You can control the global scope, a group of interface scopes, and even the enabling and use of a certain interface. Disabled, which is very useful in our actual business scenarios. Each iteration will add or modify a set of interfaces. We hope that the previous functions will still follow the developed interfaces, and let the new or modified interfaces Taking the simulation data, at this time, each developer can group the interfaces involved in this iteration into a group, and turn them on or off. - -## Features - -- ✨Works seamlessly with alova -- ✨Arbitrary grouping of simulation requests to control global, group, and individual simulation interface enable and disable -- ✨Works with mockjs -- ✨Do not pollute the production environment - -## Install - - - - -```bash -npm install @alova/mock --save -``` - - - - -```bash -yarn add @alova/mock -``` - - - - -The following is the usage flow. - -## Usage - -### Define the mock interface - -Use `defineMock` to define a set of mock interfaces. You can directly specify the return response data in each mock interface, or specify the response data to be dynamically calculated for the callback function. - -```javascript title=mockGrou1.js -import { defineMock } from '@alova/mock'; - -export default defineMock( - { - // capture get request - '/todo': [1, 2, 3, 4], - - // rest style request - '/todo/{id}': ({ params }) => { - const id = params.id; - // ... - return { - title: '...', - time: '10:00' - }; - }, - - // capture post request - '[POST]/todo': ({ query, data }) => { - // ... - return { success: true }; - }, - - // return more detailed information - '[POST]/todo': ({ query, data }) => { - //... - return { - status: 403, - statusText: 'unknown error', - responseHeaders: { - //... - }, - body: { - success: true - } - }; - }, - - // simulate network error - '[POST]/todo': ({ query, data }) => { - throw new Error('network error'); - }, - - // Add `-` before the key to disable this mock interface - '-[DELETE]/todo/{id}': ({ params }) => { - // ... - return { success: true }; - } - }, - true -); // The second parameter indicates whether to enable this group of mock interfaces, the default is true, and can be specified as false to close -``` - -### Create mock request adapter - -Create a mock request adapter when calling `createAlova`, and pass in the mock interface to complete. - -```javascript -import GlobalFetch from 'alova/GlobalFetch'; -import { createAlovaMockAdapter } from '@alova/mock'; -import mockGroup1 from './mockGroup1'; - -// highlight-start -const mockAdapter = createAlovaMockAdapter([mockGroup1, /** ... */], { - // Global control whether the mock interface is enabled, the default is true - enable: true, - - // Non-mock request adapter, used to send requests when the mock interface is not matched - httpAdapter: GlobalFetch(), - - // mock interface response delay, in milliseconds - delay: 1000, - - // Whether to print mock interface request information - mockRequestLogger: true, - - // Simulation interface callback, data is the returned simulation data, you can use it to construct any object you want and return it to alova - // The following is the default callback function, which is suitable for requesting the adapter using GlobalFetch - // If you are using other request adapters, please customize the return data structure suitable for the adapter in the mock interface callback - onMockResponse: data => new Response(JSON.stringify(data)) -}); -// highlight-end - -export const alovaInst = createAlova({ - baseURL: 'http://xxx', - - // Use the mock request adapter, if you need to switch adapters, please see the following practical suggestions - requestAdapter: mockAdapter, - - statesHook: /** ... */ -}); -``` - -### Paths match mode - -:::info version required - -1.5.0+ - -::: - -By default, the path defined in `defineMock` is the full pathname of a url, see the following code snippet. - -```javascript -const alovaInst = createAlova({ - baseURL: 'https://api.alovajs.org' - //... -}); -alovaInst.Get('/user?id=1').send(); -``` - -When the request path in the example is `https://api.alovajs.org/user?id=1`, its full pathname is `/user`, which can match `/user` in `defineMock`. - -Usually this is enough, but when your baseURL is not just a domain name. - -```javascript -const alovaInst = createAlova({ - baseURL: 'https://api.alovajs.org/v1/subname' - //... -}); -alovaInst.Get('/user?id=1').send(); -``` - -In this example, the request path is `https://api.alovajs.org/v1/subname/user?id=1`, the matching path of the mock is `/v1/subname/user`, and `/ in the baseURL needs to be v1/subname` is also written together, which is slightly redundant when the number of interfaces is large. - -At this point, you can set `matchMode` to `methodurl` in `createAlovaMockAdapter`, it will only match the url defined in the method instance, for example, the above instance will match `/user?id=1` instead of The part in baseURL needs to be written. On the contrary, if the url in the method instance has a get parameter, it also needs to be written in the matching path of `defineMock`, just like `?id=1` here. - -```javascript -createAlovaMockAdapter([mockGroup1 /** ... */], { - //... - // highlight-start - matchMode: 'methodurl' - // highlight-end -}); -``` - -## Practical advice - -### Group interfaces per developer per version - -In the team development scenario, we often only need to simulate some undeveloped interfaces for each version development, and use the test environment interface for the interface of the previous version. At this time, in order to achieve better simulation interface management, you can use The two dimensions, development version and developer, group interfaces. - -For example, there are two developers named _August_, _kevin_, they are developing v1.1 product features, they can manage the mock interface like this. - -```javascript title=August-v1.1.js -import { defineMock } from '@alova/mock'; - -export default defineMock({ - '/todo': [ - /** */ - ], - '[POST]/todo': ({ data }) => { - // ... - // return... - } - // ... -}); -``` - -```javascript title=kevin-v1.1.js -import { defineMock } from '@alova/mock'; - -export default defineMock({ - '[PUT]/todo/add': ({ data }) => { - // ... - // return... - }, - '[DELETE]/todo/remove': ({ data }) => { - // ... - // return... - } - // ... -}); -``` - -```javascript title=request.js -import Augustv1_1 from './August-v1.1'; -import Keevenv1_1 from './kevin-v1.1'; - -const mockAdapter = createAlovaMockAdapter([Augustv1_1, kevinv1_1], { - httpAdapter: GlobalFetch(), - delay: 1000 -}); -export const alovaInst = createAlova({ - baseURL: 'http://xxx', - requestAdapter: mockAdapter - // ... -}); -``` - -### Exclude mock code in production - -The mock data is generally only used in the development environment, and will be switched to the actual interface in the production environment, so this mock code becomes useless in the production environment. At this time, we can exclude this code by judging the environment variables. , you just need to do: - -```javascript -const globalFetch = GlobalFetch(); -const mockAdapter = createAlovaMockAdapter([mockGroup1, /** ... */], { - httpAdapter: globalFetch, - delay: 1000, -}); - -export const alovaInst = createAlova({ - baseURL: 'http://xxx', - - // highlight-start - // In the production environment controlled by environment variables, the mock-related code will not be packaged in - requestAdapter: process.env.NODE_ENV === 'development' ? mockAdapter : globalFetch, - // highlight-end - - statesHook: /** ... */ -}); -``` - -### Use with mockjs - -If you don't want to write the mock data yourself, but use it with a mock js library (such as mockjs), you can do so. - -```javascript -import { defineMock } from '@alova/mock'; -import Mock from 'mockjs'; - -export default defineMock({ - '/api1': Mock.mock({ - 'id|1-10000': 100 - }) -}); -``` - -## Convert mock data - -**@alova/mock** By default, the response data is packaged as a Response instance, and the response header is packaged as a Headers instance by default, which is adapted for `GlobalFetch`, but if you use other request adapters, you need to mock the data Convert to the corresponding format. - -### Convert response data - -You can intercept the mock response data in the `onMockResponse` field and return the transformed response data and response headers. - -> You can also throw an ERROR in onMockResponse to indicate a failure request. - -```javascript -const mockAdapter = createAlovaMockAdapter( - [ - /* mock data */ - ], - { - //... - // highlight-start - onMockResponse(response, request, currentMethod) { - // response is the corresponding data set, which contains status, statusText, responseHeaders, body - // request is the request data, which contains query, params, headers, data - // currentMethod is the method instance of the current request - //... - // Return converted response data and response headers - return { - response: /** response data */, - headers: /** Response headers */ - }; - } - // highlight-end - } -); -``` - -### Convert Error Instance - -You can intercept the error instance in the `onMockError` field and return the converted error message. - -> You can also throw an ERROR in onMockResponse to indicate failure request. - -```javascript -const mockAdapter = createAlovaMockAdapter( - [ - /* mock data */ - ], - { - //... - // highlight-start - onMockError(error, currentMethod) { - // error is an error instance - // currentMethod is the method instance of the current request - //... - // Return the converted error message collection - } - // highlight-end - } -); -``` +--- +title: Mock data +sidebar_position: 10 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +This mock plug-in is an alova request adapter. Different from the traditional Proxy form, you can control the scope of use of mock data. You can control the global scope, a group of interface scopes, and even the enabling and use of a certain interface. Disabled, which is very useful in our actual business scenarios. Each iteration will add or modify a set of interfaces. We hope that the previous functions will still follow the developed interfaces, and let the new or modified interfaces Taking the simulation data, at this time, each developer can group the interfaces involved in this iteration into a group, and turn them on or off. + +## Features + +- Works seamlessly with alova +- Arbitrary grouping of simulation requests to control global, group, and individual simulation interface enable and disable +- Works with mockjs +- Do not pollute the production environment + +## Install + + + + +```bash +npm install @alova/mock --save +``` + + + + +```bash +yarn add @alova/mock +``` + + + + +The following is the usage flow. + +## Usage + +### Define the mock interface + +Use `defineMock` to define a set of mock interfaces. You can directly specify the return response data in each mock interface, or specify the response data to be dynamically calculated for the callback function. + +```javascript title=mockGrou1.js +import { defineMock } from '@alova/mock'; + +export default defineMock( + { + // capture get request + '/todo': [1, 2, 3, 4], + + // rest style request + '/todo/{id}': ({ params }) => { + const id = params.id; + // ... + return { + title: '...', + time: '10:00' + }; + }, + + // capture post request + '[POST]/todo': ({ query, data }) => { + // ... + return { success: true }; + }, + + // return more detailed information + '[POST]/todo': ({ query, data }) => { + //... + return { + status: 403, + statusText: 'unknown error', + responseHeaders: { + //... + }, + body: { + success: true + } + }; + }, + + // simulate network error + '[POST]/todo': ({ query, data }) => { + throw new Error('network error'); + }, + + // Add `-` before the key to disable this mock interface + '-[DELETE]/todo/{id}': ({ params }) => { + // ... + return { success: true }; + } + }, + true +); // The second parameter indicates whether to enable this group of mock interfaces, the default is true, and can be specified as false to close +``` + +### Create mock request adapter + +Create a mock request adapter when calling `createAlova`, and pass in the mock interface to complete. + +```javascript +import GlobalFetch from 'alova/GlobalFetch'; +import { createAlovaMockAdapter } from '@alova/mock'; +import mockGroup1 from './mockGroup1'; + +// highlight-start +const mockAdapter = createAlovaMockAdapter([mockGroup1, /** ... */], { + // Global control whether the mock interface is enabled, the default is true + enable: true, + + // Non-mock request adapter, used to send requests when the mock interface is not matched + httpAdapter: GlobalFetch(), + + // mock interface response delay, in milliseconds + delay: 1000, + + // Whether to print mock interface request information + mockRequestLogger: true, + + // Simulation interface callback, data is the returned simulation data, you can use it to construct any object you want and return it to alova + // The following is the default callback function, which is suitable for requesting the adapter using GlobalFetch + // If you are using other request adapters, please customize the return data structure suitable for the adapter in the mock interface callback + onMockResponse: data => new Response(JSON.stringify(data)) +}); +// highlight-end + +export const alovaInst = createAlova({ + baseURL: 'http://xxx', + + // Use the mock request adapter, if you need to switch adapters, please see the following practical suggestions + requestAdapter: mockAdapter, + + statesHook: /** ... */ +}); +``` + +### Paths match mode + +:::info version required + +1.5.0+ + +::: + +By default, the path defined in `defineMock` is the full pathname of a url, see the following code snippet. + +```javascript +const alovaInst = createAlova({ + baseURL: 'https://api.alovajs.org' + //... +}); +alovaInst.Get('/user?id=1').send(); +``` + +When the request path in the example is `https://api.alovajs.org/user?id=1`, its full pathname is `/user`, which can match `/user` in `defineMock`. + +Usually this is enough, but when your baseURL is not just a domain name. + +```javascript +const alovaInst = createAlova({ + baseURL: 'https://api.alovajs.org/v1/subname' + //... +}); +alovaInst.Get('/user?id=1').send(); +``` + +In this example, the request path is `https://api.alovajs.org/v1/subname/user?id=1`, the matching path of the mock is `/v1/subname/user`, and `/ in the baseURL needs to be v1/subname` is also written together, which is slightly redundant when the number of interfaces is large. + +At this point, you can set `matchMode` to `methodurl` in `createAlovaMockAdapter`, it will only match the url defined in the method instance, for example, the above instance will match `/user?id=1` instead of The part in baseURL needs to be written. On the contrary, if the url in the method instance has a get parameter, it also needs to be written in the matching path of `defineMock`, just like `?id=1` here. + +```javascript +createAlovaMockAdapter([mockGroup1 /** ... */], { + //... + // highlight-start + matchMode: 'methodurl' + // highlight-end +}); +``` + +## Practical advice + +### Group interfaces per developer per version + +In the team development scenario, we often only need to simulate some undeveloped interfaces for each version development, and use the test environment interface for the interface of the previous version. At this time, in order to achieve better simulation interface management, you can use The two dimensions, development version and developer, group interfaces. + +For example, there are two developers named _August_, _kevin_, they are developing v1.1 product features, they can manage the mock interface like this. + +```javascript title=August-v1.1.js +import { defineMock } from '@alova/mock'; + +export default defineMock({ + '/todo': [ + /** */ + ], + '[POST]/todo': ({ data }) => { + // ... + // return... + } + // ... +}); +``` + +```javascript title=kevin-v1.1.js +import { defineMock } from '@alova/mock'; + +export default defineMock({ + '[PUT]/todo/add': ({ data }) => { + // ... + // return... + }, + '[DELETE]/todo/remove': ({ data }) => { + // ... + // return... + } + // ... +}); +``` + +```javascript title=request.js +import Augustv1_1 from './August-v1.1'; +import Keevenv1_1 from './kevin-v1.1'; + +const mockAdapter = createAlovaMockAdapter([Augustv1_1, kevinv1_1], { + httpAdapter: GlobalFetch(), + delay: 1000 +}); +export const alovaInst = createAlova({ + baseURL: 'http://xxx', + requestAdapter: mockAdapter + // ... +}); +``` + +### Exclude mock code in production + +The mock data is generally only used in the development environment, and will be switched to the actual interface in the production environment, so this mock code becomes useless in the production environment. At this time, we can exclude this code by judging the environment variables. , you just need to do: + +```javascript +const globalFetch = GlobalFetch(); +const mockAdapter = createAlovaMockAdapter([mockGroup1, /** ... */], { + httpAdapter: globalFetch, + delay: 1000, +}); + +export const alovaInst = createAlova({ + baseURL: 'http://xxx', + + // highlight-start + // In the production environment controlled by environment variables, the mock-related code will not be packaged in + requestAdapter: process.env.NODE_ENV === 'development' ? mockAdapter : globalFetch, + // highlight-end + + statesHook: /** ... */ +}); +``` + +### Use with mockjs + +If you don't want to write the mock data yourself, but use it with a mock js library (such as mockjs), you can do so. + +```javascript +import { defineMock } from '@alova/mock'; +import Mock from 'mockjs'; + +export default defineMock({ + '/api1': Mock.mock({ + 'id|1-10000': 100 + }) +}); +``` + +## Convert mock data + +**@alova/mock** By default, the response data is packaged as a Response instance, and the response header is packaged as a Headers instance by default, which is adapted for `GlobalFetch`, but if you use other request adapters, you need to mock the data Convert to the corresponding format. + +### Convert response data + +You can intercept the mock response data in the `onMockResponse` field and return the transformed response data and response headers. + +> You can also throw an ERROR in onMockResponse to indicate a failure request. + +```javascript +const mockAdapter = createAlovaMockAdapter( + [ + /* mock data */ + ], + { + //... + // highlight-start + onMockResponse(response, request, currentMethod) { + // response is the corresponding data set, which contains status, statusText, responseHeaders, body + // request is the request data, which contains query, params, headers, data + // currentMethod is the method instance of the current request + //... + // Return converted response data and response headers + return { + response: /** response data */, + headers: /** Response headers */ + }; + } + // highlight-end + } +); +``` + +### Convert Error Instance + +You can intercept the error instance in the `onMockError` field and return the converted error message. + +> You can also throw an ERROR in onMockResponse to indicate failure request. + +```javascript +const mockAdapter = createAlovaMockAdapter( + [ + /* mock data */ + ], + { + //... + // highlight-start + onMockError(error, currentMethod) { + // error is an error instance + // currentMethod is the method instance of the current request + //... + // Return the converted error message collection + } + // highlight-end + } +); +``` From 1f999e25feaa95544ebe5d8a88a4b7934358968e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=83=A1=E9=95=87?= Date: Tue, 18 Jun 2024 18:37:53 +0800 Subject: [PATCH 03/11] docs: basically finished tutorials --- docs/{tutorial/11-others => about}/01-RSM.md | 0 .../11-others => about}/02-comparison.md | 0 docs/{tutorial/11-others => about}/03-Q&A.md | 0 .../11-others => about}/_category_.json | 0 docs/error.md | 5 + .../01-request-adapter}/01-alova-mock.md | 0 .../02-alova-adapter-xhr.md | 0 .../03-alova-adapter-axios.md | 0 .../04-alova-adapter-taro.md | 0 .../05-alova-adapter-uniapp.md | 0 docs/resource/01-request-adapter/README.md | 9 + .../01-request-adapter/_category_.json | 3 + docs/resource/02-storage-adapter/01-psc.md | 68 ++ docs/resource/02-storage-adapter/README.md | 7 + .../02-storage-adapter/_category_.json | 3 + .../03-framework}/01-vue-options.md | 0 .../03-framework}/02-solid.md | 0 .../03-framework}/03-angular.md | 0 .../03-framework}/04-native-mp.md | 0 .../03-framework}/05-preact.md | 0 .../03-framework}/06-qwik.md | 0 .../03-framework}/07-lit.md | 0 .../03-framework}/08-stencil.md | 0 docs/resource/03-framework/README.md | 5 + .../03-framework}/_category_.json | 0 ...gration.md => 09-extension-integration.md} | 0 docs/tutorial/02-getting-started/README.md | 6 +- .../03-client/06-token-authentication.md | 4 +- docs/tutorial/05-cache/02-auto-invalidate.md | 20 + .../01-update-across-components.md | 2 +- docs/tutorial/06-advanced/04-middleware.md | 674 +++++++----------- .../06-advanced/05-custom-method-key.md | 79 +- docs/tutorial/06-advanced/07-cache-logger.md | 120 ++-- .../06-advanced/08-manage-extra-states.md | 396 +++++----- docs/tutorial/06-advanced/09-ssr.md | 448 ++++++------ docs/tutorial/06-advanced/README.md | 4 +- docs/tutorial/08-custom/01-http-adapter.md | 175 +++++ docs/tutorial/08-custom/02-storage-adapter.md | 91 +++ docs/tutorial/08-custom/03-client-strategy.md | 255 +++++++ docs/tutorial/08-custom/04-server-strategy.md | 45 ++ docs/tutorial/08-custom/05-stateshook.md | 122 ++++ docs/tutorial/08-custom/README.md | 31 + docs/tutorial/08-custom/_category_.json | 3 + .../08-request-adapter/_category_.json | 6 - docs/tutorial/09-migration/01-v2-to-v3.md | 6 + docs/tutorial/09-migration/02-from-axios.md | 6 + docs/tutorial/09-migration/_category_.json | 3 + docs/tutorial/10-custom/01-overview.md | 26 - .../10-custom/02-custom-http-adapter.md | 193 ----- .../10-custom/03-custom-storage-adapter.md | 48 -- .../10-custom/04-custom-stateshook.md | 101 --- docs/tutorial/10-custom/_category_.json | 6 - docs/tutorial/11-others/04-future.md | 36 - docs/tutorial/11-others/05-react-native.md | 15 - docs/tutorial/11-others/06-use-in-static.md | 139 ---- .../11-others/07-hide-recommend-tips.md | 46 -- docusaurus.config.ts | 667 +++++++++-------- .../current.json | 82 ++- .../{tutorial/11-others => about}/01-RSM.md | 0 .../11-others => about}/02-comparison.md | 0 .../current/about/03-Q&A.md | 176 +++++ .../current/error.md | 5 + .../01-request-adapter}/01-alova-mock.md | 0 .../02-alova-adapter-xhr.md | 0 .../03-alova-adapter-axios.md | 0 .../04-alova-adapter-taro.md | 0 .../05-alova-adapter-uniapp.md | 0 .../resource/01-request-adapter/README.md | 9 + .../01-request-adapter/_category_.json | 3 + .../resource/02-storage-adapter/01-psc.md | 68 ++ .../resource/02-storage-adapter/README.md | 7 + .../03-framework}/01-vue-options.md | 0 .../03-framework}/02-solid.md | 0 .../03-framework}/03-angular.md | 0 .../03-framework}/04-native-mp.md | 0 .../03-framework}/05-preact.md | 0 .../03-framework}/06-qwik.md | 0 .../03-framework}/07-lit.md | 0 .../03-framework}/08-stencil.md | 0 .../current/resource/03-framework/README.md | 5 + ...gration.md => 09-extension-integration.md} | 0 .../tutorial/02-getting-started/README.md | 6 +- .../03-client/06-token-authentication.md | 4 +- .../tutorial/05-cache/02-auto-invalidate.md | 20 + .../01-update-across-components.md | 2 +- .../06-advanced/05-custom-method-key.md | 79 +- .../tutorial/06-advanced/07-cache-logger.md | 120 ++-- .../06-advanced/08-manage-extra-states.md | 395 +++++----- .../current/tutorial/06-advanced/09-ssr.md | 448 ++++++------ .../01-http-adapter.md} | 368 +++++----- .../02-storage-adapter.md} | 139 ++-- .../tutorial/08-custom/03-client-strategy.md | 256 +++++++ .../tutorial/08-custom/04-server-strategy.md | 45 ++ .../tutorial/08-custom/05-stateshook.md | 122 ++++ .../current/tutorial/08-custom/README.md | 31 + .../tutorial/08-custom/_category_.json | 3 + .../08-request-adapter/_category_.json | 6 - .../tutorial/09-migration/01-v2-to-v3.md | 6 + .../tutorial/09-migration/02-from-axios.md | 6 + .../current/tutorial/10-custom/01-overview.md | 26 - .../10-custom/04-custom-stateshook.md | 101 --- .../tutorial/10-custom/_category_.json | 6 - .../current/tutorial/11-others/03-Q&A.md | 24 - .../current/tutorial/11-others/04-future.md | 36 - .../tutorial/11-others/05-react-native.md | 15 - .../tutorial/11-others/06-use-in-static.md | 139 ---- .../11-others/07-hide-recommend-tips.md | 46 -- .../tutorial/11-others/_category_.json | 6 - .../docusaurus-theme-classic/navbar.json | 88 +-- sidebars.js | 58 +- 110 files changed, 3698 insertions(+), 3131 deletions(-) rename docs/{tutorial/11-others => about}/01-RSM.md (100%) rename docs/{tutorial/11-others => about}/02-comparison.md (100%) rename docs/{tutorial/11-others => about}/03-Q&A.md (100%) rename docs/{tutorial/11-others => about}/_category_.json (100%) create mode 100644 docs/error.md rename docs/{tutorial/08-request-adapter => resource/01-request-adapter}/01-alova-mock.md (100%) rename docs/{tutorial/08-request-adapter => resource/01-request-adapter}/02-alova-adapter-xhr.md (100%) rename docs/{tutorial/08-request-adapter => resource/01-request-adapter}/03-alova-adapter-axios.md (100%) rename docs/{tutorial/08-request-adapter => resource/01-request-adapter}/04-alova-adapter-taro.md (100%) rename docs/{tutorial/08-request-adapter => resource/01-request-adapter}/05-alova-adapter-uniapp.md (100%) create mode 100644 docs/resource/01-request-adapter/README.md create mode 100644 docs/resource/01-request-adapter/_category_.json create mode 100644 docs/resource/02-storage-adapter/01-psc.md create mode 100644 docs/resource/02-storage-adapter/README.md create mode 100644 docs/resource/02-storage-adapter/_category_.json rename docs/{tutorial/09-framework => resource/03-framework}/01-vue-options.md (100%) rename docs/{tutorial/09-framework => resource/03-framework}/02-solid.md (100%) rename docs/{tutorial/09-framework => resource/03-framework}/03-angular.md (100%) rename docs/{tutorial/09-framework => resource/03-framework}/04-native-mp.md (100%) rename docs/{tutorial/09-framework => resource/03-framework}/05-preact.md (100%) rename docs/{tutorial/09-framework => resource/03-framework}/06-qwik.md (100%) rename docs/{tutorial/09-framework => resource/03-framework}/07-lit.md (100%) rename docs/{tutorial/09-framework => resource/03-framework}/08-stencil.md (100%) create mode 100644 docs/resource/03-framework/README.md rename docs/{tutorial/09-framework => resource/03-framework}/_category_.json (100%) rename docs/tutorial/02-getting-started/{09-plugin-integration.md => 09-extension-integration.md} (100%) create mode 100644 docs/tutorial/08-custom/01-http-adapter.md create mode 100644 docs/tutorial/08-custom/02-storage-adapter.md create mode 100644 docs/tutorial/08-custom/03-client-strategy.md create mode 100644 docs/tutorial/08-custom/04-server-strategy.md create mode 100644 docs/tutorial/08-custom/05-stateshook.md create mode 100644 docs/tutorial/08-custom/README.md create mode 100644 docs/tutorial/08-custom/_category_.json delete mode 100644 docs/tutorial/08-request-adapter/_category_.json create mode 100644 docs/tutorial/09-migration/01-v2-to-v3.md create mode 100644 docs/tutorial/09-migration/02-from-axios.md create mode 100644 docs/tutorial/09-migration/_category_.json delete mode 100644 docs/tutorial/10-custom/01-overview.md delete mode 100644 docs/tutorial/10-custom/02-custom-http-adapter.md delete mode 100644 docs/tutorial/10-custom/03-custom-storage-adapter.md delete mode 100644 docs/tutorial/10-custom/04-custom-stateshook.md delete mode 100644 docs/tutorial/10-custom/_category_.json delete mode 100644 docs/tutorial/11-others/04-future.md delete mode 100644 docs/tutorial/11-others/05-react-native.md delete mode 100644 docs/tutorial/11-others/06-use-in-static.md delete mode 100644 docs/tutorial/11-others/07-hide-recommend-tips.md rename i18n/zh-CN/docusaurus-plugin-content-docs/current/{tutorial/11-others => about}/01-RSM.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/{tutorial/11-others => about}/02-comparison.md (100%) create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/about/03-Q&A.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/error.md rename i18n/zh-CN/docusaurus-plugin-content-docs/current/{tutorial/08-request-adapter => resource/01-request-adapter}/01-alova-mock.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/{tutorial/08-request-adapter => resource/01-request-adapter}/02-alova-adapter-xhr.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/{tutorial/08-request-adapter => resource/01-request-adapter}/03-alova-adapter-axios.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/{tutorial/08-request-adapter => resource/01-request-adapter}/04-alova-adapter-taro.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/{tutorial/08-request-adapter => resource/01-request-adapter}/05-alova-adapter-uniapp.md (100%) create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/README.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/_category_.json create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/02-storage-adapter/01-psc.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/02-storage-adapter/README.md rename i18n/zh-CN/docusaurus-plugin-content-docs/current/{tutorial/09-framework => resource/03-framework}/01-vue-options.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/{tutorial/09-framework => resource/03-framework}/02-solid.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/{tutorial/09-framework => resource/03-framework}/03-angular.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/{tutorial/09-framework => resource/03-framework}/04-native-mp.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/{tutorial/09-framework => resource/03-framework}/05-preact.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/{tutorial/09-framework => resource/03-framework}/06-qwik.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/{tutorial/09-framework => resource/03-framework}/07-lit.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/{tutorial/09-framework => resource/03-framework}/08-stencil.md (100%) create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/README.md rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/{09-plugin-integration.md => 09-extension-integration.md} (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{10-custom/02-custom-http-adapter.md => 08-custom/01-http-adapter.md} (73%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{10-custom/03-custom-storage-adapter.md => 08-custom/02-storage-adapter.md} (57%) create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-custom/03-client-strategy.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-custom/04-server-strategy.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-custom/05-stateshook.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-custom/README.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-custom/_category_.json delete mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-request-adapter/_category_.json create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/09-migration/01-v2-to-v3.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/09-migration/02-from-axios.md delete mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/10-custom/01-overview.md delete mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/10-custom/04-custom-stateshook.md delete mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/10-custom/_category_.json delete mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/11-others/03-Q&A.md delete mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/11-others/04-future.md delete mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/11-others/05-react-native.md delete mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/11-others/06-use-in-static.md delete mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/11-others/07-hide-recommend-tips.md delete mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/11-others/_category_.json diff --git a/docs/tutorial/11-others/01-RSM.md b/docs/about/01-RSM.md similarity index 100% rename from docs/tutorial/11-others/01-RSM.md rename to docs/about/01-RSM.md diff --git a/docs/tutorial/11-others/02-comparison.md b/docs/about/02-comparison.md similarity index 100% rename from docs/tutorial/11-others/02-comparison.md rename to docs/about/02-comparison.md diff --git a/docs/tutorial/11-others/03-Q&A.md b/docs/about/03-Q&A.md similarity index 100% rename from docs/tutorial/11-others/03-Q&A.md rename to docs/about/03-Q&A.md diff --git a/docs/tutorial/11-others/_category_.json b/docs/about/_category_.json similarity index 100% rename from docs/tutorial/11-others/_category_.json rename to docs/about/_category_.json diff --git a/docs/error.md b/docs/error.md new file mode 100644 index 000000000..da4f4fb7f --- /dev/null +++ b/docs/error.md @@ -0,0 +1,5 @@ +--- +title: Error Reference +--- + +Coming soon... diff --git a/docs/tutorial/08-request-adapter/01-alova-mock.md b/docs/resource/01-request-adapter/01-alova-mock.md similarity index 100% rename from docs/tutorial/08-request-adapter/01-alova-mock.md rename to docs/resource/01-request-adapter/01-alova-mock.md diff --git a/docs/tutorial/08-request-adapter/02-alova-adapter-xhr.md b/docs/resource/01-request-adapter/02-alova-adapter-xhr.md similarity index 100% rename from docs/tutorial/08-request-adapter/02-alova-adapter-xhr.md rename to docs/resource/01-request-adapter/02-alova-adapter-xhr.md diff --git a/docs/tutorial/08-request-adapter/03-alova-adapter-axios.md b/docs/resource/01-request-adapter/03-alova-adapter-axios.md similarity index 100% rename from docs/tutorial/08-request-adapter/03-alova-adapter-axios.md rename to docs/resource/01-request-adapter/03-alova-adapter-axios.md diff --git a/docs/tutorial/08-request-adapter/04-alova-adapter-taro.md b/docs/resource/01-request-adapter/04-alova-adapter-taro.md similarity index 100% rename from docs/tutorial/08-request-adapter/04-alova-adapter-taro.md rename to docs/resource/01-request-adapter/04-alova-adapter-taro.md diff --git a/docs/tutorial/08-request-adapter/05-alova-adapter-uniapp.md b/docs/resource/01-request-adapter/05-alova-adapter-uniapp.md similarity index 100% rename from docs/tutorial/08-request-adapter/05-alova-adapter-uniapp.md rename to docs/resource/01-request-adapter/05-alova-adapter-uniapp.md diff --git a/docs/resource/01-request-adapter/README.md b/docs/resource/01-request-adapter/README.md new file mode 100644 index 000000000..f50570493 --- /dev/null +++ b/docs/resource/01-request-adapter/README.md @@ -0,0 +1,9 @@ +--- +title: Request Adapter +--- + +import DocCardList from '@theme/DocCardList'; + +We provide a variety of request adapters. + + diff --git a/docs/resource/01-request-adapter/_category_.json b/docs/resource/01-request-adapter/_category_.json new file mode 100644 index 000000000..323e503e1 --- /dev/null +++ b/docs/resource/01-request-adapter/_category_.json @@ -0,0 +1,3 @@ +{ + "label": "Request Adapter" +} diff --git a/docs/resource/02-storage-adapter/01-psc.md b/docs/resource/02-storage-adapter/01-psc.md new file mode 100644 index 000000000..2e60d4f2a --- /dev/null +++ b/docs/resource/02-storage-adapter/01-psc.md @@ -0,0 +1,68 @@ +--- +title: Process Shared Adapter +sidebar_position: 10 +--- + +Process shared storage adapter allows you to share cache in multi-process mode, which can be used in Nodejs and Electron. + +## Use in Node.js + +In Node.js environment, you can use `NodeSyncAdapter` and `createNodeSharedCacheSynchronizer` to achieve cache synchronization between processes. + +1. Set up the synchronizer in the master process: + +```javascript +const { createPSCSynchronizer, NodeSyncAdapter, is } = require('@alova/psc'); +const cluster = require('cluster'); + +if (cluster.isMaster) { + createPSCSynchronizer(NodeSyncAdapter()); +} else { + // fork worker processes +} +``` + +2. Use the adapter in the child process: + +```javascript +const { createPSCAdapter, NodeSyncAdapter, ExplictCacheAdapter } = require('@alova/psc'); + +const pscAdapter = createPSCAdapter(NodeSyncAdapter(), new ExplictCacheAdapter()); +createAlova({ + // ... + l1Cache: pscAdapter +}); +``` + +> You can also use [lru-cache](https://www.npmjs.com/package/lru-cache) as a cache adapter. + +## Use in Electron + +In Electron environment, you need to use `ElectronSyncAdapter` and `createPSCSynchronizer` to achieve cache synchronization between the main process and the rendering process. + +1. Set up the synchronizer in the main process: + +```javascript +import { createPSCSynchronizer, ElectronSyncAdapter } from 'your-module-name'; + +function setupMainProcess() { + createPSCSynchronizer(ElectronSyncAdapter()); +} + +setupMainProcess(); +``` + +2. Use the adapter in the renderer process: + +```javascript +import { createPSCAdapter, ElectronSyncAdapter } from 'your-module-name'; +import { ipcRenderer } from 'electron'; + +const pscAdapter = createPSCAdapter(ElectronSyncAdapter(ipcRenderer)); +createAlova({ + // ... + l1Cache: pscAdapter +}); +``` + +This way you can keep the cache in sync between Electron's main process and renderer process. diff --git a/docs/resource/02-storage-adapter/README.md b/docs/resource/02-storage-adapter/README.md new file mode 100644 index 000000000..2bdc97622 --- /dev/null +++ b/docs/resource/02-storage-adapter/README.md @@ -0,0 +1,7 @@ +--- +title: 存储适配器 +--- + +import DocCardList from '@theme/DocCardList'; + + diff --git a/docs/resource/02-storage-adapter/_category_.json b/docs/resource/02-storage-adapter/_category_.json new file mode 100644 index 000000000..0d4ccc0c3 --- /dev/null +++ b/docs/resource/02-storage-adapter/_category_.json @@ -0,0 +1,3 @@ +{ + "label": "Storage Adapter" +} diff --git a/docs/tutorial/09-framework/01-vue-options.md b/docs/resource/03-framework/01-vue-options.md similarity index 100% rename from docs/tutorial/09-framework/01-vue-options.md rename to docs/resource/03-framework/01-vue-options.md diff --git a/docs/tutorial/09-framework/02-solid.md b/docs/resource/03-framework/02-solid.md similarity index 100% rename from docs/tutorial/09-framework/02-solid.md rename to docs/resource/03-framework/02-solid.md diff --git a/docs/tutorial/09-framework/03-angular.md b/docs/resource/03-framework/03-angular.md similarity index 100% rename from docs/tutorial/09-framework/03-angular.md rename to docs/resource/03-framework/03-angular.md diff --git a/docs/tutorial/09-framework/04-native-mp.md b/docs/resource/03-framework/04-native-mp.md similarity index 100% rename from docs/tutorial/09-framework/04-native-mp.md rename to docs/resource/03-framework/04-native-mp.md diff --git a/docs/tutorial/09-framework/05-preact.md b/docs/resource/03-framework/05-preact.md similarity index 100% rename from docs/tutorial/09-framework/05-preact.md rename to docs/resource/03-framework/05-preact.md diff --git a/docs/tutorial/09-framework/06-qwik.md b/docs/resource/03-framework/06-qwik.md similarity index 100% rename from docs/tutorial/09-framework/06-qwik.md rename to docs/resource/03-framework/06-qwik.md diff --git a/docs/tutorial/09-framework/07-lit.md b/docs/resource/03-framework/07-lit.md similarity index 100% rename from docs/tutorial/09-framework/07-lit.md rename to docs/resource/03-framework/07-lit.md diff --git a/docs/tutorial/09-framework/08-stencil.md b/docs/resource/03-framework/08-stencil.md similarity index 100% rename from docs/tutorial/09-framework/08-stencil.md rename to docs/resource/03-framework/08-stencil.md diff --git a/docs/resource/03-framework/README.md b/docs/resource/03-framework/README.md new file mode 100644 index 000000000..1609bc2fc --- /dev/null +++ b/docs/resource/03-framework/README.md @@ -0,0 +1,5 @@ +--- +title: overview +--- + +sdfasfasdf diff --git a/docs/tutorial/09-framework/_category_.json b/docs/resource/03-framework/_category_.json similarity index 100% rename from docs/tutorial/09-framework/_category_.json rename to docs/resource/03-framework/_category_.json diff --git a/docs/tutorial/02-getting-started/09-plugin-integration.md b/docs/tutorial/02-getting-started/09-extension-integration.md similarity index 100% rename from docs/tutorial/02-getting-started/09-plugin-integration.md rename to docs/tutorial/02-getting-started/09-extension-integration.md diff --git a/docs/tutorial/02-getting-started/README.md b/docs/tutorial/02-getting-started/README.md index 62ef3324e..d28fedb28 100644 --- a/docs/tutorial/02-getting-started/README.md +++ b/docs/tutorial/02-getting-started/README.md @@ -38,9 +38,11 @@ alova provides 10+ request strategy modules based on the [RSM](/tutorial/others/ ### Alova editor extension -Using the alova plugin in vscode can help you automatically generate request code with complete API document annotations and response types. In the past, you needed to query the API documentation first and constantly switch between the API documentation and the editor to write request code. After using the alova plugin, you no longer need to leave the editor, and you can directly use the API while checking in the editor to experience a different API usage experience. +Using the alova extension in vscode can help you automatically generate request codes with complete API document annotations and response types. Whether it is a ts project or a js project, you can get complete interface queries, interface details, and smart prompts for response data types. -> For a detailed introduction to the alova plugin, please refer to [Integrated IDE plugin](/tutorial/getting-started/plugin-integration). +This extension also optimizes the API consumption process and gives you a different API usage experience. In the past, you needed to query the API document first and constantly switch between the API document and the editor to write request codes. With the alova plug-in, you no longer need to leave the editor and can directly use the API in the editor while checking. + +> For a detailed introduction to the alova plug-in, please refer to [Integrated Editor Extension](/tutorial/getting-started/extension-integration). ## Is there any difference? diff --git a/docs/tutorial/03-client/06-token-authentication.md b/docs/tutorial/03-client/06-token-authentication.md index 922ff7bf0..bc692b559 100644 --- a/docs/tutorial/03-client/06-token-authentication.md +++ b/docs/tutorial/03-client/06-token-authentication.md @@ -6,9 +6,9 @@ sidebar_position: 60 import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -:::info +:::info strategy type -Policy type: Interceptor +Interceptor Version requirements: v1.3.0+ diff --git a/docs/tutorial/05-cache/02-auto-invalidate.md b/docs/tutorial/05-cache/02-auto-invalidate.md index 2b9b65e2d..495505970 100644 --- a/docs/tutorial/05-cache/02-auto-invalidate.md +++ b/docs/tutorial/05-cache/02-auto-invalidate.md @@ -79,6 +79,26 @@ alova.Get('/todo/1', { }); ``` +## Automatic invalidation scope + +Automatic invalidation will search for caches under all alova instances by default. Too many invalidation targets may lead to poor performance. If you want to control the scope of automatic invalidation or turn it off, you can set it as follows. + +```js +import { globalConfig } from 'alova'; + +globalConfig({ + /** + * Automatic hit cache switch. + * There are three options here: + * - global: invalidate cache across alova instances. + * - self: only invalidate caches from the same alova instance. + * - close: no longer automatically invalidate cache. + * Defaults to 'global' + */ + autoHitCache: 'self' +}); +``` + ## hitSource data type ```typescript diff --git a/docs/tutorial/06-advanced/01-update-across-components.md b/docs/tutorial/06-advanced/01-update-across-components.md index 6d083b436..a176d5f2c 100644 --- a/docs/tutorial/06-advanced/01-update-across-components.md +++ b/docs/tutorial/06-advanced/01-update-across-components.md @@ -5,7 +5,7 @@ sidebar_position: 20 :::info usage scope -client useHook +Client useHook ::: diff --git a/docs/tutorial/06-advanced/04-middleware.md b/docs/tutorial/06-advanced/04-middleware.md index 2ffa7b652..8924181cd 100644 --- a/docs/tutorial/06-advanced/04-middleware.md +++ b/docs/tutorial/06-advanced/04-middleware.md @@ -1,405 +1,269 @@ ---- -title: Middleware -sidebar_position: 40 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -Request middleware is an asynchronous function. it provides a powerful ability to control almost all behaviors of a request. If you just use alova, then you probably don't need to use request middleware, because it is mainly used to complete custom request strategies, no matter simple or complex request strategies, you may use it, let's look at it next What magical powers does it have. - -## Middleware function - -Request middleware is an async function, You can define request middleware in `useRequest`, `useWatcher`, `useFetcher`. The following is a simple request middleware, which prints some information before and after the request without changing any request behavior. - -```javascript -useRequest(todoList, { - async middleware(_, next) { - console.log('before request'); - await next(); - console.log('after request'); - } -}); -``` - -Here are a few things you need to know about the `next` function call. This function is also an asynchronous function. Calling it can continue to send requests. At this time, the _loading_ state will be set to true, and then the request will be sent. The return value of next is a Promise instance with the response data, you can manipulate the return value in the middleware function. - -## Control response data - -The return value of the middleware function will be used as the response data of this request to participate in subsequent processing. If the middleware does not return any data but calls `next`, the response data of this request will be used for subsequent processing. - -```javascript -// The modified result will be used as the response data -useRequest(todoList, { - async middleware(_, next) { - const result = await next(); - result.code = 500; - return result; - } -}); - -// Will participate in subsequent processing with the response data of this request -useRequest(todoList, { - async middleware(_, next) { - await next(); - } -}); - -// will respond with the string abc -useRequest(todoList, { - async middleware(_, next) { - await next(); - return 'abc'; - } -}); -``` - -There is also a special case here. When `next` is not called and there is no return value, subsequent processing will not be performed, which means that _onSuccess_, _onError_, _onComplete_ response events will not be triggered. - -```javascript -useRequest(todoList, { - async middleware() {} -}); -``` - -## change request - -Sometimes you want to change the request. At this time, you can specify another method instance in `next`, and the information in this method will be requested when sending the request. At the same time, you can also set whether to force the request through `next` Penetrating the cache is also very simple. - -```javascript -useRequest(todoList, { - async middleware(_, next) { - await next({ - // Change the requested method instance - method: newMethodInstance, - - // Whether to force the request this time - force: true - }); - } -}); -``` - -## Control errors - -### Catch errors - -In the middleware, you can capture the request error generated in `next`, after capturing, the global `onError` hook will no longer be triggered. - -```javascript -useRequest(todoList, { - async middleware(_, next) { - try { - await next(); - } catch (e) { - console.error('Error caught', e); - } - } -}); -``` - -### Throw an error - -Of course, you can also throw a custom error in the middleware, even if the request is normal, it will enter the request error process. - -```javascript -// No request is sent, and global and request-level onError will be triggered at the same time. If the request is sent through `method.send`, the promise instance of rejection will be returned -useRequest(todoList, { - async middleware(_, next) { - throw new Error('error on before request'); - await next(); - } -}); - -// After request is success, global and request-level onError will be triggered at the same time. If the request is sent through `method.send`, the promise instance of rejection will be returned -useRequest(todoList, { - async middleware(_, next) { - await next(); - throw new Error('error on after request'); - } -}); -``` - -## Control response delay - -In the middleware, we can delay the response or respond in advance. In the case of advance, although the response data cannot be obtained, some other data can be returned as the response data to participate in subsequent processing. - -```javascript -// Delay response for 1 second -useRequest(todoList, { - async middleware(_, next) { - await new Promise(resolve => { - setTimeout(resolve, 1000); - }); - return next(); - } -}); - -// Respond immediately and use the string abc as the response data -useRequest(todoList, { - async middleware(_, next) { - return 'abc'; - } -}); -``` - -## More than that - -**So far, all we have mentioned is the use of the second parameter `next` of the middleware, so what is the first parameter for? ** - -The first parameter of the middleware contains some information about this request, as well as the control functions for the status and events returned in useHook such as `loading`, `data` and `onSuccess`. Let's move on! - -## Included request information - - - - -The following is the request information contained in the middleware of useRequest and useWatcher - -```javascript -async function alovaFrontMiddleware(context, next) { - // The method instance of this request - context.method; - - // The parameter array sent by the send function, the default is [] - context.sendArgs; - - // The cache data hit by this request - context.cachedResponse; - - // configuration collection of useHook - context.config; - - // The various states returned by useHook, including the following attributes - // loading, data, error, downloading, uploading, and additional states managed by managedStates - context.frontStates; - //... -} -``` - - - - -The following is the request information contained in the middleware of useFetcher - -```javascript -async function alovaFetcherMiddleware(context, next) { - // The method instance of this request - context.method; - - // The parameter group passed in by the fetch of useFetcher, the default is [] - context.fetchArgs; - - // The cache data hit by this request - context.cachedResponse; - - // configuration collection of useHook - context.config; - - // The various states returned by useHook, including the following attributes - // fetching, error, downloading, uploading - context.fetchStates; - //... -} -``` - - - - -Next, let's take a look at what controls are available. - -## Modify responsive data - -Use `context.update` to modify reactive data. - - - - -```javascript -async function alovaFrontMiddleware(context, next) { - context.update({ - // Modify the loading status to true in advance - loading: true, - - // Modify the data value, such as setting custom initialization data - data: { - /* ... */ - } - }); - //... -} -``` - - - - -```javascript -async function alovaFetcherMiddleware(context, next) { - context.update({ - // Modify the fetching status to true in advance - fetching: true, - - // Modify the value of error - error: new Error('custom midleware error') - }); - //... -} -``` - - - - -## Decorate events - -You can also decorate _onSuccess_, _onError_, _onComplete_ callback functions in middleware to make them richer, such as changing the parameters of the callback function, or receiving the return value of the callback function to achieve more functions. - -You can use `decorateSuccess`, `decorateError`, `decorateComplete` functions to decorate callback functions. The following takes the success callback as an example, which is decorated in 3 places: - -1. Added `custom` attribute to event object; -2. Added a second parameter to the success callback function, the value is `extra data`; -3. Receive the value of the second success callback function and print it; - -```javascript -const { onSuccess } = useRequest(todoList, { - //... - async middleware(context, next) { - // Decorate the successful callback function, the following function parameters are explained: - // handler: bound callback function - // event: the event object corresponding to the callback function - // index: The subscript of the callback function, indicating which callback function is currently being executed - // length: the number of callback functions bound - context.decorateSuccess((handler, event, index, length) => { - event.custom = 1; - const received = handler(event, 'extra data'); - if (index === 1) { - console.log(`received the return value of ${index + 1} callback function:`, received); - // [Print information] Received the return value of the second callback function: I'm second handler - } - }); - //... - } -}); -onSuccess((event, extra) => { - console.log(event.custom); // 1 - console.log(extra); // extra data -}); -onSuccess((event, extra) => { - return "I'm second handler"; -}); -``` - -The usage of `decorateError`, `decorateComplete` is the same as `decorateSuccess`. - -## Abort or repeat send request - -In the middleware, you can also receive `abort` and `send` functions returned by use hooks (`fetch` in useFetcher), and you can also send multiple requests when triggering a request intent. - -A typical usage example is request retry. After sending a request, if the request fails, it will automatically request again according to a certain strategy, and `onSuccess` will be triggered after the retry is successful. The following is a sample code for a simple request retry. - - - - -```javascript -async function alovaFrontMiddleware(context, next) { - return next().catch(error => { - if (needRetry) { - setTimeout(() => { - context.send(...context.sendArgs); - }, retryDelay); - } - return Promise.reject(error); - }); -} -``` - - - - -```javascript -async function alovaFetcherMiddleware(context, next) { - return next().catch(error => { - if (needRetry) { - setTimeout(() => { - context.fetch(context.method, ...context.fetchArgs); - }, retryDelay); - } - return Promise.reject(error); - }); -} -``` - - - - -If you need to abort the request inside the middleware, you can call `context.abort()`. - -## Controlled loading state - -In the above content, we know that you can customize and modify the responsive data through `context.update`, but when you modify the loading status value (`loading` or `fetching`), it will be hindered, because in normal circumstances Next, the loading status value will be automatically set to true when `next` is called, and false will be automatically set in the response process, which will overwrite the loading status value modified by `context.update`, at this time we can turn on the controlled loading status , after it is turned on, the `next` function and the response process will no longer modify the loading status value, but we have full control over it. - -Let's take request retry as an example. We hope that the loading status will remain true after the request is retried until the request ends. - - - - -In the middleware of useRequest and useWatcher, use `context.controlLoading` to enable custom control loading status. - -```javascript -async function alovaFrontMiddleware(context, next) { - context.controlLoading(); - - // Set to true when the request starts - context.update({ loading: true }); - return next() - .then(value => { - // set to false after successful request - context.update({ loading: false }); - return value; - }) - .catch(error => { - if (needRetry) { - setTimeout(() => { - context.send(...context.sendArgs); - }, retryDelay); - } else { - // Also set to false when not retrying again - context.update({ loading: false }); - } - return Promise.reject(error); - }); -} -``` - - - - -In the middleware of useFetching, use `context.controlFetching` to enable custom control loading state. - -```javascript -async function alovaFetcherMiddleware(context, next) { - context.controlFetching(); - - // Set to true when the request starts - context.update({ fetching: true }); - return next() - .then(value => { - // set to false after successful request - context.update({ fetching: false }); - return value; - }) - .catch(error => { - if (needRetry) { - setTimeout(() => { - context.fetch(context.method, ...context.fetchArgs); - }, retryDelay); - } else { - // Also set to false when not retrying again - context.update({ fetching: false }); - } - return Promise.reject(error); - }); -} -``` - - - +--- +title: Request Middleware +sidebar_position: 40 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info Usage scope + +Client useHook + +::: + +You can set request middleware for all useHooks to freely control request behavior, providing powerful capabilities that can control almost all behaviors of a request. Whether it is a simple or complex request strategy, you may use it. Next, let's see what it can do. + +## Middleware function + +Request middleware is an asynchronous function. The following is a simple request middleware that prints some information before and after the request, without changing any request behavior. + +```javascript +useRequest(todoList, { + async middleware(_, ​​next) { + console.log('before request'); + await next(); + console.log('after requeste'); + } +}); +``` + +The `next` function is an asynchronous function. Calling it will continue to send requests. At this time, the _loading_ state will be set to true, and then the request will be sent. The return value of next is a Promise instance with response data. You can manipulate the return value in the middleware function. + +## Ignore requests + +When you don't want to make a request, you can ignore the request by not calling `next`, as if the request has never been made. For example, do not make a request when a listening field changes in `useWatcher`. + +```js +useWatcher(() => todoList(), [state1], { + middleware: async (_, next) => { + if (state1 === 'a') { + return next(); + } + } +}); +``` + +## Control response data + +The return value of the middleware function will be used as the response data of this request for subsequent processing. If the middleware does not return any data but calls `next`, the response data of this request will be used for subsequent processing. + +```javascript +// Convert response data and return +useRequest(todoList, { + async middleware(_, ​​next) { + const result = await next(); + result.code = 500; + return result; + } +}); + +// The response data of this request will be used for subsequent processing +useRequest(todoList, { + async middleware(_, ​​next) { + await next(); + } +}); + +// The string abc will be used as the response data +useRequest(todoList, { + async middleware(_, ​​next) { + await next(); + return 'abc'; + } +}); +``` + +## Change request + +Sometimes you want to change the request. At this time, you can specify another method instance in `next`. When sending the request, the information in this method will be requested. At the same time, you can also use `next` to set whether to force the request to penetrate the cache. This is also very simple. + +```javascript +useRequest(todoList, { + async middleware(_, ​​next) { + await next({ + // Change the method instance of the request + method: newMethodInstance, + + // Force the request this time + force: true + }); + } +}); +``` + +## Control errors + +### Capture errors + +In the middleware, you can capture the request error generated in `next`. After capturing, the global `onError` hook will no longer be triggered. + +```javascript +useRequest(todoList, { + async middleware(_, ​​next) { + try { + await next(); + } catch (e) { + console.error('Caught error', e); + } + } +}); +``` + +### Throw an error + +Of course, you can also throw a custom error in the middleware, and even if the request is normal, it will enter the request error process. + +```javascript +// No request is sent, and global and request-level onError will be triggered. If the request is sent through `method.send`, a reject promise instance will be returned +useRequest(todoList, { + async middleware(_, ​​next) { + throw new Error('error on before request'); + await next(); + } +}); + +// After the request is successful, global and request-level onError will be triggered. If the request is sent through `method.send`, a reject promise instance will be returned +useRequest(todoList, { + async middleware(_, ​​next) { + await next(); + throw new Error('error on after request'); + } +}); +``` + +## Control response delay + +In the middleware, we can delay the response or respond in advance. In the case of early response, although the response data cannot be obtained, some other data can be returned as response data to participate in subsequent processing. + +```javascript +// Delay response for 1 second +useRequest(todoList, { + async middleware(_, ​​next) { + await new Promise(resolve => { + setTimeout(resolve, 1000); + }); + return next(); + } +}); + +// Respond immediately and use string abc as response data +useRequest(todoList, { + async middleware(_, ​​next) { + return 'abc'; + } +}); +``` + +## More than that + +**So far, we have mentioned the use of the second parameter `next` of the middleware, so what does the first parameter do? ** + +The first parameter of the middleware contains some information about this request, as well as control functions for the status and events returned in useHook such as `loading`, `data` and `onSuccess`. Let's continue to look down! + +## Request information included + +```javascript +async function middleware(context, next) { + // Method instance for this request + context.method; + + // Parameter array sent by the send function, default is [] + context.args; + + // Cache data hit by this request + context.cachedResponse; + + // Configuration collection of useHook + context.config; + + // The various states returned by useHook, it is a state proxy, including the following properties + // loading, data, error, downloading, uploading, and additional states managed by managedStates + context.proxyStates; + + // Operation function in current useHook, send, abort + // context.fetch in useFetcher + context.send; + context.abort; +} +``` + +Next, let's take a look at what control capabilities are available. + +## Modify responsive data + +Use `context.proxyStates` to modify the state data of the current useHook. Since alova's useHook is compatible with multiple UI frameworks, proxyStates is a unified state proxy, which is used in a similar way to vue's ref value. + +```javascript +async function middleware(context, next) { + const { loading, data } = context.proxyStates; + + // Get loading value + const loadingValue = loading.v; + // Change loading status to true + loading.v = true; + + // Modify data status value + data.v = { + /* ... */ + }; +} +``` + +For detailed usage of state proxy, please refer to [State Proxy](/docs/guide/state-proxy). + +## Interrupt or repeat request + +The `abort` and `send` functions (`fetch` in useFetcher) received by the middleware can also send multiple requests when a request intent is triggered. + +A typical use case is request retry. If a request fails after sending a request, it will automatically request again according to a certain strategy. After the retry succeeds, it will trigger `onSuccess`. The following is a simple request retry example code. + +```javascript +async function middleware(context, next) { + return next().catch(error => { + if (needRetry) { + setTimeout(() => { + context.send(...context.sendArgs); + }, retryDelay); + } + return Promise.reject(error); + }); +} +``` + +If you need to interrupt the request in the middleware, you can call `context.abort()`. + +## Controlled loading state + +In the above content, we know that we can modify responsive data through `loading.v`, but there will be obstacles when you modify the loading state value `loading`, because under normal circumstances, the loading state value will be automatically set to true when calling `next`, and automatically set to false in the response process, which will overwrite the loading state value modified by `loading.v`. At this time, we can turn on the controlled loading state. After turning it on, the loading state value will no longer be modified in the `next` function and the response process, and we will be fully controlled. + +Let's take request retry as an example. We hope that the loading state will remain true from the beginning of triggering a request intention, through request retry until the request ends. + +Use `context.controlLoading` to turn on the custom control loading state. + +```javascript +async function middleware(context, next) { + context.controlLoading(); + const { loading } = context.proxyStates; + + // Set to true when the request starts + loading.v = true; + return next() + .then(value => { + // Set to false after the request is successful + loading.v = false; + return value; + }) + .catch(error => { + if (needRetry) { + setTimeout(() => { + context.send(...context.sendArgs); + }, retryDelay); + } else { + // Set to false when no longer retrying + loading.v = false; + } + return Promise.reject(error); + }); +} +``` diff --git a/docs/tutorial/06-advanced/05-custom-method-key.md b/docs/tutorial/06-advanced/05-custom-method-key.md index 031585771..185fa5e64 100644 --- a/docs/tutorial/06-advanced/05-custom-method-key.md +++ b/docs/tutorial/06-advanced/05-custom-method-key.md @@ -1,26 +1,53 @@ ---- -title: custom method key -sidebar_position: 50 ---- - -:::info version required - -v2.20.0+ - -::: - -Method key is used to identify all data associated with method instances and has a great effect, for example: - -- Caching of associated response data -- Identity sharing request - -Associate the status value returned by useRequest and other useHook - -By default, the method key is generated from the relevant request parameters of the method instance, which can accurately identify a request. - -But sometimes you want to change it so that the above three situations can be recognized as the same method in different requests. - -```javascript -//The method key is generated when creating, you can customize it through __key__ -const methodInst = alovaInstance.Get('/api/user', {}); -methodInst.__key__ = 'my-custom-method-key'; -``` +--- +title: Custom Method Key +sidebar_position: 50 +--- + +:::info Usage scope + +Fully + +::: + +Method key is used to identify all data associated with method instances and has a great effect, for example: + +- Caching of associated response data +- Identity sharing request +- Associate the states returned by useHooks such as `useRequest`, so that it can match these states with `updateState`. + +By default, the method key is generated from the relevant request parameters of the method instance, which can accurately identify a request. + +But sometimes you want to change it so that the above three situations can be recognized as the same method in different requests. + +## Customize method instance key + +```javascript +// Method key is generated when it is created, you can customize it through __key__ +const methodInst = alovaInstance.Get('/api/user', {}); +methodInst.__key__ = 'my-custom-method-key'; +``` + +## Customize all method instance keys + +The method key is generated through `Method.prototype.generateKey`, you can override this method to change the method key generation rules. + +```javascript +import { Method } from 'alova'; + +Method.prototype.generateKey = function () { + return 'your-custom-method-key'; +}; +``` + +You can also set different generation rules according to the alova instance. + +```javascript +Method.prototype.generateKey = function () { + if (this.context === alovaInstance1) { + return 'alova-1-method-key'; + } else { + // ... + } + return 'alova-default-method-key'; +}; +``` diff --git a/docs/tutorial/06-advanced/07-cache-logger.md b/docs/tutorial/06-advanced/07-cache-logger.md index 311c0d67a..1462d92e2 100644 --- a/docs/tutorial/06-advanced/07-cache-logger.md +++ b/docs/tutorial/06-advanced/07-cache-logger.md @@ -1,60 +1,60 @@ ---- -title: cache logger -sidebar_position: 70 ---- - -:::info version required - -v2.8.0+ - -::: - -In order to facilitate debugging when using the interface cache, when the request hits the cache without sending a network request, the hit cache information will be printed on the console by default, which can solve some confusion when using the cache. - -If you don't want to print cache information or custom control print cache information in some cases (such as production environment), alova also provides support for them. - -## Close cache logger printing - -Console printing can be turned off by setting `cacheLogger` to `false or null` when creating an alova instance. - -```javascript -const alovaInstance = createAlova({ - //... - cacheLogger: false -}); -``` - -You can also dynamically turn it on and off according to different environments. - -```javascript -const alovaInstance = createAlova({ - //... - // Enable cache logger in the development environment - cacheLogger: process.env.NODE_ENV === 'development' -}); -``` - -## Custom print cache logger - -The cache logger is printed via `console.log` by default. If `console.log` or other purposes are not supported in your project environment, `cacheLogger` can be specified as a function to customize the logger for processing cache hits. - -```javascript -const alovaInstance = createAlova({ - //... - /** - * Custom cache logger function - * @param response hit cache data - * @param method the current method instance - * @param cacheMode cache mode memory or restore - * @param tag The tag in the restore mode has a value only when the tag is set in the corresponding cache - */ - cacheLogger(response, method, cacheMode, tag) { - saveHitCache({ - response, - method, - cacheMode, - tag - }); - } -}); -``` +--- +title: Cache Logger +sidebar_position: 70 +--- + +:::info Usage scope + +Fully + +::: + +In order to facilitate debugging when using the interface cache, when the request hits the cache without sending a network request, the hit cache information will be printed on the console by default, which can solve some confusion when using the cache. + +If you don't want to print cache information or custom control print cache information in some cases (such as production environment), you can also close it. + +## Close cache logger printing + +Console printing can be turned off by setting `cacheLogger` to `false/null` when creating an alova instance. + +```javascript +const alovaInstance = createAlova({ + //... + cacheLogger: false +}); +``` + +You can also dynamically turn it on and off according to different environments. + +```javascript +const alovaInstance = createAlova({ + //... + // Enable cache logger in the development environment + cacheLogger: process.env.NODE_ENV === 'development' +}); +``` + +## Custom print cache logger + +The cache logger is printed via `console.log` by default. If `console.log` or other purposes are not supported in your project environment, `cacheLogger` can be specified as a function to customize the logger for processing cache hits. + +```javascript +const alovaInstance = createAlova({ + //... + /** + * Custom cache logger function + * @param response hit cache data + * @param method the current method instance + * @param cacheMode cache mode memory or restore + * @param tag The tag in the restore mode has a value only when the tag is set in the corresponding cache + */ + cacheLogger(response, method, cacheMode, tag) { + saveHitCache({ + response, + method, + cacheMode, + tag + }); + } +}); +``` diff --git a/docs/tutorial/06-advanced/08-manage-extra-states.md b/docs/tutorial/06-advanced/08-manage-extra-states.md index 8de90c559..c71e217e3 100644 --- a/docs/tutorial/06-advanced/08-manage-extra-states.md +++ b/docs/tutorial/06-advanced/08-manage-extra-states.md @@ -1,198 +1,198 @@ ---- -title: Manage extra states -sidebar_position: 80 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -In the previous [Cross Page/Module Update Response States](/tutorial/advanced/update-across-components) chapter, we introduced how to update the response status across pages or modules, but in this chapter we It only introduces updating the `data` state returned by `useRequest` and `useWatcher` through `updateState`, the value of data is always consistent with the response data, but in many cases we will use additional states(such as state A) to display data, and After the request is successful, the data data is appended to the additional state A, such as the pagination scheme of pull-down loading. In this case, we need to manage the additional state A so that it can be updated across pages/modules. - -## Update a single state - -Additional states can be managed via `managedStates` when called by the use hook, and automatically assigned the state name to update it when `updateState` is called in other modules/pages. - - - - -```javascript title="A.vue" -const todoList = page => - alova.Get('/todo', { - name: 'todoList' - }); - -const allTodo = ref([]); -useRequest(todoList, { - //... - - // highlight-start - // manage allTodo as additional state - managedStates: { - allTodo - } - // highlight-end -}); -``` - -```javascript title="B.vue" -const handleSuccess = () => { - // highlight-start - // Pass in an object and specify the state name to look up - updateState('todoList', { - allTodo: allTodoData => { - // Add a new todo item - allTodoData.push({ - title: 'new todo', - time: '10:00' - }); - return allTodoData; - } - }); - // highlight-end -}; -``` - - - - - -```javascript title="A.jsx" -const PageA = () => { - const todoList = page => - alova.Get('/todo', { - name: 'todoList' - }); - - const [allTodo, setAllTodo] = allTodoState = useState([]); - useRequest(todoList, { - //... - - // highlight-start - // manage allTodo as additional state - managedStates: { - allTodo: allTodoState - } - // highlight-end - }); - - return ( - //... - ); -} -``` - -```javascript title="B.jsx" -const PageB = () => { - //... - const handleSuccess = () => { - // highlight-start - // Pass in an object and specify the state name to look up - updateState('todoList', { - allTodo: allTodoData => { - // Add a new todo item - allTodoData.push({ - title: 'new todo', - time: '10:00' - }); - return allTodoData; - } - }); - // highlight-end - }; - - return ( - //... - ); -} -``` - - - - - -```javascript title="A.svelte" -const todoList = page => - alova.Get('/todo', { - name: 'todoList' - }); - -const allTodo = ref([]); -useRequest(todoList, { - //... - - // highlight-start - // manage allTodo as additional state - managedStates: { - allTodo - } - // highlight-end -}); -``` - -```javascript title="B.svelte" -const handleSuccess = () => { - // highlight-start - // Pass in an object and specify the state name to look up - updateState('todoList', { - allTodo: allTodoData => { - // Add a new todo item - allTodoData.push({ - title: 'new todo', - time: '10:00' - }); - return allTodoData; - } - }); - // highlight-end -}; -``` - - - - -:::info Note - -Not support to manage additional states. - -::: - - - - -## Update multiple states - -In the above example, we implemented the update of a single `allTodo` state across pages. In fact, any number of states can be updated at the same time through the object description method of `updateState`. - -```javascript -updateState('todoList', { - state1: state1Data => { - //... - }, - state2: state2Data => { - //... - }, - state3: state3Data => { - //... - } - //... -}); -``` - -It should be noted that the above 3 additional states need to be managed through the `managedStates` property before updating. - -## shorthand for data status update - -When only updating the data state, you can directly pass in the callback function instead of specifying it as an object. - -```javascript -updateState('todoList', { - data: dataRaw => { - //... - } -}); - -// The following are shorthand -updateState('todoList', dataRaw => { - //... -}); -``` +--- +title: Manage Extra states +sidebar_position: 80 +--- + +:::info Scope of use + +Client useHook + +::: + +import Tabs from '@theme/Tabs'; + +import TabItem from '@theme/TabItem'; + +In the previous [Update responsive state across pages/modules](/tutorial/advanced/update-across-components) chapter, we introduced how to update responsive states across pages or modules through `updateState`, but it can only update states created by useHooks. What should we do if we need to update custom states across components? Let's continue! + +## Update a single state + +You can manage additional states through `managedStates` when using useHooks, and automatically specify the state name to update it when calling `updateState` in other modules/pages. + + + + +```javascript title="A.vue" +const todoList = page => + alova.Get('/todo', { + name: 'todoList' + }); + +const allTodo = ref([]); +useRequest(todoList, { + // ... + + // highlight-start + // Manage allTodo as an additional state + managedStates: { + allTodo + } + // highlight-end +}); +``` + +```javascript title="B.vue" +const handleSuccess = () => { + // highlight-start + // Pass in an object and specify the state name to find + updateState('todoList', { + allTodo: allTodoData => { + // Add a new todo item + allTodoData.push({ + title: 'new todo', + time: '10:00' + }); + return allTodoData; + } + }); + // highlight-end +}; +``` + + + + + +```javascript title="A.jsx" +const PageA = () => { + const todoList = page => + alova.Get('/todo', { + name: 'todoList' + }); + + const [allTodo, setAllTodo] = allTodoState = useState([]); + useRequest(todoList, { + // ... + + // highlight-start + // Manage allTodo as an additional state + managedStates: { + allTodo: allTodoState + } + // highlight-end + }); + + return ( + // ... + ); +} +``` + +```javascript title="B.jsx" +const PageB = () => { + // ... + const handleSuccess = () => { + // highlight-start + // Pass in an object and specify the state name to look up + updateState('todoList', { + allTodo: allTodoData => { + // Add a new todo item + allTodoData.push({ + title: 'new todo', + time: '10:00' + }); + return allTodoData; + } + }); + // highlight-end + }; + + return ( + // ... + ); +} +``` + + + + + +```javascript title="A.svelte" +// a.svelte +const todoList = page => + alova.Get('/todo', { + name: 'todoList' + }); + +const allTodo = writable([]); +useRequest(todoList, { + // ... + + // highlight-start + // Manage allTodo as an additional state + managedStates: { + allTodo + } + // highlight-end +}); +``` + +```javascript title="B.svelte" +const handleSuccess = () => { + // highlight-start + // Pass in an object and specify the state name to search + updateState('todoList', { + allTodo: allTodoData => { + // Add a new todo item + allTodoData.push({ + title: 'new todo', + time: '10:00' + }); + return allTodoData; + } + }); + // highlight-end +}; +``` + + + + + +## Update multiple states + +In the above example, we implemented the update of a single `allTodo` state across pages. In fact, any multiple states can be updated at the same time through the object description method of `updateState`. + +```javascript +updateState('todoList', { + state1: state1Data => { + // ... + }, + state2: state2Data => { + // ... + }, + state3: state3Data => { + // ... + } + // ... +}); +``` + +It should be noted that the above 3 additional states need to be managed through the `managedStates` property before updating. + +## Abbreviation for data state update + +When only updating the data state, you can directly pass in the callback function without specifying it as an object. + +```javascript +updateState('todoList', { + data: dataRaw => { + // ... + } +}); + +// The following is an abbreviation +updateState('todoList', dataRaw => { + // ... +}); +``` diff --git a/docs/tutorial/06-advanced/09-ssr.md b/docs/tutorial/06-advanced/09-ssr.md index e2a09ab2e..fe8c1c177 100644 --- a/docs/tutorial/06-advanced/09-ssr.md +++ b/docs/tutorial/06-advanced/09-ssr.md @@ -1,228 +1,220 @@ ---- -title: Server-Side Rendering(SSR) -sidebar_position: 90 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -:::info version required - -2.8.0+ - -::: - -## Overview - -Although the positioning of alova is not to make requests in nodejs, we have also adapted it in order to combine the server-side rendering([Nuxt3.x](https://nuxt.com/) / [Nextjs](https://nextjs.org/) / [sveltekit](https://kit.svelte.dev/)) of the UI framework. Although built-in request functionality is provided in e.g. `Nuxt3.x`, `Sveltekit`, if you choose to use alova, you can use alova to manage requests in both server and client, instead of server and client separately. Use different request schemes to manage them. - -Here are some caveats for using alova in SSR, and examples of usage in SSR for different UI frameworks. - -## Call apis on server - -In SSR, it is necessary to get data on server and render it into HTML. In this case, we cannot use alova's use hooks (and do not need to use them) to obtain data. Below we will show the supported SSR frameworks respectively. - -### Nuxt3.x - -In Nuxt3.x, `useAsyncData` is provided to initialize page data on server, and `useFetch` and `$fetch` request functions are also provided. These request functions that can be used on both server and client are really convenient. However, if you want to use alova in nuxt, you can use the combination of **useAsyncData + alova.Method** to complete the server-side data fetching, which is no different from your usual `useAsyncData`. - -```html - -``` - -### Nextjs - -Nextjs provides fixed server-side initialization page data functions, such as `getStaticProps`, `getServerSideProps`, etc., you can [directly use the method instance](/tutorial/getting-started/quick-start) call apis in the function. - -```jsx -const todoListGetter = alovaInstance.Get('/todo/list', { - headers: { - 'Content-Type': 'application/json;charset=UTF-8' - } -}); - -export const getServerSideProps = async ctx => { - const list = await todoListGetter.send(); - return { - props: { - list - } - }; -}; -export default function App(props) { - return props.list.map(item => ( -
- {item.title} - {item.time} -
- )); -} -``` - -### Sveltekit - -Sveltekit also provides the `load` function to initialize the page data on server, and you can also [directly use the method instance](/tutorial/getting-started/quick-start) call apis in the function. For example, call apis in `+page.server.js`. - -```javascript title=+page.server.js -const todoListGetter = alovaInstance.Get('/todo/list', { - headers: { - 'Content-Type': 'application/json;charset=UTF-8' - } -}); - -/** @type {import('./$types').PageServerLoad} */ -export async function load({ params }) { - return { - list: todoListGetter.send() - }; -} -``` - -## Using usehooks in SSR - -Since each SSR framework has its own way to initialize data on server, when generating html in SSR, `useRequest` and `useWatcher` in the component will not be initiated even if `immediate` is set to `true` request, as this is more like client initialization data. - -However, if you need to initialize the data of the page as in the client, you can also set `immediate` to `true`, and when the page is running in the browser, you can use all the functions of alova as usual. - -## Precautions - -### Client and server caches are separate - -If you use alova's caching function, you may need to pay attention here that the client and server caches are not shared, which means that if you directly use **usehooks** to get data when initializing the page, you may Run into inconsistencies in client-side and server-side rendering, although few people do. - -Please see the following code snippet. - - - - -```html - - - -``` - - - - -```jsx -function App(props) { - const { loading, data } = useRequest(alovaGetter); - return ( - <> - {loading ?
loading
: null} -
{data}
- - ); -} -``` - -
- - -```html - - -{#if $loading} -
loading
-{/if} -
{{ data }}
-``` - -
-
- -The following code assumes that `alovaGetter` requests are cached on server, but not on the client. - -At this time, when the html is generated on server , `loading` is `false` and does not display `
loading
` because it hits the cache, but when the client is initialized, because it misses the cache, `loading` is `true` will cause `
loading
` to be displayed, and the SSR framework will prompt that the rendering of the two ends is inconsistent. - -**Solution** - -1. Try to put the page data initialization work in the acquisition function instead of the component; -2. If you must do this, you can avoid using the same apis on the client and server, or turn off the problematic api caches; -3. If caching is also required, you can clear the cache on server in the data initialization function of server. The sample code is as follows: - - - - -```html - - - -``` - - - - -```jsx -import { invalidateCache } from 'alova'; - -function App(props) { - const { loading, data } = useRequest(alovaGetter); - return ( - <> - {loading ?
loading
: null} -
{data}
- - ); -} - -export const getServerSideProps = async () => { - // Clear the cache on server - invalidateCache(alovaGetter); - return { - props: {} - }; -}; -``` - -
- - -```javascript title=+page.server.js -import { invalidateCache } from 'alova'; - -/** @type {import('./$types').PageServerLoad} */ -export async function load({ params }) { - // Clear the cache on server - invalidateCache(alovaGetter); - return {}; -} -``` - - -
+--- +title: Server-Side Rendering(SSR) +sidebar_position: 90 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## Overview + +Although the positioning of alova is not to make requests in nodejs, we have also adapted it in order to combine the server-side rendering([Nuxt3.x](https://nuxt.com/) / [Nextjs](https://nextjs.org/) / [sveltekit](https://kit.svelte.dev/)) of the UI framework. Although built-in request functionality is provided in e.g. `Nuxt3.x`, `Sveltekit`, if you choose to use alova, you can use alova to manage requests in both server and client, instead of server and client separately. Use different request schemes to manage them. + +Here are some caveats for using alova in SSR, and examples of usage in SSR for different UI frameworks. + +## Call apis on server + +In SSR, it is necessary to get data on server and render it into HTML. In this case, we cannot use alova's use hooks (and do not need to use them) to obtain data. Below we will show the supported SSR frameworks respectively. + +### Nuxt3.x + +In Nuxt3.x, `useAsyncData` is provided to initialize page data on server, and `useFetch` and `$fetch` request functions are also provided. These request functions that can be used on both server and client are really convenient. However, if you want to use alova in nuxt, you can use the combination of **useAsyncData + alova.Method** to complete the server-side data fetching, which is no different from your usual `useAsyncData`. + +```html + +``` + +### Nextjs + +Nextjs provides fixed server-side initialization page data functions, such as `getStaticProps`, `getServerSideProps`, etc., you can [directly use the method instance](/tutorial/getting-started/quick-start) call apis in the function. + +```jsx +export const getServerSideProps = async ctx => { + const list = await alovaInstance.Get('/todo/list', { + headers: { + 'Content-Type': 'application/json;charset=UTF-8' + } + }); + return { + props: { + list + } + }; +}; +export default function App(props) { + return props.list.map(item => ( +
+ {item.title} + {item.time} +
+ )); +} +``` + +### Sveltekit + +Sveltekit also provides the `load` function to initialize the page data on server, and you can also [directly use the method instance](/tutorial/getting-started/quick-start) call apis in the function. For example, call apis in `+page.server.js`. + +```javascript title=+page.server.js +const todoListGetter = alovaInstance.Get('/todo/list', { + headers: { + 'Content-Type': 'application/json;charset=UTF-8' + } +}); + +/** @type {import('./$types').PageServerLoad} */ +export async function load({ params }) { + return { + list: todoListGetter + }; +} +``` + +## Using usehooks in SSR + +Since each SSR framework has its own way to initialize data on server, when generating html in SSR, `useRequest` and `useWatcher` in the component will not be initiated even if `immediate` is set to `true` request, as this is more like client initialization data. + +However, if you need to initialize the data of the page as in the client, you can also set `immediate` to `true`, and when the page is running in the browser, you can use all the functions of alova as usual. + +## Precautions + +### Client and server caches are separate + +If you use alova's caching function, you may need to pay attention here that the client and server caches are not shared, which means that if you directly use **usehooks** to get data when initializing the page, you may Run into inconsistencies in client-side and server-side rendering, although few people do. + +Please see the following code snippet. + + + + +```html + + + +``` + + + + +```jsx +function App(props) { + const { loading, data } = useRequest(alovaGetter); + return ( + <> + {loading ?
loading
: null} +
{data}
+ + ); +} +``` + +
+ + +```html + + +{#if $loading} +
loading
+{/if} +
{{ data }}
+``` + +
+
+ +The following code assumes that `alovaGetter` requests are cached on server, but not on the client. + +At this time, when the html is generated on server , `loading` is `false` and does not display `
loading
` because it hits the cache, but when the client is initialized, because it misses the cache, `loading` is `true` will cause `
loading
` to be displayed, and the SSR framework will prompt that the rendering of the two ends is inconsistent. + +**Solution** + +1. Try to put the page data initialization work in the acquisition function instead of the component; +2. If you must do this, you can avoid using the same apis on the client and server, or turn off the problematic api caches; +3. If caching is also required, you can clear the cache on server in the data initialization function of server. The sample code is as follows: + + + + +```html + + + +``` + + + + +```jsx +import { invalidateCache } from 'alova'; + +function App(props) { + const { loading, data } = useRequest(alovaGetter); + return ( + <> + {loading ?
loading
: null} +
{data}
+ + ); +} + +export const getServerSideProps = async () => { + // Clear the cache on server + invalidateCache(alovaGetter); + return { + props: {} + }; +}; +``` + +
+ + +```javascript title=+page.server.js +import { invalidateCache } from 'alova'; + +/** @type {import('./$types').PageServerLoad} */ +export async function load({ params }) { + // Clear the cache on server + invalidateCache(alovaGetter); + return {}; +} +``` + + +
diff --git a/docs/tutorial/06-advanced/README.md b/docs/tutorial/06-advanced/README.md index feaeb352d..1f08bc4e7 100644 --- a/docs/tutorial/06-advanced/README.md +++ b/docs/tutorial/06-advanced/README.md @@ -1,9 +1,9 @@ --- -title: 进阶 +title: Advanced --- import DocCardList from '@theme/DocCardList'; -进阶教程可以让你更深入地了解 alova 的一些特性,它们可能并不是常用功能,但可以帮你快速解决更多特殊的请求问题。 +The advanced tutorial allows you to have a deeper understanding of some features of alova. They may not be commonly used functions, but can help you quickly solve more special request problems. diff --git a/docs/tutorial/08-custom/01-http-adapter.md b/docs/tutorial/08-custom/01-http-adapter.md new file mode 100644 index 000000000..2b4e60102 --- /dev/null +++ b/docs/tutorial/08-custom/01-http-adapter.md @@ -0,0 +1,175 @@ +--- +title: Request Adapter +sidebar_position: 10 +--- + +Remember how to create an Alova instance? + +```javascript +import adapterFetch from 'alova/fetch'; + +const alovaInstance = createAlova({ + // ... + requestAdapter: adapterFetch() +}); +``` + +`requestAdapter` is the request adapter. The internal request sending and receiving will rely on the request adapter. `alova/fetch` manages requests through the fetch api. In most cases, we can use it. However, when `alova` runs in an environment where the fetch api is not available (such as app, mini program), you need to replace a request adapter that supports the current environment. + +So how should you customize a request adapter? It's very simple. It is actually a function that will be called every time a request is initiated and returns an object. This object contains request-related data sets such as `url`, `method`, `data`, `headers`, `timeout`, etc. Although there are many fields, we only need to access the data we need. + +## Request adapter structure + +The request adapter will receive request-related parameters and the method instance currently being requested, and return a response-related function set. + +```javascript +function CustomRequestAdapter(requestElements, methodInstance) { + // Send the request here... + + return { + async response() { + // Return response data + }, + async headers() { + // Asynchronous function that returns the response header + }, + abort() { + // Interrupt the request, this function will be triggered when abort is called externally + }, + onDownload(updateDownloadProgress) { + // Download progress information, updateDownloadProgress is continuously called internally to update the download progress + }, + onUpload(updateUploadProgress) { + // Upload progress information, updateUploadProgress is continuously called internally to update the upload progress + } + }; +} +``` + +### Request parameter details + +**requestElements** + +Send the relevant elements of the request, including the following data. + +```typescript +interface RequestElements { + // Request url, get parameters are already included + readonly url: string; + + // Request type, such as GET, POST, PUT, etc. + readonly type: MethodType; + + // Request header information, object + readonly headers: Arg; + + // Request body information + readonly data?: RequestBody; +} +``` + +**methodInstance** + +The method instance of the current request + +### Return parameter details + +**response (required)** + +An asynchronous function, the function returns a response value, which will be passed to the global response interceptor (responded); + +**headers (required)** + +An asynchronous function, the response header object returned by the function will be passed to the transformData of the Method instance Conversion hook function; + +**abort (required)** + +A normal function, which is used to interrupt the request. All interrupt requests will eventually call this function to execute; + +**onDownload (optional)** + +A normal function, which receives a callback function to update the download progress. The frequency of progress update is customized in this function. In this example, it is simulated to update every 100 milliseconds. The `updateDownloadProgress` callback function receives two parameters, the first parameter is the total size, and the second parameter is the downloaded size; + +**onUpload (optional)** + +A normal function, which receives a callback function to update the upload progress. The frequency of progress update is customized in this function. In this example, it is simulated to update every 100 milliseconds. The `updateUploadProgress` callback function receives two parameters, the first parameter is the total size, and the second parameter is the uploaded size; + +## XMLHttpRequest request adapter example + +The following is an example of an adapter that sends a request through XMLHttpRequest. It is mainly used to demonstrate the writing of the adapter. The code is incomplete and cannot be run. + +```javascript +function XMLHttpRequestAdapter(requestElements, methodInstance) { + // Deconstruct the data needed + const { url, type, data, headers } = config; + + // Send request + const xhr = new XMLHttpRequest(); + xhr.open(type, url); + for (const key in headers) { + xhr.setRequestHeader(key, headers[key]); + } + const responsePromise = new Promise((resolve, reject) => { + xhr.addEventListener('load', event => { + // Process response data + resolve(/* ... */); + }); + xhr.addEventListener('error', event => { + // Process request error + reject(/* ... */); + }); + }); + + xhr.send(JSON.stringify(data)); + + return { + // Asynchronous function that returns response data + response: () => responsePromise, + + // Asynchronous function that returns the response header + headers: () => responsePromise.then(() => xhr.getAllResponseHeaders()), + abort: () => { + xhr.abort(); + }, + + // Download progress information, continuously call updateDownloadProgress internally to update the download progress + onDownload: updateDownloadProgress => { + xhr.addEventListener('progress', event => { + // Data receiving progress + updateDownloadProgress(event.total, event.loaded); + }); + }, + + // Upload progress information, continuously call updateUploadProgress internally to update the upload progress + onUpload: updateUploadProgress => { + xhr.upload.onprogress = event => { + updateUploadProgress(event.total, event.loaded); + }; + } + }; +} +``` + +:::note + +For more complete request adapter details, please refer to [alova/fetch Source code](https://github.com/alovajs/alova/blob/main/packages/alova/src/predefine/adapterFetch.ts) to understand. + +::: + +## Request adapter type + +The global `beforeRequest`, `responded` interceptors, and the types of configuration objects when creating method instances are automatically inferred based on the types provided by the request adapter. The following is the type of `alova/fetch`. + +```javascript +import type { AlovaRequestAdapter } from 'alova'; + +export type adapterFetch = () => AlovaRequestAdapter; +``` + +The generic parameters in `AlovaRequestAdapter` are values ​​of three types: `RequestConfig`, `Response`, and `ResponseHeader`, which are automatically inferred to the types given by the request adapter in global interceptors, method instance configurations, etc. + +They are represented as follows: + +- **RequestConfig**: request configuration object type, which will be applied to the `config` parameter when the method is created. +- **Response**: response type, for example, `alova/fetch` is the Response type +- **ResponseHeader**: response header object type diff --git a/docs/tutorial/08-custom/02-storage-adapter.md b/docs/tutorial/08-custom/02-storage-adapter.md new file mode 100644 index 000000000..ae4072066 --- /dev/null +++ b/docs/tutorial/08-custom/02-storage-adapter.md @@ -0,0 +1,91 @@ +--- +title: Storage Adapter +sidebar_position: 20 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +alova involves multiple features that require data persistence, such as persistent caching, silent submission, and offline submission. **By default, alova uses `localStorage` to store persistent data**, but considering the non-browser environment, it also supports customization. + +Customizing the storage adapter is also very simple. You only need to specify the functions for saving data, getting data, and removing data, which is roughly like this. + + + + +```javascript +const customStorageAdapter = { + set(key, value) { + // Save data, value is structured data, you can call JSON.stringify to convert it to a string + }, + get(key) { + // Get data, you need to return structured data, you can call JSON.parse to convert it to an object + }, + remove(key) { + // Remove data + }, + clear() { + // Clear data + } +}; +``` + +Use a custom adapter. + +```javascript +const alovaInstance = createAlova({ + // ... + storageAdapter: customStorageAdapter +}); +``` + + + + +```ts +import { AlovaGlobalCacheAdapter } from 'alova'; + +class CustomStorageAdapter implements AlovaGlobalCacheAdapter { + set(key, value) { + // Save data, value is structured data, you can call JSON.stringify to convert it to a string + } + get(key) { + // Get data, you need to return structured data, you can call JSON.parse to convert it to an object + } + remove(key) { + // Remove data + } + clear() { + // Clear data + } +} +``` + +Use a custom adapter. + +```javascript +const alovaInstance = createAlova({ + // ... + storageAdapter: new CustomStorageAdapter() +}); +``` + + + + +## SessionStorage Storage Adapter Example + +```javascript +const sessionStorageAdapter = { + set(key, value) { + sessionStorage.setItem(key, JSON.stringify(value)); + }, + get(key) { + const data = sessionStorage.getItem(key); + return data ? JSON.parse(data) : data; + }, + remove(key) { + sessionStorage.removeItem(key); + } +}; +``` diff --git a/docs/tutorial/08-custom/03-client-strategy.md b/docs/tutorial/08-custom/03-client-strategy.md new file mode 100644 index 000000000..b8a8dc2eb --- /dev/null +++ b/docs/tutorial/08-custom/03-client-strategy.md @@ -0,0 +1,255 @@ +--- +title: Client Strategy +sidebar_position: 30 +--- + +alova's client strategies are divided into three types: middleware, interceptor, and useHook. When your project needs customization, you can refer to this section. + +## Middleware + +Middleware provides powerful capabilities that can control almost all behaviors of a request. You can use it to control request behavior, customize request status, error handling, etc. For details, please go to [Request Middleware](/tutorial/advanced/middleware) to view. The following source code can tell you what the middleware can do. + +- [actionDelegationMiddleware](https://github.com/alovajs/alova/blob/main/packages/client/src/middlewares/actionDelegation.ts) Cross-component triggering requests are implemented through middleware. +- [useSQRequest](https://github.com/alovajs/alova/blob/main/packages/client/src/hooks/silent/useSQRequest.ts) Implement immediate response requests in middleware without waiting. +- [useSerialRequest](https://github.com/alovajs/alova/blob/main/packages/client/src/hooks/serial/useSerialRequest.ts) Serialize requests in middleware and manage response data for multiple requests. +- [useRetriableRequest](https://github.com/alovajs/alova/blob/main/packages/client/src/hooks/useRetriableRequest.ts) Retry failed requests in middleware. +- [Delayed update loading](/tutorial/best-practice/middleware) example. + +## Interceptor + +Interceptors control global pre- and post-request behaviors. We can wrap global interceptors to implement interceptors with specific functions. + +The following is an example that interrupts all ongoing requests under certain conditions. + +```js +const methodsAborter = (handler, detector) => { + let requestingMethods = []; + // Return the pre-request interceptor + return method => { + if (detector()) { + requestingMethods.forEach(method => { + method.abort(); + }); + return; + } + requestingMethods.push(method); + method.promise + ?.then(() => { + requestingMethods = requestingMethods.filter(item => item !== method); + }) + .catch(() => {}); // Prevent errors from being thrown + handler(method); + }; +}; + +createAlova({ + beforeRequest: methodsAborter( + method => { + // Original pre-request hook + }, + () => { + // Interrupt request judgment condition + return false; + } + ) +}); +``` + +A more complex example, [token Authentication interceptor](https://github.com/alovajs/alova/blob/main/packages/client/src/functions/tokenAuthentication/createTokenAuthentication.ts) + +## useHook + +useHook is the most commonly used request strategy of alova, and it is cross-UI framework. When you write useHook for a specific UI framework, you can write it just like ordinary useHook. Here we mainly understand the useHook writing across UI frameworks. + +We innovatively use **state proxy** to smooth out the differences in UI framework states. Its usage is similar to Vue's ref value. You only need to simply access the v attribute or assign a value to the v attribute, and you no longer need to care about the differences in UI frameworks. + +In order to smooth out the differences in state proxies, we provide the `statesHookHelper` function to create auxiliary functions, and use these auxiliary functions to implement useHook across UI frameworks. + +```js +import { statesHookHelper } from '@alova/shared/function'; +import { promiseStatesHook } from 'alova'; + +function myUseHook(methodHandler, options) { + const { + create, + computed, + ref, + onMounted, + onUnmount, + watch, + objectify, + exposeProvider, + __referingObj + } = statesHookHelper(promiseStatesHook()); +} +``` + +Next, let's take a closer look at them. + +### Create a state + +Use `create` to create a UI framework-independent state proxy FrameworkState, taking the loading state as an example. + +```js +// Parameter 1: initial value +// Parameter 2: export key +const loading = create(false, 'loading'); + +// Get the original value +const dehydratedLoading = loading.v; +// Update the state value +loading.v = true; +// Get the export value, call `statesHook.export` to export the value internally +const exportedLoading = loading.e; +// Get the platform-related state value created by statesHook.create +const platformedState = loading.s; +``` + +### Create a computed value + +Use `computed` to create a UI framework-independent computed value. In order to be compatible with react, you need to pass in the computed value dependency, which can be a platform-related state value and FrameworkState. + +```js +// Parameter 1: Function of computed value +// Parameter 2: Computed property dependency +// Parameter 3: Export key +const computedState = computed(() => !loading.v, [loading], 'states'); + +// Get the original value +const dehydratedComputedState = computedState.v; +// Get the exported value, call `statesHook.export` to export the value internally +const exportedComputedState = computedState.e; +// Get the platform-related state value created by statesHook.computed +const platformedComputedState = computedState.s; +``` + +### Create reference value + +It is mainly used to cross the closure trap of react, call `useRef` internally, and other frameworks directly return `{ current: value }` objects, which are accessed and updated through `unifiedValue.current`. + +```js +const unifiedValue = ref({}); +``` + +### Component mounting hook + +```js +onMounted(() => {}); +``` + +### Component uninstallation hook + +```js +onUnmount(() => {}); +``` + +### Monitor state changes + +```js +// Parameter 1: Monitor item, used for compatibility with react, can pass platform-related state values ​​and FrameworkState +// Parameter 2: Callback function +watch([loading, computedState.e], () => {}); +``` + +### State objectification + +Convert the state proxy array unrelated to the UI framework into a state object, generally used with `exposeProvider`, with key as the key, and the second parameter can also specify the value of the object. + +```js +const states = objectify([loading, data, error]); +/* The value of states is +{ +loading: loading, +data: data, +error: error +} +*/ + +const states = objectify([loading, data, error], 's'); +/* The value of states is +{ +loading: loading.s, +data: data.s, +error: error.s +} +*/ +``` + +### Expose internal data + +If the internal information of useHook is exported directly, it will become unusable, so `exposeProvider` is provided for export, which will automatically help us handle the following: + +1. Automatically convert the state proxy to the state of the corresponding UI framework. +2. Provide a unified update function. If the incoming parameters include the states and update functions returned by useRequest, they will also be automatically compatible with these. +3. In react, functions that do not start with on are wrapped with memorize, and those that have been wrapped are no longer wrapped. +4. Functions that start with on are considered event binding functions and will be directly added to the export object. +5. Export a unified referringObject. + +The following is an export example of `usePagination`: + +```js +export const usePagination = (/* ... */) => { + return exposeProvider({ + // Return object of useWatcher in current useHook + ...useWatcherReturns, + + // State array to object + ...objectify([page, pageSize, data, pageCount, total, isLastPage]), + + // Operation function + reset: () => { + // ... + }, + + // Event binding function + onFetchSuccess: fetchState.onSuccess + // ... + }); +}; +``` + +### `__referingObj` Description + +`__referingObj` is for compatibility with options style and class style UI frameworks. It is a common reference object used to synchronize the component objects of options and class style, so that the corresponding component objects can be accessed in statesHook. The same referingObj object needs to be used in the custom useHook to ensure that the states in a useHook can access the same component object. +`__referingObj` will be created and returned in `statesHookHelper`, and no specific processing is required. Just export it as follows. + +When the alova core hook is used in the scene hook, pass this object into the hook and ensure that it is exported in this useHook. + +> If referingObj is not passed in, the core hook will be automatically created internally + +```js + +export const useXXX = (...) => { +const {__referingObj, +// ... +} = statesHookHelper(promiseStatesHook()); + +const states = useReqest(methodHandler, { +__referingObj +}); + +return { +// ... +__referingObj, +} +} +``` + +When the alova core hook is not used in the scene hook, the referingObj object is exported directly in this useHook. + +```js +export const useXXX = (/* ... */) => { + const { + __referingObj + // ... + } = statesHookHelper(promiseStatesHook()); + // ... + + return { + // ... + __referingObj + }; +}; +``` + +When you use `exposeProvider` to export information, it will automatically export `__referingObj` without us having to handle it manually. diff --git a/docs/tutorial/08-custom/04-server-strategy.md b/docs/tutorial/08-custom/04-server-strategy.md new file mode 100644 index 000000000..f68153ed8 --- /dev/null +++ b/docs/tutorial/08-custom/04-server-strategy.md @@ -0,0 +1,45 @@ +--- +title: Server Strategy +sidebar_position: 40 +--- + +Server strategy is also called `Server hook`, which is a decorated function of a method instance. + +The following is the specification of server hooks, which receives a method instance and returns a new method instance, so it is very convenient to combine multiple server hooks. + +```ts +export interface AlovaServerHook> { + (method: Method, options: Options): Method; +} +``` + +Customizing `Server hook` is very simple. The following is a simple request retry example. + +```js +import { HookedMethod } from 'alova/server'; + +const retry = (method, options) => { + const { retry: maxRetryTimes = 3, delay = 1000 } = options; + let retryTimes = 0; + + return new HookedMethod(method, forceRequest => + method.send(forceRequest).then( + value => value, + error => { + if (retryTimes < maxRetryTimes) { + retryTimes += 1; + setTimeout(() => { + method.send(forceRequest).catch(noop); + }, delay); + } + } + ) + ); +}; + +// Usage +const userData = await retry(alova.Get('/api/user'), { + retry: 5, + delay: 1500 +}); +``` diff --git a/docs/tutorial/08-custom/05-stateshook.md b/docs/tutorial/08-custom/05-stateshook.md new file mode 100644 index 000000000..1f3f52aa0 --- /dev/null +++ b/docs/tutorial/08-custom/05-stateshook.md @@ -0,0 +1,122 @@ +--- +title: States Hook +sidebar_position: 50 +--- + +Remember how to create an Alova instance? + +```javascript +const alovaInstance = createAlova({ + // ... + statesHook: ReactHook +}); +``` + +`statesHook` will determine which UI library state is returned when requested. In most cases you don't need to customize `statesHook`, but if you need to adapt more MVVM libraries that alova does not support, you need to customize `statesHook`. + +`statesHook` is a normal object containing specific functions. Let's take a look at how **VueHook** is written. + +## statesHook structure + +statesHook is an object. The following is its type definition. + +```ts +interface StatesHook { + /** + * Create state + * @param initialValue Initial data + * @returns State value + */ + create: (initialValue: any, referingObject: ReferingObject) => State; + + /** + * Create computed state + * @param initialValue Initial data + * @param referingObject Reference object + */ + computed: (getter: () => any, deps: Export[], referingObject: ReferingObject) => Computed; + + /** + * Export value for developers + * @param state State value + * @param referingObject refering object + * @returns Exported value + */ + export?: (state: State, referingObject: ReferingObject) => Export; + + /** Convert state to normal data */ + dehydrate: (state: State, key: string, referingObject: ReferingObject) => any; + + /** + * Update state value + * @param newVal new data set + * @param state original state value + * @param @param referingObject refering object + */ + update: (newVal: any, state: State, key: string, referingObject: ReferingObject) => void; + + /** + * Control the function of executing the request. This function will be executed once when useRequest and useWatcher are called + * Executed once in the fetch function in useFetcher + * When watchingStates is an empty array, execute the handleRequest function once + * When watchingStates is a non-empty array, call when the state changes. When immediate is true, call it immediately + * hook is an instance of use hook. Each time use hook is called, a hook instance will be generated + * It can be executed directly in vue, but it needs to be executed in useEffect in react + * removeStates function is a function to clear the current state. It should be called when the component is uninstalled + */ + effectRequest: ( + effectParams: EffectRequestParams, + referingObject: ReferingObject + ) => void; + + /** + * Wrap send, abort and other use hooks operation functions + * This is mainly used to optimize the problem of generating new functions for each rendering in react and optimize performance + * @param fn use hook operation function + * @returns wrapped operation function + */ + memorize?: any>(fn: Callback) => Callback; + + /** + * Create a reference object + * @param initialValue initial value + * @returns reference object containing initial value + */ + ref?: (initialValue: D) => { current: D }; + + /** + * Status monitoring + * @param source monitoring status + * @param callback status change callback function + * @param referingObject referring object + */ + watch: (source: Watched[], callback: () => void, referingObject: ReferingObject) => void; + + /** + * Component mounting hook + * @param callback callback function + * @param referingObject refering object + */ + onMounted: (callback: () => void, referingObject: ReferingObject) => void; + + /** + * Component uninstallation hook + * @param callback callback function + * @param referingObject refering object + */ + onUnmounted: (callback: () => void, referingObject: ReferingObject) => void; +} +``` + +:::warning Note + +If statesHook involves something similar to `react`, when `alova`'s use hook is called every time it is re-rendered, the `saveStates` function needs to be triggered every time it is re-rendered in `effectRequest`, because `react` refreshes its state references every time it is re-rendered, so we need to re-save them again. + +::: + +The following is the statesHook source code that leaves the UI framework. + +- [react hook](https://github.com/alovajs/alova/blob/main/packages/client/src/statesHook/react.ts) +- [vue hook](https://github.com/alovajs/alova/blob/main/packages/client/src/statesHook/vue.ts) +- [svelte hook](https://github.com/alovajs/alova/blob/main/packages/client/src/statesHook/svelte.ts) +- [vue options hook](https://github.com/alovajs/alova/blob/main/packages/vue-options/src/stateHook.ts) diff --git a/docs/tutorial/08-custom/README.md b/docs/tutorial/08-custom/README.md new file mode 100644 index 000000000..53c4300d9 --- /dev/null +++ b/docs/tutorial/08-custom/README.md @@ -0,0 +1,31 @@ +--- +title: Overview +--- + +alova has high scalability. In addition to providing common features such as core caching mechanism, request sharing mechanism and state management, it also provides various customization functions and middleware mechanisms, which can adapt to different js environments and customize request strategies. In the following chapters, we will introduce them in detail. + +## Custom Adapter + +In order to meet the running requirements of js in different environments, you can customize the request adapter, storage adapter, and even the state adapter of the UI framework, which will also be introduced in detail in the following chapters. Some adapter examples are listed below. + +- [fetch adapter](https://github.com/alovajs/alova/blob/main/packages/alova/src/predefine/adapterFetch.ts) +- [localStorage storage adapter](https://github.com/alovajs/alova/blob/main/packages/alova/src/defaults/cacheAdapter.ts) +- [vue states hook](https://github.com/alovajs/alova/blob/main/packages/client/src/statesHook/vue.ts) + +You can also group multiple types of adapters into a collection, such as [Uniapp adapter](/tutorial/request-adapter/alova-adapter-uniapp). + +## Custom client strategy + +alova provides 10+ custom client strategy modules, but sometimes you may need to write your own strategy module. Usually, a custom request strategy is based on the combination of the three core useHooks of `useRequest`, `useWatcher` and `useFetcher`, and writes [middleware](/tutorial/advanced/middleware) and cache manipulation functions for them to control their request methods, so as to achieve various request strategies + +The following strategy modules are very representative, and it is strongly recommended that you refer to the source code for inspiration. + +- [source of usePagination](https://github.com/alovajs/scene/blob/main/packages/client/src/hooks/pagination/usePagination.ts) +- [source of useCaptcha](https://github.com/alovajs/scene/blob/main/packages/client/src/hooks/useCaptcha.ts) +- [source of useForm](https://github.com/alovajs/scene/blob/main/packages/client/src/hooks/useForm.ts) + +## Custom server-side strategy + +The server-side strategy module is a simple function. The following is a `Server hook` for request retry. + +- [Request retry](https://github.com/alovajs/scene/blob/main/packages/server/src/hooks/retry.ts) diff --git a/docs/tutorial/08-custom/_category_.json b/docs/tutorial/08-custom/_category_.json new file mode 100644 index 000000000..0b24b5159 --- /dev/null +++ b/docs/tutorial/08-custom/_category_.json @@ -0,0 +1,3 @@ +{ + "label": "Custom" +} diff --git a/docs/tutorial/08-request-adapter/_category_.json b/docs/tutorial/08-request-adapter/_category_.json deleted file mode 100644 index 2c4f02c49..000000000 --- a/docs/tutorial/08-request-adapter/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Request adapter", - "link": { - "type": "generated-index" - } -} diff --git a/docs/tutorial/09-migration/01-v2-to-v3.md b/docs/tutorial/09-migration/01-v2-to-v3.md new file mode 100644 index 000000000..f3b1a92e0 --- /dev/null +++ b/docs/tutorial/09-migration/01-v2-to-v3.md @@ -0,0 +1,6 @@ +--- +title: v3 upgrade guidelines +sidebar_position: 10 +--- + +alova@3 is beta, so it is not recommended to upgrade in the actual project. diff --git a/docs/tutorial/09-migration/02-from-axios.md b/docs/tutorial/09-migration/02-from-axios.md new file mode 100644 index 000000000..3661c9a20 --- /dev/null +++ b/docs/tutorial/09-migration/02-from-axios.md @@ -0,0 +1,6 @@ +--- +title: migrate from axios +sidebar_position: 20 +--- + +Coming soon... diff --git a/docs/tutorial/09-migration/_category_.json b/docs/tutorial/09-migration/_category_.json new file mode 100644 index 000000000..9aa227523 --- /dev/null +++ b/docs/tutorial/09-migration/_category_.json @@ -0,0 +1,3 @@ +{ + "label": "Migration" +} diff --git a/docs/tutorial/10-custom/01-overview.md b/docs/tutorial/10-custom/01-overview.md deleted file mode 100644 index 0c66df6ee..000000000 --- a/docs/tutorial/10-custom/01-overview.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: Overview -sidebar_position: 10 ---- - -Alova has high scalability. In addition to providing common features such as core caching mechanism, request sharing mechanism, and state management, it also provides various customization functions and middleware mechanisms, which can adapt to different js environments and customize requests. Strategy. - -## Adapter - -In order to meet the running requirements of js in different environments, you can customize the request adapter, storage adapter, and even the state adapter of the UI framework, which will be introduced in detail in the next chapters. Some adapter examples are listed below. - -- [fetch adapter](https://github.com/alovajs/alova/blob/main/src/predefine/GlobalFetch.ts) -- [localStorage storage adapter](https://github.com/alovajs/alova/blob/main/src/predefine/globalLocalStorage.ts) -- [vue states hook](https://github.com/alovajs/alova/blob/main/src/predefine/VueHook.ts) - -You can also combine multiple types of adapters into a collection, for example [Uniapp Adapter](/tutorial/request-adapter/alova-adapter-uniapp). - -## Write request strategy - -alova's request strategy is separate from the alova core library, so that developers can also take advantage of alova's high scalability to write their own request strategies. Usually, a custom request strategy is based on the combination of `useRequest`, `useWatcher` and `useFetcher`, and writing [middleware](/tutorial/advanced/middleware), cache manipulation functions for them to control their The request method, so as to realize the request strategy of various effects. - -The request strategies in **@alova/scene** are well represented, and it is strongly recommended that you refer to the source code for inspiration. - -- [Paging request strategy source code](https://github.com/alovajs/scene/blob/main/src/hooks/pagination/usePagination.js) -- [Captcha strategy source code](https://github.com/alovajs/scene/blob/main/src/hooks/useCaptcha.ts) -- [Form submission strategy source code](https://github.com/alovajs/scene/blob/main/src/hooks/useForm.ts) diff --git a/docs/tutorial/10-custom/02-custom-http-adapter.md b/docs/tutorial/10-custom/02-custom-http-adapter.md deleted file mode 100644 index ad6a9729a..000000000 --- a/docs/tutorial/10-custom/02-custom-http-adapter.md +++ /dev/null @@ -1,193 +0,0 @@ ---- -title: Custom Request Adapter -sidebar_position: 20 ---- - -Remember how to create an Alova instance? - -```javascript -const alovaInstance = createAlova({ - //... - requestAdapter: GlobalFetch() -}); -``` - -`requestAdapter` is a request adapter. Internal request sending and receiving will depend on the request adapter. `GlobalFetch` manages requests through fetch api. In most cases, we can use it. However, when `alova` When running in an environment where fetch api is not available (such as app, applet), it is necessary to replace a request adapter that supports the current environment. - -So how should you customize a request adapter? Very simple, it is actually a function, which is called every time a request is made, and returns an object, which contains such things as `url`, `method`, `data`, `headers`, `timeout`, etc. Request related data sets. Although there are many fields, we only need to access the data we need. - -## Request adapter structure - -The request adapter will receive request-related parameters and the currently requesting method instance, and return a set of response-related functions. - -```javascript -function CustomRequestAdapter(requestElements, methodInstance) { - // send request... - return { - async response() { - // ...return the response data - }, - async headers() { - // Asynchronous function that returns response headers - }, - abort() { - // Abort request, this function will be triggered when abort is called externally - }, - onDownload(updateDownloadProgress) { - // Download progress information, internally call updateDownloadProgress continuously to update the download progress - }, - onUpload(updateUploadProgress) { - // Upload progress information, internally call updateUploadProgress continuously to update the upload progress - } - }; -} -``` - -### Request parameter details - -**requestElements** - -Relevant elements of the send request, including the following data. - -```typescript -interface RequestElements { - // request url, the get parameter is already included - readonly url: string; - - // Request type, such as GET, POST, PUT, etc. - readonly type: MethodType; - - // Request header information, object - readonly headers: Arg; - - // request body information - readonly data?: RequestBody; -} -``` - -**methodInstance** - -The method instance of the current request - -### Return parameter details - -**response (required)** - -An asynchronous function, the function returns the response value, which will be passed to the global response interceptor responded; - -**headers (required)** - -An asynchronous function, the response header object returned by the function will be passed to the transformData conversion hook function of the Method instance; - -**abort (required)** - -A common function, which is used for aborting request. All aborting requests will eventually call this function to execute; - -**onDownload (optional)** - -An ordinary function that receives a callback function that updates the download progress, and customizes the frequency of the progress update within this function, in this example simulating an update every 100 milliseconds. The `updateDownloadProgress` callback function receives two parameters, the first parameter is the total size, and the second parameter is the downloaded size; - -**onUpload (optional)** - -An ordinary function that receives a callback function that updates the upload progress, and customizes the frequency of the progress update within this function, in this example simulating an update every 100 milliseconds. The `updateUploadProgress` callback function receives two parameters, the first parameter is the total size, and the second parameter is the uploaded size; - -## XMLHttpRequest request adapter example - -The following is an example of an adapter that sends requests through XMLHttpRequest, mainly to demonstrate how to write the adapter, the code is incomplete and cannot be run. - -```javascript -function XMLHttpRequestAdapter(requestElements, methodInstance) { - // Deconstruct the data that needs to be used - const { url, type, data, headers } = config; - - // send request - const xhr = new XMLHttpRequest(); - xhr.open(type, url); - for (const key in headers) { - xhr.setRequestHeader(key, headers[key]); - } - const responsePromise = new Promise((resolve, reject) => { - xhr.addEventListener('load', event => { - // process response data - resolve(/* ... */); - }); - xhr.addEventListener('error', event => { - // Handle request errors - reject(/* ... */); - }); - }); - - xhr.send(JSON.stringify(data)); - - return { - // Asynchronous function that returns response data - response: () => responsePromise, - - // Asynchronous function that returns response headers - headers: () => responsePromise.then(() => xhr.getAllResponseHeaders()), - abort: () => { - xhr.abort(); - }, - - // Download progress information, internally call updateDownloadProgress continuously to update the download progress - onDownload: updateDownloadProgress => { - xhr.addEventListener('progress', event => { - // data receiving progress - updateDownloadProgress(event.total, event.loaded); - }); - }, - - // Upload progress information, internally call updateUploadProgress continuously to update the upload progress - onUpload: updateUploadProgress => { - xhr.upload.onprogress = event => { - updateUploadProgress(event.total, event.loaded); - }; - } - }; -} -``` - -:::note - -More complete request adapter details can be found in [GlobalFetch source code](https://github.com/alovajs/alova/blob/main/src/predefine/GlobalFetch.ts) to understand. - -::: - -## Request adapter type - -The types of the global `beforeRequest`, `responded` interceptors, and the configuration object when the `Method` instance is created will be automatically inferred based on the type provided by the request adapter. The following are the types of GlobalFetch. - -```javascript -import type { RequestElements, Method, ProgressUpdater } from 'alova'; - -export type GlobalFetch = () => ( - elements: RequestElements, - method: Method -) => { - response: () => Promise, - headers: () => Promise, - onDownload: (handler: ProgressUpdater) => void, - abort: () => void -}; -``` - -Three types of values ​​of `RC`, `RE` and `RH` are specified in this type, so the type given by the request adapter will be automatically inferred in the global interceptor, method instance configuration and other places. - -They are expressed as: - -- **RC**: Abbreviation of _RequestConfig_, request configuration object type -- **RH**: Abbreviation of _ResponseHeader_, response header object type -- **RE**: Abbreviation of _Response_, response type - -If you are using **GlobalFetch**, their types will be inferred as: - -- **RC**: fetch api's request configuration object `RequestInit`; -- **RH**: Response header object `Headers`; -- **RE**: response object `Response`; - -In order to facilitate the definition of the request adapter type, alova also provides an adapter type. You only need to pass in the `RC/RE/RH` generic parameters as needed. - -```typescript -import type { AlovaRequestAdapter } from 'alova'; -type CustomRequestAdpater = AlovaRequestAdapter; -``` diff --git a/docs/tutorial/10-custom/03-custom-storage-adapter.md b/docs/tutorial/10-custom/03-custom-storage-adapter.md deleted file mode 100644 index 2cd749f8f..000000000 --- a/docs/tutorial/10-custom/03-custom-storage-adapter.md +++ /dev/null @@ -1,48 +0,0 @@ ---- -title: Custom Storage Adapter -sidebar_position: 30 ---- - -Alova involves multiple functions that require data persistence, such as persistent cache, silent submission, and offline submission. **By default, alova will use `localStorage` to store persistent data**, but considering the non-browser environment, it also supports customization. - -Customizing the storage adapter is also very simple, you only need to specify the functions for saving data, getting data, and removing data, roughly like this. - -```javascript -const customStorageAdapter = { - set(key, value) { - // Save the data, value is structured data, which can be converted to a string by calling JSON.stringify - }, - get(key) { - // get data, return structured data, which can be converted by calling JSON.parse - }, - remove(key) { - // remove data - } -}; -``` - -Then pass in this adapter when creating an `alova` instance. - -```javascript -const alovaInstance = createAlova({ - //... - storageAdapter: customStorageAdapter -}); -``` - -## SessionStorage storage adapter example - -```javascript -const sessionStorageAdapter = { - set(key, value) { - sessionStorage.setItem(key, JSON.stringify(value)); - }, - get(key) { - const data = sessionStorage.getItem(key); - return data ? JSON.parse(data) : data; - }, - remove(key) { - sessionStorage.removeItem(key); - } -}; -``` diff --git a/docs/tutorial/10-custom/04-custom-stateshook.md b/docs/tutorial/10-custom/04-custom-stateshook.md deleted file mode 100644 index e2b686346..000000000 --- a/docs/tutorial/10-custom/04-custom-stateshook.md +++ /dev/null @@ -1,101 +0,0 @@ ---- -title: Custom States Hook -sidebar_position: 50 ---- - -Remember how to create an Alova instance? - -```javascript -const alovaInstance = createAlova({ - //... - statesHook: ReactHook -}); -``` - -`statesHook` will decide which MVVM library state to return when requesting, alova currently provides **VueHook, ReactHook, svelteHook**. - -In most cases, you should not use this function, but if you need to adapt to more MVVM libraries that alova does not support, you need to customize `statesHook`. - -`statesHook` is an ordinary object that contains specific functions, but these basically do not involve algorithms, let's see how **VueHook** is written. - -## statesHook structure - -statesHook is represented by an object, the following is an example of **VueHook**. - -```javascript -import { ref, watch, onUnmounted } from 'vue'; - -const VueHook = { - // state creation function - create: rawData => ref(data), - - // state export function - export: state => state, - - // dehydration function - dehydrate: state => state.value, - - // Reactive state update function - update: (newVal, states) => { - Object.keys(newVal).forEach(key => { - states[key].value = newVal[key]; - }); - }, - - // request send control function - effectRequest({ handler, removeStates, saveStates, immediate, frontStates, watchingStates }) { - // Remove the corresponding state when the component is uninstalled - onUnmounted(removeStates); - - // When calling useRequest and useFetcher, watchingStates is undefined - if (!watchingStates) { - handler(); - return; - } - - // When calling useWatcher, watchingStates is an array of states that need to be monitored - // When immediate is true, it means that the request needs to be sent immediately - watch(watchingStates, handler, { immediate }); - } -}; -``` - -## Custom statesHook function description - -> All of the following 5 functions must be specified. - -**create** - -Responsive state creation function, `loading`, `error`, `data`, `downloading`, `uploading`, etc. are all created by calling this function, such as the vue3 project will be created as a ref value; - -**export** - -State export function, this function receives the responsive state created by the create function, and exports the final state for developers to use, where the state exported by `VueHook` is the original state; - -**dehydrate** - -Dehydration function, which means converting the responsive state into normal data, is the opposite operation to create, in `updateState`; - -**update** - -Responsive status update function, the status update maintained internally by `alova` is done through this function. This function receives two parameters, the first parameter is the new data object, and the second parameter is the map collection of the original responsive state, here you can write a fixed cycle to update `states`; - -**effectRequest** - -Request sending control function, it will execute this function immediately when `useRequest`, `useWatcher`, `useFetcher` are called, we need to complete three things in this function: - -1. When the current component is uninstalled, call the removeStates function to remove the responsive state involved in the current component to avoid memory overflow; -2. When calling useWatcher, bind the state monitor, and call the sendRequest function when the state changes. You can use whether `states` is an array to judge whether `useWatcher` is called. At the same time, the `immediate` parameter is used to judge whether `useWatcher` is called Whether the request needs to be sent immediately; -3. When calling `useRequest` and `useFetcher`, call sendRequest to send a request, at this time `states` is `undefined`; - -:::warning Caution - -If the library involved in statesHook is like `react`, the use hook of `alova` will be called every time it is re-rendered, then the `saveStates` function needs to be triggered every time it is re-rendered in `effectRequest`, this is because `react `Every re-render refreshes its state references, so we need to re-save them again. - -::: - -[ReactHook source code click here to view](https://github.com/alovajs/alova/blob/main/src/predefine/ReactHook.ts) - -## statesHook type - -If you want it to support typescript when customizing statesHook, you can [click here to view](/tutorial/combine-framework/typescript) diff --git a/docs/tutorial/10-custom/_category_.json b/docs/tutorial/10-custom/_category_.json deleted file mode 100644 index 505fadaab..000000000 --- a/docs/tutorial/10-custom/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Custom", - "link": { - "type": "generated-index" - } -} diff --git a/docs/tutorial/11-others/04-future.md b/docs/tutorial/11-others/04-future.md deleted file mode 100644 index d889d2646..000000000 --- a/docs/tutorial/11-others/04-future.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -title: Future of alova -sidebar_position: 40 ---- - -alovajs is positioned as a lightweight request strategy library. It currently provides good support in terms of request functions and request strategies, but the future of alovajs does not stop there. - -## More request strategies - -This is the direction that remains unchanged, and we will continue to explore efficient and easy-to-use request strategies based on common businesses. - -## More UI framework support - -Although alovajs is a request tool based on UI framework, its flexible design allows us to use it in various UI frameworks. It will eventually be compatible with the following UI frameworks and js environments: - -- Functional style framework, such as `react/react-native/vue-composntion/svelte/solid/preact/qwik`. -- SSR frameworks such as `next/nuxt/sveltekit`. -- class style framework, such as `angular/lit/stencil`. -- options style framework, such as `vue-options/native applet (China🇨🇳)`. -- Multi-end adaptation framework, such as `Uniapp/Taro` (China 🇨🇳). - -Please check [Go to UI Framework](/category/framework) for details. - -## Automatic management and maintenance of APIs - -In the future, alovajs is also committed to solving front-end API problems and further simplifying the workflow of front-end development. This is the next development direction of alovajs: **automatic management and maintenance of APIs**, which specifically includes the following three points. - -1. Automatically generate a request function with complete ts type and complete description. Whether it is a js project or a ts project, the call does not need to be introduced, making it as convenient for developers as directly calling `location.reload`, and the request function can be directly seen A complete description and request parameter type hints, which can be automatically generated by openAPI. - -2. Since the automatically generated request function has a complete description and type hint, develop a vscode plug-in to quickly retrieve the API you need to use through keywords, and you no longer need to consult the API documentation. - -3. Solve the problem of front-end and back-end collaboration fault. Any changes in the interface are known to the front-end and can be notified when starting the project. If changes are found when building the project, an error will be thrown to stop the build. If it is a ts project, it will also be compiled. When an error is thrown, you can also view the change record through the vscode plugin. - -## Development checklist - -[Click here to view all the development checklist](/contributing/become-core-member/#list-of-participating-repositories) diff --git a/docs/tutorial/11-others/05-react-native.md b/docs/tutorial/11-others/05-react-native.md deleted file mode 100644 index dbd0d47df..000000000 --- a/docs/tutorial/11-others/05-react-native.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -title: React-Native app development -sidebar_position: 50 ---- - -You can develop a React-Native app with alova, and you can also use request adapter `GlobalFetch` directly to handle request event. - -But...there are some cautions: - -## metro version - -In the alova, `exports` is used to define multiple items in `package.json`, so it's necessary to ensure 2 points below: - -1. version of metro must >= 0.76.0 -2. enable `resolver.unstable_enablePackageExports` in `metro.config.js`. [Click here for detail](https://facebook.github.io/metro/docs/configuration/#unstable_enablepackageexports-experimental) diff --git a/docs/tutorial/11-others/06-use-in-static.md b/docs/tutorial/11-others/06-use-in-static.md deleted file mode 100644 index d47608206..000000000 --- a/docs/tutorial/11-others/06-use-in-static.md +++ /dev/null @@ -1,139 +0,0 @@ ---- -title: Use in static html -sidebar_position: 60 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -You can also use alova via importing from CDN. - - - - -```html - - - - - - - - - -
-
Loading...
-
{{ error.message }}
- responseData: {{ data }} -
- - - -``` - -
- - -```html - - - - - - - - - - - -
- - - -``` - -
- - -:::tip - -svelte 依赖于编译工具,不能通过 CDN 直接使用,详情见 [svelte.dev](https://svelte.dev/) - -::: - - - - -```html - - - - - - - - - -
-
Loading...
-
{{ todo.error.message }}
- responseData: {{ todo.data }} -
- - - -``` - -
-
diff --git a/docs/tutorial/11-others/07-hide-recommend-tips.md b/docs/tutorial/11-others/07-hide-recommend-tips.md deleted file mode 100644 index 75aad3a62..000000000 --- a/docs/tutorial/11-others/07-hide-recommend-tips.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -title: Hide recommend Tips -sidebar_position: 70 ---- - -:::info version required - -v2.7.0- - -::: - -Alova can cooperate with the extension library to obtain a better development experience. In order to allow more developers to obtain a better development experience, the extension of alova will be recommended in the console when using it. - -![tips](/img/alova-tips.png) - -These prompt codes will be automatically removed when building the production environment package. If you want to hide them in the development environment, you can do the following: - -## Vite - -Set environment variable **VITE_ALOVA_TIPS=0** in `.env.development` file - -```bash title=.env.development -VITE_ALOVA_TIPS=0 -``` - -:::warning Warning -If info still exists. you can try to remove the deps cache of vite, which at the dir `node_modules/.vite/deps`. -::: - -## Webpack - -### Vue - -Set environment variable **VUE_APP_ALOVA_TIPS=0** in `.env.development` file - -```bash title=.env.development -VUE_APP_ALOVA_TIPS=0 -``` - -### React - -Set environment variable **REACT_APP_ALOVA_TIPS=0** in `.env.development` file - -```bash title=.env.development -REACT_APP_ALOVA_TIPS=0 -``` diff --git a/docusaurus.config.ts b/docusaurus.config.ts index 2d228267d..a8f159077 100644 --- a/docusaurus.config.ts +++ b/docusaurus.config.ts @@ -1,313 +1,354 @@ -// @ts-check -// Note: type annotations allow type checking and IDEs autocompletion -import type { Config } from '@docusaurus/types'; -import { themes } from 'prism-react-renderer'; - -const lightCodeTheme = themes.duotoneLight; -const darkCodeTheme = themes.oceanicNext; -const config: Config = { - title: 'Alova.JS', - tagline: - 'According to different request scenarios, we provide targeted request strategies to improve application fluency and availability, reduce server pressure, and enable applications to have excellent strategic thinking like a wise man', - url: 'https://alova.js.org', - baseUrl: '/', - onBrokenLinks: 'throw', - onBrokenMarkdownLinks: 'throw', - favicon: 'img/favicon.ico', - - // GitHub pages deployment config. - // If you aren't using GitHub pages, you don't need these. - organizationName: 'alovajs', // Usually your GitHub org/user name. - projectName: 'alova', // Usually your repo name. - - // Even if you don't use internalization, you can use this field to set useful - // metadata like html lang. For example, if your site is Chinese, you may want - // to replace "en" with "zh-Hans". - i18n: { - defaultLocale: 'en', - locales: ['en', 'zh-CN'], - localeConfigs: { - en: { - htmlLang: 'en-GB' - } - } - }, - scripts: ['/iconfont/iconfont.js'], - presets: [ - [ - 'classic', - { - docs: { - routeBasePath: '/', - sidebarPath: require.resolve('./sidebars.js'), - // Please change this to your repo. - // Remove this to remove the "edit this page" links. - editUrl: 'https://github.com/alovajs/alovajs.github.io/blob/main/', - versions: { - current: { - label: '3.0-beta' - } - } - }, - // disable blog - blog: false, - theme: { - customCss: require.resolve('./src/css/custom.css') - } - } - ] - ], - - stylesheets: [ - 'https://rsms.me/inter/inter.css', - 'https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,400;0,700;1,400;1,700&display=swap' - ], - themeConfig: { - image: '/img/card_image.jpg', - metadata: [{ name: 'twitter:site', content: '@alovajs' }], - navbar: { - title: '', - logo: { - alt: 'ALOVA', - src: 'img/logo-text.svg' - }, - items: [ - { - to: 'tutorial/getting-started', - position: 'left', - label: 'Docs' - }, - { - to: 'tutorial/example/init-page', - position: 'left', - label: 'Example' - }, - { - to: 'api/alova', - position: 'left', - label: 'API' - }, - { - type: 'dropdown', - label: 'Contributing', - position: 'left', - items: [ - { - label: 'Contributing Guidelines', - to: 'contributing/overview' - }, - { - label: 'Become core member', - to: 'contributing/become-core-member' - }, - { - label: 'Developing Guidelines', - to: 'contributing/developing-guidelines' - }, - { - label: 'Code of conduct', - to: 'contributing/code-of-conduct' - } - ] - }, - { - type: 'docsVersionDropdown', - position: 'right', - dropdownActiveClassDisabled: true - }, - { - type: 'localeDropdown', - position: 'right' - }, - { - to: 'https://github.com/alovajs/alova/releases', - position: 'right', - label: 'Releases' - }, - { - href: 'https://x.com/alovajs', - className: 'header-x-link', - position: 'right' - }, - { - href: 'https://discord.gg/S47QGJgkVb', - className: 'header-discord-link', - position: 'right' - }, - { - href: 'https://github.com/alovajs/alova', - className: 'header-github-link', - position: 'right' - } - ] - }, - colorMode: { - defaultMode: 'light', - disableSwitch: false, - respectPrefersColorScheme: true - }, - announcementBar: { - id: 'support_us', - content: `⭐️ - If you also like alova, - - star it on GitHub! - - ⭐️`, - backgroundColor: 'var(--ifm-color-primary-light)', - textColor: '#fff', - isCloseable: false - }, - footer: { - style: 'light', - - logo: { - alt: 'Meta Open Source Logo', - src: 'img/logo.svg', - href: 'https://opensource.fb.com', - width: 160, - height: 51 - }, - - links: [ - { - title: 'Nav', - items: [ - { - label: 'Docs', - to: 'tutorial/getting-started' - }, - { - label: 'Example', - to: 'tutorial/example/init-page' - } - ] - }, - { - title: 'Community', - items: [ - // { - // label: 'Stack Overflow', - // to: 'https://stackoverflow.com/questions/tagged/docusaurus', - // }, - { - label: 'Discord', - to: 'https://discord.gg/S47QGJgkVb' - }, - { - label: 'X', - to: 'https://x.com/alovajs' - }, - { - html: 'Wechat Group' - } - ] - }, - { - title: 'More', - items: [ - { - label: 'GitHub', - to: 'https://github.com/alovajs/alova' - }, - { - label: 'Issues', - to: 'https://github.com/alovajs/alova/issues' - }, - { - label: 'Pull request', - to: 'https://github.com/alovajs/alova/pulls' - } - ] - } - ], - copyright: `Copyright © ${new Date().getFullYear()} alova.js Team` - }, - prism: { - theme: lightCodeTheme, - darkTheme: darkCodeTheme, - - // https://github.com/facebook/docusaurus/discussions/9506#discussioncomment-7506183 - additionalLanguages: ['javascript', 'bash'] - }, - - algolia: { - // The application ID provided by Algolia - appId: 'LGEFHNJ1SI', - - // Public API key: it is safe to commit it - apiKey: '4c4f6078174a5ae66234de817e75e0a8', - - indexName: 'alova_website', - - // Optional: see doc section below - contextualSearch: true, - - // Optional: Specify domains where the navigation should occur through window.location instead on history.push. Useful when our Algolia config crawls multiple documentation sites and we want to navigate with window.location.href to them. - externalUrlRegex: 'external\\.com|domain\\.com', - - // Optional: Replace parts of the item URLs from Algolia. Useful when using the same search index for multiple deployments using a different baseUrl. You can use regexp or string in the `from` param. For example: localhost:3000 vs myCompany.com/docs - replaceSearchResultPathname: { - from: '/docs/', // or as RegExp: /\/docs\// - to: '/' - }, - - // Optional: Algolia search parameters - searchParameters: {}, - - // Optional: path for search page that enabled by default (`false` to disable it) - searchPagePath: 'search' - } - - // algolia搜索 - // algolia: { - // // The application ID provided by Algolia - // appId: 'LGEFHNJ1SI', - - // // Public API key: it is safe to commit it - // apiKey: '4c4f6078174a5ae66234de817e75e0a8', - - // indexName: 'YOUR_INDEX_NAME', - - // // Optional: see doc section below - // contextualSearch: true, - - // // Optional: Specify domains where the navigation should occur through window.location instead on history.push. Useful when our Algolia config crawls multiple documentation sites and we want to navigate with window.location.href to them. - // externalUrlRegex: 'external\\.com|domain\\.com', - - // // Optional: Replace parts of the item URLs from Algolia. Useful when using the same search index for multiple deployments using a different baseUrl. You can use regexp or string in the `from` param. For example: localhost:3000 vs myCompany.com/docs - // replaceSearchResultPathname: { - // from: '/docs/', // or as RegExp: /\/docs\// - // to: '/' - // }, - - // // Optional: Algolia search parameters - // searchParameters: {}, - - // // Optional: path for search page that enabled by default (`false` to disable it) - // searchPagePath: 'search' - - // //... other Algolia params - // } - }, - - markdown: { - mermaid: true - }, - // 主题 - themes: ['@docusaurus/theme-mermaid'], - - // 插件 - plugins: [ - [ - './plugin/baiduStatistics', - { - id: '5afa4c96fca09cb386951b736ee31e56' - } - ] - ] -}; - -export default config; +// @ts-check +// Note: type annotations allow type checking and IDEs autocompletion +import type { Config } from '@docusaurus/types'; +import { themes } from 'prism-react-renderer'; + +const lightCodeTheme = themes.duotoneLight; +const darkCodeTheme = themes.oceanicNext; +const config: Config = { + title: 'Alova.JS', + tagline: + 'According to different request scenarios, we provide targeted request strategies to improve application fluency and availability, reduce server pressure, and enable applications to have excellent strategic thinking like a wise man', + url: 'https://alova.js.org', + baseUrl: '/', + onBrokenLinks: 'throw', + onBrokenMarkdownLinks: 'throw', + favicon: 'img/favicon.ico', + + // GitHub pages deployment config. + // If you aren't using GitHub pages, you don't need these. + organizationName: 'alovajs', // Usually your GitHub org/user name. + projectName: 'alova', // Usually your repo name. + + // Even if you don't use internalization, you can use this field to set useful + // metadata like html lang. For example, if your site is Chinese, you may want + // to replace "en" with "zh-Hans". + i18n: { + defaultLocale: 'en', + locales: ['en', 'zh-CN'], + localeConfigs: { + en: { + htmlLang: 'en-GB' + } + } + }, + scripts: ['/iconfont/iconfont.js'], + presets: [ + [ + 'classic', + { + docs: { + routeBasePath: '/', + sidebarPath: require.resolve('./sidebars.js'), + // Please change this to your repo. + // Remove this to remove the "edit this page" links. + editUrl: 'https://github.com/alovajs/alovajs.github.io/blob/main/', + versions: { + current: { + label: '3.0-beta' + } + } + }, + // disable blog + blog: false, + theme: { + customCss: require.resolve('./src/css/custom.css') + } + } + ] + ], + + stylesheets: [ + 'https://rsms.me/inter/inter.css', + 'https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,400;0,700;1,400;1,700&display=swap' + ], + themeConfig: { + image: '/img/card_image.jpg', + metadata: [{ name: 'twitter:site', content: '@alovajs' }], + navbar: { + title: '', + logo: { + alt: 'ALOVA', + src: 'img/logo-text.svg' + }, + items: [ + { + type: 'dropdown', + label: 'Docs', + position: 'left', + items: [ + { + label: 'Getting Started', + to: 'tutorial/getting-started' + }, + { + label: 'Request Adapter', + to: 'next/resource/request-adapter' + }, + { + label: 'Storage Adapter', + to: 'next/resource/storage-adapter' + }, + { + label: 'UI Frameworks', + to: 'next/resource/framework' + }, + { + label: 'Error Reference', + to: 'next/error' + } + ] + }, + { + to: 'tutorial/example/init-page', + position: 'left', + label: 'Example' + }, + { + to: 'api/alova', + position: 'left', + label: 'API' + }, + { + type: 'dropdown', + label: 'About', + position: 'left', + items: [ + { + label: 'Request Scene Model', + to: 'next/about/rsm' + }, + { + label: 'Comparison', + to: 'next/about/comparison' + }, + { + label: 'Q&A', + to: 'next/about/q&a' + } + ] + }, + { + type: 'dropdown', + label: 'Contributing', + position: 'left', + items: [ + { + label: 'Contributing Guidelines', + to: 'contributing/overview' + }, + { + label: 'Become core member', + to: 'contributing/become-core-member' + }, + { + label: 'Developing Guidelines', + to: 'contributing/developing-guidelines' + }, + { + label: 'Code of conduct', + to: 'contributing/code-of-conduct' + } + ] + }, + { + type: 'docsVersionDropdown', + position: 'right', + dropdownActiveClassDisabled: true + }, + { + type: 'localeDropdown', + position: 'right' + }, + { + to: 'https://github.com/alovajs/alova/releases', + position: 'right', + label: 'Releases' + }, + { + href: 'https://x.com/alovajs', + className: 'header-x-link', + position: 'right' + }, + { + href: 'https://discord.gg/S47QGJgkVb', + className: 'header-discord-link', + position: 'right' + }, + { + href: 'https://github.com/alovajs/alova', + className: 'header-github-link', + position: 'right' + } + ] + }, + colorMode: { + defaultMode: 'light', + disableSwitch: false, + respectPrefersColorScheme: true + }, + announcementBar: { + id: 'support_us', + content: `⭐️ + If you also like alova, + + star it on GitHub! + + ⭐️`, + backgroundColor: 'var(--ifm-color-primary-light)', + textColor: '#fff', + isCloseable: false + }, + footer: { + style: 'light', + + logo: { + alt: 'Meta Open Source Logo', + src: 'img/logo.svg', + href: 'https://opensource.fb.com', + width: 160, + height: 51 + }, + + links: [ + { + title: 'Nav', + items: [ + { + label: 'Docs', + to: 'tutorial/getting-started' + }, + { + label: 'Example', + to: 'tutorial/example/init-page' + } + ] + }, + { + title: 'Community', + items: [ + // { + // label: 'Stack Overflow', + // to: 'https://stackoverflow.com/questions/tagged/docusaurus', + // }, + { + label: 'Discord', + to: 'https://discord.gg/S47QGJgkVb' + }, + { + label: 'X', + to: 'https://x.com/alovajs' + }, + { + html: 'Wechat Group' + } + ] + }, + { + title: 'More', + items: [ + { + label: 'GitHub', + to: 'https://github.com/alovajs/alova' + }, + { + label: 'Issues', + to: 'https://github.com/alovajs/alova/issues' + }, + { + label: 'Pull request', + to: 'https://github.com/alovajs/alova/pulls' + } + ] + } + ], + copyright: `Copyright © ${new Date().getFullYear()} alova.js Team` + }, + prism: { + theme: lightCodeTheme, + darkTheme: darkCodeTheme, + + // https://github.com/facebook/docusaurus/discussions/9506#discussioncomment-7506183 + additionalLanguages: ['javascript', 'bash'] + }, + + algolia: { + // The application ID provided by Algolia + appId: 'LGEFHNJ1SI', + + // Public API key: it is safe to commit it + apiKey: '4c4f6078174a5ae66234de817e75e0a8', + + indexName: 'alova_website', + + // Optional: see doc section below + contextualSearch: true, + + // Optional: Specify domains where the navigation should occur through window.location instead on history.push. Useful when our Algolia config crawls multiple documentation sites and we want to navigate with window.location.href to them. + externalUrlRegex: 'external\\.com|domain\\.com', + + // Optional: Replace parts of the item URLs from Algolia. Useful when using the same search index for multiple deployments using a different baseUrl. You can use regexp or string in the `from` param. For example: localhost:3000 vs myCompany.com/docs + replaceSearchResultPathname: { + from: '/docs/', // or as RegExp: /\/docs\// + to: '/' + }, + + // Optional: Algolia search parameters + searchParameters: {}, + + // Optional: path for search page that enabled by default (`false` to disable it) + searchPagePath: 'search' + } + + // algolia搜索 + // algolia: { + // // The application ID provided by Algolia + // appId: 'LGEFHNJ1SI', + + // // Public API key: it is safe to commit it + // apiKey: '4c4f6078174a5ae66234de817e75e0a8', + + // indexName: 'YOUR_INDEX_NAME', + + // // Optional: see doc section below + // contextualSearch: true, + + // // Optional: Specify domains where the navigation should occur through window.location instead on history.push. Useful when our Algolia config crawls multiple documentation sites and we want to navigate with window.location.href to them. + // externalUrlRegex: 'external\\.com|domain\\.com', + + // // Optional: Replace parts of the item URLs from Algolia. Useful when using the same search index for multiple deployments using a different baseUrl. You can use regexp or string in the `from` param. For example: localhost:3000 vs myCompany.com/docs + // replaceSearchResultPathname: { + // from: '/docs/', // or as RegExp: /\/docs\// + // to: '/' + // }, + + // // Optional: Algolia search parameters + // searchParameters: {}, + + // // Optional: path for search page that enabled by default (`false` to disable it) + // searchPagePath: 'search' + + // //... other Algolia params + // } + }, + + markdown: { + mermaid: true + }, + // 主题 + themes: ['@docusaurus/theme-mermaid'], + + // 插件 + plugins: [ + [ + './plugin/baiduStatistics', + { + id: '5afa4c96fca09cb386951b736ee31e56' + } + ] + ] +}; + +export default config; diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current.json b/i18n/zh-CN/docusaurus-plugin-content-docs/current.json index 8599723a1..9efa96a05 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current.json +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current.json @@ -3,76 +3,84 @@ "message": "3.0-beta", "description": "The label for version current" }, - "sidebar.tutorialSidebar.category.Overview": { + "sidebar.tutorial.category.Overview": { "message": "概览", - "description": "The label for category Overview in sidebar tutorialSidebar" + "description": "The label for category Overview in sidebar tutorial" }, - "sidebar.tutorialSidebar.category.Examples": { + "sidebar.tutorial.category.Examples": { "message": "示例", - "description": "The label for category Examples in sidebar tutorialSidebar" + "description": "The label for category Examples in sidebar tutorial" }, - "sidebar.tutorialSidebar.category.Examples.link.generated-index.description": { + "sidebar.tutorial.category.Examples.link.generated-index.description": { "message": "这里有丰富的示例,来展示alova在不同请求场景下的表现。", - "description": "The generated-index page description for category Examples in sidebar tutorialSidebar" + "description": "The generated-index page description for category Examples in sidebar tutorial" }, - "sidebar.tutorialSidebar.category.Getting Started": { + "sidebar.tutorial.category.Getting Started": { "message": "开始", - "description": "The label for category Get Started in sidebar tutorialSidebar" + "description": "The label for category Get Started in sidebar tutorial" }, - "sidebar.tutorialSidebar.category.Cache Details": { + "sidebar.tutorial.category.Cache Details": { "message": "缓存详解", - "description": "The label for category Cache Details in sidebar tutorialSidebar" + "description": "The label for category Cache Details in sidebar tutorial" }, - "sidebar.tutorialSidebar.category.Combine Framework": { + "sidebar.tutorial.category.Combine Framework": { "message": "结合框架", - "description": "The label for category Combine Framework in sidebar tutorialSidebar" + "description": "The label for category Combine Framework in sidebar tutorial" }, - "sidebar.tutorialSidebar.category.Client Strategy": { + "sidebar.tutorial.category.Client Strategy": { "message": "客户端策略", - "description": "The label for category Client Strategy in sidebar tutorialSidebar" + "description": "The label for category Client Strategy in sidebar tutorial" }, - "sidebar.tutorialSidebar.category.Server Strategy": { + "sidebar.tutorial.category.Server Strategy": { "message": "服务端策略", - "description": "The label for category Server Strategy in sidebar tutorialSidebar" + "description": "The label for category Server Strategy in sidebar tutorial" }, - "sidebar.tutorialSidebar.category.Strategy": { + "sidebar.tutorial.category.Strategy": { "message": "请求策略", - "description": "The label for category Strategy in sidebar tutorialSidebar" + "description": "The label for category Strategy in sidebar tutorial" }, - "sidebar.tutorialSidebar.category.Sensorless data interaction": { + "sidebar.tutorial.category.Sensorless data interaction": { "message": "无感数据交互", - "description": "The label for category Sensorless data interaction in sidebar tutorialSidebar" + "description": "The label for category Sensorless data interaction in sidebar tutorial" }, - "sidebar.tutorialSidebar.category.Basics completed": { + "sidebar.tutorial.category.Basics completed": { "message": "🎉你已完成基础", - "description": "The label for category Basics completed in sidebar tutorialSidebar" + "description": "The label for category Basics completed in sidebar tutorial" }, - "sidebar.tutorialSidebar.category.Advanced": { + "sidebar.tutorial.category.Advanced": { "message": "进阶", - "description": "The label for category Advanced in sidebar tutorialSidebar" + "description": "The label for category Advanced in sidebar tutorial" }, - "sidebar.tutorialSidebar.category.Custom": { + "sidebar.tutorial.category.Custom": { "message": "自定义", - "description": "The label for category custom in sidebar tutorialSidebar" + "description": "The label for category custom in sidebar tutorial" }, - "sidebar.tutorialSidebar.category.Request adapter": { + "sidebar.tutorial.category.Migration": { + "message": "迁移指南", + "description": "The label for category Migration in sidebar tutorial" + }, + "sidebar.resource.category.Request Adapter": { "message": "请求适配器", - "description": "The label for category Request adapter in sidebar tutorialSidebar" + "description": "The label for category Request Adapter in sidebar resource" + }, + "sidebar.resource.category.Storage Adapter": { + "message": "存储适配器", + "description": "The label for category Request Adapter in sidebar resource" }, - "sidebar.tutorialSidebar.category.extend hooks": { - "message": "扩展hooks", - "description": "The label for category extend hooks in sidebar tutorialSidebar" + "sidebar.resource.category.Framework": { + "message": "UI框架", + "description": "The label for category Framework in sidebar resource" }, - "sidebar.tutorialSidebar.category.Best practice": { + "sidebar.tutorial.category.Best practice": { "message": "最佳实践", - "description": "The label for category best practice in sidebar tutorialSidebar" + "description": "The label for category best practice in sidebar tutorial" }, - "sidebar.tutorialSidebar.category.Framework": { + "sidebar.tutorial.category.Framework": { "message": "UI框架", - "description": "The label for category Framework in sidebar tutorialSidebar" + "description": "The label for category Framework in sidebar tutorial" }, - "sidebar.tutorialSidebar.category.Others": { + "sidebar.tutorial.category.Others": { "message": "其他", - "description": "The label for category Others in sidebar tutorialSidebar" + "description": "The label for category Others in sidebar tutorial" } } diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/11-others/01-RSM.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/about/01-RSM.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/11-others/01-RSM.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/about/01-RSM.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/11-others/02-comparison.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/about/02-comparison.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/11-others/02-comparison.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/about/02-comparison.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/about/03-Q&A.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/about/03-Q&A.md new file mode 100644 index 000000000..8c70bd750 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/about/03-Q&A.md @@ -0,0 +1,176 @@ +--- +title: 提问&回答 +sidebar_position: 30 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## 为什么创造 alova? + +数据请求一直是应用程序必不可少的重要部分,自从 XMLHttpRequest 诞生以来请求方案层出不穷,客户端的数据交互探索一直聚焦于请求的简单性,如`$.ajax`、`axios`、`fetch api`以及`react-query`等请求工具,编码形式从回调函数、Promise,再到 usehook 不断发展,这些 js 库在请求简单性上已经做得很好了,不过它们只提供了通用的功能,这意味着对于不同的请求场景例如共享请求、分页请求、表单提交、上传和下载文件等,开发者依然需要自己编写复杂的代码,降低开发效率,性能也无法得到保证,在越来越看重的用户体验的时代,应用的流畅性变得越来越重要。 + +同时,客户端和服务端的协作也是割裂的,前端工程师需要查阅 API 文档并手动编写 API 函数,并且服务端 API 的任何更改都需要主动通知前端工程师,这将会使产品变得更加不可控。 + +**而我们认为还有更简单的方案,即根据请求场景,例如分页、表单提交、断点续传等,选择对应的 useHook,它将帮你管理数据,控制何时应该发送请求**。从而让开发者在编写少量代码也能实现更高效地 Client-Server 数据交互。 + +同时,alova 具有很灵活的扩展能力来实现不同场景下的请求策略,你也可以自定义自己的请求场景,这部分内容在[自定义章节](/category/custom)。 + +为了覆盖更多请求场景,我们还将请求场景抽象成了 [请求场景模型(RSM)](/tutorial/others/RSM),它很好地解释了 alova 的请求策略方案。在未来,alova 将承载着我们对请求策略的探索之路继续前行。 + +## 替代请求库? + +alova 是一个请求策略库,它的创建初衷是对不同请求场景提供特定的请求策略解决方案,从而更简洁优雅地实现流畅的请求体验,而例如`$.ajax`、`axios`和`fetch-api`等对请求发送和响应接收提供了很好的支持,它们是 [RSM](/tutorial/others/RSM) 流程中必不可少的一个环节(请求事件),alova 仍然需要依靠它们进行请求,因此我们可以将 alova 看作是请求库的一种武装,让请求库变得更加强大。 + +## 为什么要深度绑定 UI 框架? + +对一个 js 库来说解耦意味着更多场景下的使用,例如 axios 可以在 nodejs 中使用,但同时意味着开发者需要写更多的模板代码,比如使用 useHooks 封装 axios 等。而 alova 摒弃了解耦带来的更多使用场景,将使用范围定位在与 UI 框架配合使用,以最精简的方式使用 alova,这是为了开发者的收益方面而考量的,在一个 UI 框架盛行的时候,深度绑定可以为开发者提供直接使用的功能,提升开发者的使用体验,而不需要太多的模板代码。 + +## 如何通过 cnd 使用 alova? + + + + +```html + + + + + + + + + +
+
Loading...
+
{{ error.message }}
+ responseData: {{ data }} +
+ + + +``` + +
+ + +```html + + + + + + + + + + + +
+ + + +``` + +
+ + +:::tip + +svelte 依赖于编译工具,不能通过 CDN 直接使用,详情见 [svelte.dev](https://svelte.dev/) + +::: + + + + +```html + + + + + + + + + +
+
Loading...
+
{{ todo.error.message }}
+ responseData: {{ todo.data }} +
+ + + +``` + +
+
+ +## 在 React-Native 中要注意什么? + +使用 alova 开发 React-Native 应用时,你也可以使用 `alova/fetch`。 + +但是有以下的注意事项: + +**metro 版本** + +在 alova 中的`package.json`中使用了`exports`来定义多个导出项,因此需要确保这两点: + +1. metro 版本高于 0.76.0 +2. 在`metro.config.js`中开启`resolver.unstable_enablePackageExports`。[详情点此查看](https://facebook.github.io/metro/docs/configuration/#unstable_enablepackageexports-experimental) diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/error.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/error.md new file mode 100644 index 000000000..f95055260 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/error.md @@ -0,0 +1,5 @@ +--- +title: 错误码参考 +--- + +敬请期待... diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-request-adapter/01-alova-mock.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/01-alova-mock.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-request-adapter/01-alova-mock.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/01-alova-mock.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-request-adapter/02-alova-adapter-xhr.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/02-alova-adapter-xhr.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-request-adapter/02-alova-adapter-xhr.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/02-alova-adapter-xhr.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-request-adapter/03-alova-adapter-axios.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/03-alova-adapter-axios.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-request-adapter/03-alova-adapter-axios.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/03-alova-adapter-axios.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-request-adapter/04-alova-adapter-taro.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/04-alova-adapter-taro.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-request-adapter/04-alova-adapter-taro.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/04-alova-adapter-taro.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-request-adapter/05-alova-adapter-uniapp.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/05-alova-adapter-uniapp.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-request-adapter/05-alova-adapter-uniapp.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/05-alova-adapter-uniapp.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/README.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/README.md new file mode 100644 index 000000000..a88834e2d --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/README.md @@ -0,0 +1,9 @@ +--- +title: 请求适配器 +--- + +import DocCardList from '@theme/DocCardList'; + +我们提供了丰富的请求适配器。 + + diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/_category_.json b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/_category_.json new file mode 100644 index 000000000..323e503e1 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/_category_.json @@ -0,0 +1,3 @@ +{ + "label": "Request Adapter" +} diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/02-storage-adapter/01-psc.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/02-storage-adapter/01-psc.md new file mode 100644 index 000000000..426550fa4 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/02-storage-adapter/01-psc.md @@ -0,0 +1,68 @@ +--- +title: 进程共享适配器 +sidebar_position: 10 +--- + +进程共享存储适配器可以让你在多进程模式中共享缓存,可以在 Nodejs 和 Electron 中使用。 + +## 在 Node.js 中使用 + +在 Node.js 环境中,您可以使用 `NodeSyncAdapter` 和 `createNodeSharedCacheSynchronizer` 来实现进程间的缓存同步。 + +1. 在主进程中设置同步器: + +```javascript +const { createPSCSynchronizer, NodeSyncAdapter, is } = require('@alova/psc'); +const cluster = require('cluster'); + +if (cluster.isMaster) { + createPSCSynchronizer(NodeSyncAdapter()); +} else { + // fork worker processes +} +``` + +2. 在子进程中使用适配器: + +```javascript +const { createPSCAdapter, NodeSyncAdapter, ExplictCacheAdapter } = require('@alova/psc'); + +const pscAdapter = createPSCAdapter(NodeSyncAdapter(), new ExplictCacheAdapter()); +createAlova({ + // ... + l1Cache: pscAdapter +}); +``` + +> 你还可以使用[lru-cache](https://www.npmjs.com/package/lru-cache)作为缓存适配器。 + +## 在 Electron 中使用 + +Electron 环境下,您需要使用 `ElectronSyncAdapter` 和 `createPSCSynchronizer` 来实现主进程和渲染进程之间的缓存同步。 + +1. 在主进程中设置同步器: + +```javascript +import { createPSCSynchronizer, ElectronSyncAdapter } from 'your-module-name'; + +function setupMainProcess() { + createPSCSynchronizer(ElectronSyncAdapter()); +} + +setupMainProcess(); +``` + +2. 在渲染进程中使用适配器: + +```javascript +import { createPSCAdapter, ElectronSyncAdapter } from 'your-module-name'; +import { ipcRenderer } from 'electron'; + +const pscAdapter = createPSCAdapter(ElectronSyncAdapter(ipcRenderer)); +createAlova({ + // ... + l1Cache: pscAdapter +}); +``` + +通过这种方式,您可以在 Electron 的主进程和渲染进程之间保持缓存的同步。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/02-storage-adapter/README.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/02-storage-adapter/README.md new file mode 100644 index 000000000..2bdc97622 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/02-storage-adapter/README.md @@ -0,0 +1,7 @@ +--- +title: 存储适配器 +--- + +import DocCardList from '@theme/DocCardList'; + + diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/09-framework/01-vue-options.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/01-vue-options.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/09-framework/01-vue-options.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/01-vue-options.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/09-framework/02-solid.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/02-solid.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/09-framework/02-solid.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/02-solid.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/09-framework/03-angular.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/03-angular.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/09-framework/03-angular.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/03-angular.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/09-framework/04-native-mp.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/04-native-mp.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/09-framework/04-native-mp.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/04-native-mp.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/09-framework/05-preact.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/05-preact.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/09-framework/05-preact.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/05-preact.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/09-framework/06-qwik.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/06-qwik.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/09-framework/06-qwik.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/06-qwik.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/09-framework/07-lit.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/07-lit.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/09-framework/07-lit.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/07-lit.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/09-framework/08-stencil.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/08-stencil.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/09-framework/08-stencil.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/08-stencil.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/README.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/README.md new file mode 100644 index 000000000..1609bc2fc --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/README.md @@ -0,0 +1,5 @@ +--- +title: overview +--- + +sdfasfasdf diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/09-plugin-integration.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/09-extension-integration.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/09-plugin-integration.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/09-extension-integration.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/README.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/README.md index 5de4870e2..2acc89ac9 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/README.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/README.md @@ -36,9 +36,11 @@ alova 提供了 10+个基于[RSM](/tutorial/others/RSM)规范的请求策略模 ### alova 编辑器扩展 -在 vscode 中使用 alova 扩展可以帮你自动生成包含完整的 API 文档标注,响应类型的请求代码。在过去,你需要先查询 API 文档,并不断地在 API 文档与编辑器切换来编写请求代码,使用 alova 插件后,你可以不再需要离开编辑器,直接在编辑器中边查边使用 API,感受不一样的 API 使用体验。 +在 vscode 中使用 alova 扩展可以帮你自动生成包含完整的 API 文档标注,响应类型的请求代码,无论是 ts 项目还是 js 项目,你都可以获得完整的接口查询、接口详细信息,以及响应数据类型的智能提示。 -> 关于 alova 插件的详细介绍,请参考 [集成 IDE 插件](/tutorial/getting-started/plugin-integration)。 +这个扩展也优化了 API 的消费流程,感受不一样的 API 使用体验,在过去,你需要先查询 API 文档,并不断地在 API 文档与编辑器切换来编写请求代码,使用 alova 插件后,你可以不再需要离开编辑器,直接在编辑器中边查边使用 API。 + +> 关于 alova 插件的详细介绍,请参考 [集成编辑器扩展](/tutorial/getting-started/extension-integration)。 ## 有什么不同吗? diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/06-token-authentication.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/06-token-authentication.md index 75f5e768f..a5e286e16 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/06-token-authentication.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/06-token-authentication.md @@ -6,9 +6,9 @@ sidebar_position: 60 import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -:::info +:::info 策略类型 -策略类型:拦截器 +拦截器 ::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/02-auto-invalidate.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/02-auto-invalidate.md index 4710eba6b..40845c678 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/02-auto-invalidate.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/02-auto-invalidate.md @@ -79,6 +79,26 @@ alova.Get('/todo/1', { }); ``` +## 自动失效范围 + +自动失效默认会查找所有 alova 实例下的缓存,太多的失效目标目标可能会导致性能降低,你希望控制自动失效的查找范围或关闭它,你可以按以下方式设置。 + +```js +import { globalConfig } from 'alova'; + +globalConfig({ + /** + * 自动命中缓存开关。 + * 这里有三个选项: + * - global:跨 alova 实例使缓存无效。 + * - self:仅使来自同一 alova 实例的缓存无效。 + * - close:不再自动使缓存无效。 + * 默认为'global' + */ + autoHitCache: 'self' +}); +``` + ## hitSource 数据类型 ```typescript diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/01-update-across-components.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/01-update-across-components.md index 5e2df9454..63ee4d609 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/01-update-across-components.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/01-update-across-components.md @@ -70,7 +70,7 @@ updateState('todoList', todoListRaw => { }); ``` -关于 [method 匹配器](/tutorial/advanced/method-matcher) 将在后面的章节中详细介绍。 +关于 [method 快照匹配器](/tutorial/advanced/method-matcher) 将在后面的章节中详细介绍。 ## 监听匹配事件 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/05-custom-method-key.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/05-custom-method-key.md index 4457c40a1..6681806e1 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/05-custom-method-key.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/05-custom-method-key.md @@ -1,26 +1,53 @@ ---- -title: 自定义method key -sidebar_position: 50 ---- - -:::info version required - -v2.20.0+ - -::: - -method key 用来标识一切与 method 实例关联的数据,有很大的作用,例如: - -- 关联响应数据的缓存 -- 标识共享请求 -- 关联 useRequest 等 useHook 返回的状态值 - -在默认情况下,method key 由 method 实例的相关请求参数生成,它可以准确标识一个请求。 - -但有时候你希望改变它,让以上三个情况在不同的请求中也可以被识别为同一个 method。 - -```javascript -// method key在创建时生成,可以通过__key__自定义它 -const methodInst = alovaInstance.Get('/api/user', {}); -methodInst.__key__ = 'my-custom-method-key'; -``` +--- +title: 自定义method key +sidebar_position: 50 +--- + +:::info 使用范围 + +全范围 + +::: + +method key 用来标识一切与 method 实例关联的数据,有很大的作用,例如: + +- 关联响应数据的缓存 +- 标识共享请求 +- 关联例如 `useRequest` 等 useHook 返回的状态值,以便通过 `updateState` 查找到对应的状态值。 + +在默认情况下,method key 由 method 实例的相关请求参数生成,它可以准确标识一个请求。 + +但有时候你希望改变它,让以上三个情况在不同的请求中也可以被识别为同一个 method。 + +## 自定义 method 实例 key + +```javascript +// method key在创建时生成,可以通过__key__自定义它 +const methodInst = alovaInstance.Get('/api/user', {}); +methodInst.__key__ = 'my-custom-method-key'; +``` + +## 自定义全部 method 实例 key + +method 的 key 通过`Method.prototype.generateKey`生成,你可以重写此方法来改变 method key 生成规则。 + +```javascript +import { Method } from 'alova'; + +Method.prototype.generateKey = function () { + return 'your-custom-method-key'; +}; +``` + +还可以根据 alova 实例设置不同的生成规则。 + +```javascript +Method.prototype.generateKey = function () { + if (this.context === alovaInstance1) { + return 'alova-1-method-key'; + } else { + // ... + } + return 'alova-default-method-key'; +}; +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/07-cache-logger.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/07-cache-logger.md index c871a76df..1a1e9b679 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/07-cache-logger.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/07-cache-logger.md @@ -1,60 +1,60 @@ ---- -title: 缓存命中日志 -sidebar_position: 70 ---- - -:::info 版本要求 - -v2.8.0+ - -::: - -在使用接口缓存时为了便于调试,当请求命中缓存而未发出网络请求时,将默认在控制台打印出命中的缓存信息,这可以解决在使用缓存时的一些困惑。 - -如果你在一些情况下(例如生产环境)不希望打印缓存信息或自定义控制打印缓存信息,alova 也提供了对它们的支持。 - -## 关闭打印缓存命中日志 - -可在创建 alova 实例时将`cacheLogger`设置为`false或null`关闭控制台打印。 - -```javascript -const alovaInstance = createAlova({ - // ... - cacheLogger: false -}); -``` - -你也可以根据不同的环境动态开启与关闭。 - -```javascript -const alovaInstance = createAlova({ - // ... - // 在开发环境开启缓存命中日志 - cacheLogger: process.env.NODE_ENV === 'development' -}); -``` - -## 自定义打印缓存命中日志 - -缓存日志默认通过`console.log`进行打印,如果你的项目环境中不支持`console.log`或其他目的,可以将`cacheLogger`指定为一个函数自定义处理缓存命中的日志。 - -```javascript -const alovaInstance = createAlova({ - // ... - /** - * 自定义的缓存命中日志函数 - * @param response 命中的缓存数据 - * @param method 当前的method实例 - * @param cacheMode 缓存模式 memory或restore - * @param tag restore模式下的tag,只有在对应的缓存设置了tag时有值 - */ - cacheLogger(response, method, cacheMode, tag) { - saveHitCache({ - response, - method, - cacheMode, - tag - }); - } -}); -``` +--- +title: 缓存命中日志 +sidebar_position: 70 +--- + +:::info 使用范围 + +全范围 + +::: + +在使用接口缓存时为了便于调试,当请求命中缓存而未发出网络请求时,将默认在控制台打印出命中的缓存信息。 + +如果你在一些情况下(例如生产环境)不希望打印缓存信息或自定义控制打印缓存信息,你也可以关闭它。 + +## 关闭打印缓存命中日志 + +可在创建 alova 实例时将`cacheLogger`设置为`false/null`关闭控制台打印。 + +```javascript +const alovaInstance = createAlova({ + // ... + cacheLogger: false +}); +``` + +你也可以根据不同的环境动态开启与关闭。 + +```javascript +const alovaInstance = createAlova({ + // ... + // 在开发环境开启缓存命中日志 + cacheLogger: process.env.NODE_ENV === 'development' +}); +``` + +## 自定义打印缓存命中日志 + +缓存日志默认通过`console.log`进行打印,如果你的项目环境中不支持`console.log`或其他目的,可以将`cacheLogger`指定为一个函数自定义处理缓存命中的日志。 + +```javascript +const alovaInstance = createAlova({ + // ... + /** + * 自定义的缓存命中日志函数 + * @param response 命中的缓存数据 + * @param method 当前的method实例 + * @param cacheMode 缓存模式 memory或restore + * @param tag restore模式下的tag,只有在对应的缓存设置了tag时有值 + */ + cacheLogger(response, method, cacheMode, tag) { + saveHitCache({ + response, + method, + cacheMode, + tag + }); + } +}); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/08-manage-extra-states.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/08-manage-extra-states.md index 86d4741a8..03f4714ab 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/08-manage-extra-states.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/08-manage-extra-states.md @@ -1,199 +1,196 @@ ---- -title: 管理额外的状态 -sidebar_position: 80 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -在之前的[跨页面/模块更新响应状态](/tutorial/advanced/update-across-components)章节中,介绍了如何跨页面或模块更新响应状态,但在此章节中我们只介绍了通过`updateState`更新在`useRequest`和`useWatcher`中返回的`data`状态,data 的值总是和响应数据一致,但在很多情况下我们会使用额外的状态来展示(如状态 A)数据,并在请求成功后将 data 数据附加到额外的状态 A 中,如下拉加载的分页方案。在这种情况下,我们就需要将额外的状态 A 进行管理,便于实现跨页面/模块更新它。 - -## 更新单个状态 - -可以在 use hook 调用时通过`managedStates`管理额外的状态,并在其他模块/页面中调用`updateState`时,自动指定状态名称来更新它。 - - - - -```javascript title="A.vue" -const todoList = page => - alova.Get('/todo', { - name: 'todoList' - }); - -const allTodo = ref([]); -useRequest(todoList, { - // ... - - // highlight-start - // 将allTodo作为额外的状态进行管理 - managedStates: { - allTodo - } - // highlight-end -}); -``` - -```javascript title="B.vue" -const handleSuccess = () => { - // highlight-start - // 传入一个对象并指定状态名来查找 - updateState('todoList', { - allTodo: allTodoData => { - // 新增一条todo项 - allTodoData.push({ - title: 'new todo', - time: '10:00' - }); - return allTodoData; - } - }); - // highlight-end -}; -``` - - - - - -```javascript title="A.jsx" -const PageA = () => { - const todoList = page => - alova.Get('/todo', { - name: 'todoList' - }); - - const [allTodo, setAllTodo] = allTodoState = useState([]); - useRequest(todoList, { - // ... - - // highlight-start - // 将allTodo作为额外的状态进行管理 - managedStates: { - allTodo: allTodoState - } - // highlight-end - }); - - return ( - // ... - ); -} -``` - -```javascript title="B.jsx" -const PageB = () => { - // ... - const handleSuccess = () => { - // highlight-start - // 传入一个对象并指定状态名来查找 - updateState('todoList', { - allTodo: allTodoData => { - // 新增一条todo项 - allTodoData.push({ - title: 'new todo', - time: '10:00' - }); - return allTodoData; - } - }); - // highlight-end - }; - - return ( - // ... - ); -} -``` - - - - - -```javascript title="A.svelte" -// a.svelte -const todoList = page => - alova.Get('/todo', { - name: 'todoList' - }); - -const allTodo = ref([]); -useRequest(todoList, { - // ... - - // highlight-start - // 将allTodo作为额外的状态进行管理 - managedStates: { - allTodo - } - // highlight-end -}); -``` - -```javascript title="B.svelte" -const handleSuccess = () => { - // highlight-start - // 传入一个对象并指定状态名来查找 - updateState('todoList', { - allTodo: allTodoData => { - // 新增一条todo项 - allTodoData.push({ - title: 'new todo', - time: '10:00' - }); - return allTodoData; - } - }); - // highlight-end -}; -``` - - - - -:::info 说明 - -不支持管理额外状态。 - -::: - - - - -## 更新多个状态 - -在上面的例子中我们实现了跨页面对单个`allTodo`状态进行更新,实际上,通过`updateState`的对象描述方式可以同时更新任意多个状态。 - -```javascript -updateState('todoList', { - state1: state1Data => { - // ... - }, - state2: state2Data => { - // ... - }, - state3: state3Data => { - // ... - } - // ... -}); -``` - -需要注意的是,以上 3 个额外的状态在更新前,需要通过`managedStates`属性来管理起来。 - -## data 状态更新的简写 - -当只更新 data 状态时,可以直接传入回调函数即可,而不需要指定为对象。 - -```javascript -updateState('todoList', { - data: dataRaw => { - // ... - } -}); - -// 以下为简写 -updateState('todoList', dataRaw => { - // ... -}); -``` +--- +title: 管理额外的状态 +sidebar_position: 80 +--- + +:::info 使用范围 + +客户端 useHook + +::: + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +在之前的[跨页面/模块更新响应状态](/tutorial/advanced/update-across-components)章节中,介绍了如何通过`updateState`跨页面或模块更新响应状态,但它只能更新由 useHooks 创建的状态,如果需要跨组件更新自定义的状态,应该怎么做呢?让我们继续吧! + +## 更新单个状态 + +可以在 useHooks 调用时通过`managedStates`管理额外的状态,并在其他模块/页面中调用`updateState`时,自动指定状态名称来更新它。 + + + + +```javascript title="A.vue" +const todoList = page => + alova.Get('/todo', { + name: 'todoList' + }); + +const allTodo = ref([]); +useRequest(todoList, { + // ... + + // highlight-start + // 将allTodo作为额外的状态进行管理 + managedStates: { + allTodo + } + // highlight-end +}); +``` + +```javascript title="B.vue" +const handleSuccess = () => { + // highlight-start + // 传入一个对象并指定状态名来查找 + updateState('todoList', { + allTodo: allTodoData => { + // 新增一条todo项 + allTodoData.push({ + title: 'new todo', + time: '10:00' + }); + return allTodoData; + } + }); + // highlight-end +}; +``` + + + + + +```javascript title="A.jsx" +const PageA = () => { + const todoList = page => + alova.Get('/todo', { + name: 'todoList' + }); + + const [allTodo, setAllTodo] = allTodoState = useState([]); + useRequest(todoList, { + // ... + + // highlight-start + // 将allTodo作为额外的状态进行管理 + managedStates: { + allTodo: allTodoState + } + // highlight-end + }); + + return ( + // ... + ); +} +``` + +```javascript title="B.jsx" +const PageB = () => { + // ... + const handleSuccess = () => { + // highlight-start + // 传入一个对象并指定状态名来查找 + updateState('todoList', { + allTodo: allTodoData => { + // 新增一条todo项 + allTodoData.push({ + title: 'new todo', + time: '10:00' + }); + return allTodoData; + } + }); + // highlight-end + }; + + return ( + // ... + ); +} +``` + + + + + +```javascript title="A.svelte" +// a.svelte +const todoList = page => + alova.Get('/todo', { + name: 'todoList' + }); + +const allTodo = writable([]); +useRequest(todoList, { + // ... + + // highlight-start + // 将allTodo作为额外的状态进行管理 + managedStates: { + allTodo + } + // highlight-end +}); +``` + +```javascript title="B.svelte" +const handleSuccess = () => { + // highlight-start + // 传入一个对象并指定状态名来查找 + updateState('todoList', { + allTodo: allTodoData => { + // 新增一条todo项 + allTodoData.push({ + title: 'new todo', + time: '10:00' + }); + return allTodoData; + } + }); + // highlight-end +}; +``` + + + + +## 更新多个状态 + +在上面的例子中我们实现了跨页面对单个`allTodo`状态进行更新,实际上,通过`updateState`的对象描述方式可以同时更新任意多个状态。 + +```javascript +updateState('todoList', { + state1: state1Data => { + // ... + }, + state2: state2Data => { + // ... + }, + state3: state3Data => { + // ... + } + // ... +}); +``` + +需要注意的是,以上 3 个额外的状态在更新前,需要通过`managedStates`属性来管理起来。 + +## data 状态更新的简写 + +当只更新 data 状态时,可以直接传入回调函数即可,而不需要指定为对象。 + +```javascript +updateState('todoList', { + data: dataRaw => { + // ... + } +}); + +// 以下为简写 +updateState('todoList', dataRaw => { + // ... +}); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/09-ssr.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/09-ssr.md index 529595d09..b4e167855 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/09-ssr.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/09-ssr.md @@ -1,228 +1,220 @@ ---- -title: 服务端渲染(SSR) -sidebar_position: 90 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -:::info 版本要求 - -2.8.0+ - -::: - -## 概述 - -尽管 alova 的定位并不是在 nodejs 中进行请求,但为了可以结合 UI 框架的服务端渲染([Nuxt3.x](https://nuxt.com/) / [Nextjs](https://nextjs.org/) / [sveltekit](https://kit.svelte.dev/)),我们也对它做了适配。尽管例如`Nuxt3.x`、`Sveltekit`中提供了内置的请求功能,但如果你选择使用 alova 的话,你可以同时在服务端和客户端中使用 alova 管理请求,而不是服务端和客户端分别使用不同的请求方案来管理它们。 - -这里有一些在 SSR 中使用 alova 需要注意的地方,以及不同 UI 框架的 SSR 中的使用示例。 - -## 在服务端调用接口 - -SSR 中经常需要在服务端获取数据并渲染成 HTML,这种情况下我们不能使用 alova 的 use hooks(也无需使用)来获取数据,以下我们将分别对支持的 SSR 框架进行展示。 - -### Nuxt3.x - -在 Nuxt3.x 中提供了`useAsyncData`在服务端初始化页面数据,同时还提供了`useFetch`和`$fetch`请求函数,这些可以同时在服务端和客户端使用的请求函数真的很方便。尽管如此,如果你希望在 nuxt 中使用 alova 的话,你可以使用 **useAsyncData + alova.Method** 组合的方式完成服务端数据获取,这与你平时使用`useAsyncData`没什么区别。 - -```html - -``` - -### Nextjs - -Nextjs 提供了固定的服务端初始化页面数据的函数,如`getStaticProps`、`getServerSideProps`等,可以在函数中[直接使用 method 实例](/tutorial/getting-started/quick-start)调用接口。 - -```jsx -const todoListGetter = alovaInstance.Get('/todo/list', { - headers: { - 'Content-Type': 'application/json;charset=UTF-8' - } -}); - -export const getServerSideProps = async ctx => { - const list = await todoListGetter.send(); - return { - props: { - list - } - }; -}; -export default function App(props) { - return props.list.map(item => ( -
- {item.title} - {item.time} -
- )); -} -``` - -### Sveltekit - -Sveltekit 中也提供了`load`函数进行服务端的页面数据初始化,你同样可以在函数中[直接使用 method 实例](/tutorial/getting-started/quick-start)调用接口。例如在`+page.server.js`中调用接口。 - -```javascript title=+page.server.js -const todoListGetter = alovaInstance.Get('/todo/list', { - headers: { - 'Content-Type': 'application/json;charset=UTF-8' - } -}); - -/** @type {import('./$types').PageServerLoad} */ -export async function load({ params }) { - return { - list: todoListGetter.send() - }; -} -``` - -## 在 SSR 中使用 usehooks - -由于每个 SSR 框架都有各自的在服务端中初始化数据的方式,因此在 SSR 中生成 html 时,组件中的`useRequest`和`useWatcher`即使将`immediate`设置为`true`也不会发起请求,因为这更像是客户端初始化数据。 - -不过,如果你需要像客户端中一样初始化页面的数据,也可以设置`immediate`为`true`,当页面在浏览器中运行时,你可以和往常一样使用 alova 的所有功能。 - -## 注意事项 - -### 客户端和服务端的缓存可能不一致 - -如果你使用了 alova 的缓存功能,这里可能需要注意的是,客户端和服务端的缓存并不是共享的,这意味着如果你在初始化页面时直接使用了**usehooks**获取数据,你可能会遇到客户端和服务端渲染不一致的问题,尽管很少人这样做。 - -请看以下代码片段。 - - - - -```html - - - -``` - - - - -```jsx -function App(props) { - const { loading, data } = useRequest(alovaGetter); - return ( - <> - {loading ?
loading
: null} -
{data}
- - ); -} -``` - -
- - -```html - - -{#if $loading} -
loading
-{/if} -
{{ data }}
-``` - -
-
- -以下代码假设`alovaGetter`请求在服务端存在缓存,但在客户端不存在。 - -此时在服务端生成时 html 时,由于命中缓存,`loading`为`false`而不显示`
loading
`,但在客户端初始化时由于未命中缓存,`loading`为`true`而导致显示`
loading
`,此时 SSR 框架将会提示两个端渲染不一致。 - -**解决方法** - -1. 尽量将页面数据初始化的工作放在获取函数中,而不是组件中; -2. 如果必须这样做,则可以避免在客户端和服务端使用相同的接口,或者关闭出现问题的接口缓存; -3. 如果也需要缓存,你可以在服务端数据初始化函数中清除服务端的缓存,示例代码如下: - - - - -```html - - - -``` - - - - -```jsx -import { invalidateCache } from 'alova'; - -function App(props) { - const { loading, data } = useRequest(alovaGetter); - return ( - <> - {loading ?
loading
: null} -
{data}
- - ); -} - -export const getServerSideProps = async () => { - // 在服务端中清除缓存 - invalidateCache(alovaGetter); - return { - props: {} - }; -}; -``` - -
- - -```javascript title=+page.server.js -import { invalidateCache } from 'alova'; - -/** @type {import('./$types').PageServerLoad} */ -export async function load({ params }) { - // 在服务端中清除缓存 - invalidateCache(alovaGetter); - return {}; -} -``` - - -
+--- +title: 服务端渲染(SSR) +sidebar_position: 90 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## 概述 + +尽管 alova 的定位并不是在 nodejs 中进行请求,但为了可以结合 UI 框架的服务端渲染([Nuxt3.x](https://nuxt.com/) / [Nextjs](https://nextjs.org/) / [sveltekit](https://kit.svelte.dev/)),我们也对它做了适配。尽管例如`Nuxt3.x`、`Sveltekit`中提供了内置的请求功能,但如果你选择使用 alova 的话,你可以同时在服务端和客户端中使用 alova 管理请求,而不是服务端和客户端分别使用不同的请求方案来管理它们。 + +这里有一些在 SSR 中使用 alova 需要注意的地方,以及不同 UI 框架的 SSR 中的使用示例。 + +## 在服务端调用接口 + +SSR 中经常需要在服务端获取数据并渲染成 HTML,这种情况下我们不能使用 alova 的 use hooks(也无需使用)来获取数据,以下我们将分别对支持的 SSR 框架进行展示。 + +### Nuxt3.x + +在 Nuxt3.x 中提供了`useAsyncData`在服务端初始化页面数据,同时还提供了`useFetch`和`$fetch`请求函数,这些可以同时在服务端和客户端使用的请求函数真的很方便。尽管如此,如果你希望在 nuxt 中使用 alova 的话,你可以使用 **useAsyncData + alova.Method** 组合的方式完成服务端数据获取,这与你平时使用`useAsyncData`没什么区别。 + +```html + +``` + +### Nextjs + +Nextjs 提供了固定的服务端初始化页面数据的函数,如`getStaticProps`、`getServerSideProps`等,可以在函数中[直接使用 method 实例](/tutorial/getting-started/quick-start)调用接口。 + +```jsx +export const getServerSideProps = async ctx => { + const list = await alovaInstance.Get('/todo/list', { + headers: { + 'Content-Type': 'application/json;charset=UTF-8' + } + }); + return { + props: { + list + } + }; +}; +export default function App(props) { + return props.list.map(item => ( +
+ {item.title} + {item.time} +
+ )); +} +``` + +### Sveltekit + +Sveltekit 中也提供了`load`函数进行服务端的页面数据初始化,你同样可以在函数中[直接使用 method 实例](/tutorial/getting-started/quick-start)调用接口。例如在`+page.server.js`中调用接口。 + +```javascript title=+page.server.js +const todoListGetter = alovaInstance.Get('/todo/list', { + headers: { + 'Content-Type': 'application/json;charset=UTF-8' + } +}); + +/** @type {import('./$types').PageServerLoad} */ +export async function load({ params }) { + return { + list: todoListGetter + }; +} +``` + +## 在 SSR 中使用 usehooks + +由于每个 SSR 框架都有各自的在服务端中初始化数据的方式,因此在 SSR 中生成 html 时,组件中的`useRequest`和`useWatcher`即使将`immediate`设置为`true`也不会发起请求,因为这更像是客户端初始化数据。 + +不过,如果你需要像客户端中一样初始化页面的数据,也可以设置`immediate`为`true`,当页面在浏览器中运行时,你可以和往常一样使用 alova 的所有功能。 + +## 注意事项 + +### 客户端和服务端的缓存可能不一致 + +如果你使用了 alova 的缓存功能,这里可能需要注意的是,客户端和服务端的缓存并不是共享的,这意味着如果你在初始化页面时直接使用了**usehooks**获取数据,你可能会遇到客户端和服务端渲染不一致的问题,尽管很少人这样做。 + +请看以下代码片段。 + + + + +```html + + + +``` + + + + +```jsx +function App(props) { + const { loading, data } = useRequest(alovaGetter); + return ( + <> + {loading ?
loading
: null} +
{data}
+ + ); +} +``` + +
+ + +```html + + +{#if $loading} +
loading
+{/if} +
{{ data }}
+``` + +
+
+ +以下代码假设`alovaGetter`请求在服务端存在缓存,但在客户端不存在。 + +此时在服务端生成时 html 时,由于命中缓存,`loading`为`false`而不显示`
loading
`,但在客户端初始化时由于未命中缓存,`loading`为`true`而导致显示`
loading
`,此时 SSR 框架将会提示两个端渲染不一致。 + +**解决方法** + +1. 尽量将页面数据初始化的工作放在获取函数中,而不是组件中; +2. 如果必须这样做,则可以避免在客户端和服务端使用相同的接口,或者关闭出现问题的接口缓存; +3. 如果也需要缓存,你可以在服务端数据初始化函数中清除服务端的缓存,示例代码如下: + + + + +```html + + + +``` + + + + +```jsx +import { invalidateCache } from 'alova'; + +function App(props) { + const { loading, data } = useRequest(alovaGetter); + return ( + <> + {loading ?
loading
: null} +
{data}
+ + ); +} + +export const getServerSideProps = async () => { + // 在服务端中清除缓存 + invalidateCache(alovaGetter); + return { + props: {} + }; +}; +``` + +
+ + +```javascript title=+page.server.js +import { invalidateCache } from 'alova'; + +/** @type {import('./$types').PageServerLoad} */ +export async function load({ params }) { + // 在服务端中清除缓存 + invalidateCache(alovaGetter); + return {}; +} +``` + + +
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/10-custom/02-custom-http-adapter.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-custom/01-http-adapter.md similarity index 73% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/10-custom/02-custom-http-adapter.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-custom/01-http-adapter.md index 647b1a2e7..cdd503c7d 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/10-custom/02-custom-http-adapter.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-custom/01-http-adapter.md @@ -1,193 +1,175 @@ ---- -title: 自定义请求适配器 -sidebar_position: 20 ---- - -还记得如何创建一个 Alova 实例吗? - -```javascript -const alovaInstance = createAlova({ - // ... - requestAdapter: GlobalFetch() -}); -``` - -`requestAdapter`就是请求适配器,内部的请求发送和接收都将依赖请求适配器,`GlobalFetch`就是通过 fetch api 的方式来管理请求的,在大多数情况下我们可以使用它,但是,当`alova`运行在 fetch api 不可用的环境时(如 app、小程序),就需要更换一个支持当前环境的请求适配器。 - -那应该如何自定义一个请求适配器呢?很简单,它其实是一个函数,在每次发起请求时都会调用此函数,并返回一个对象,这个对象内包含如`url`、`method`、`data`、`headers`、`timeout`等请求相关的数据集合,虽然字段较多,但我们只需访问我们需要的数据即可。 - -## 请求适配器结构 - -请求适配器将接收到请求相关的参数,以及当前正在请求的 method 实例,并返回一个响应相关的函数集合。 - -```javascript -function CustomRequestAdapter(requestElements, methodInstance) { - // 发送请求... - return { - async response() { - // ...返回响应数据 - }, - async headers() { - // 返回响应头的异步函数 - }, - abort() { - // 中断请求,当外部调用abort时将触发此函数 - }, - onDownload(updateDownloadProgress) { - // 下载进度信息,内部持续调用updateDownloadProgress来更新下载进度 - }, - onUpload(updateUploadProgress) { - // 上传进度信息,内部持续调用updateUploadProgress来更新上传进度 - } - }; -} -``` - -### 请求参数详细 - -**requestElements** - -发送请求的相关元素,包含以下数据。 - -```typescript -interface RequestElements { - // 请求url,get参数已包含其中 - readonly url: string; - - // 请求类型,如GET、POST、PUT等 - readonly type: MethodType; - - // 请求头信息,对象 - readonly headers: Arg; - - // 请求体信息 - readonly data?: RequestBody; -} -``` - -**methodInstance** - -当前请求的 method 实例 - -### 返回参数详细 - -**response(必填)** - -一个异步函数,函数返回响应值,它将会传递给全局的响应拦截器(responded); - -**headers(必填)** - -一个异步函数,函数返回的响应头对象将传递给 Method 实例的 transformData 转换钩子函数; - -**abort(必填)** - -一个普通函数,它用于中断请求,所有的中断请求最终都将会调用此函数来执行; - -**onDownload(可选)** - -一个普通函数,它接收一个更新下载进度的回调函数,在此函数内自定义进度更新的频率,在此示例中模拟每隔 100 毫秒更新一次。`updateDownloadProgress`回调函数接收两个参数,第一个参数是总大小,第二个参数是已下载大小; - -**onUpload(可选)** - -一个普通函数,它接收一个更新上传进度的回调函数,在此函数内自定义进度更新的频率,在此示例中模拟每隔 100 毫秒更新一次。`updateUploadProgress`回调函数接收两个参数,第一个参数是总大小,第二个参数是已上传大小; - -## XMLHttpRequest 请求适配器示例 - -以下是一个通过 XMLHttpRequest 发送请求的适配器的示例,主要用于演示适配器的写法,代码不完整,不可运行。 - -```javascript -function XMLHttpRequestAdapter(requestElements, methodInstance) { - // 解构出需要用到的数据 - const { url, type, data, headers } = config; - - // 发送请求 - const xhr = new XMLHttpRequest(); - xhr.open(type, url); - for (const key in headers) { - xhr.setRequestHeader(key, headers[key]); - } - const responsePromise = new Promise((resolve, reject) => { - xhr.addEventListener('load', event => { - // 处理响应数据 - resolve(/* ... */); - }); - xhr.addEventListener('error', event => { - // 处理请求错误 - reject(/* ... */); - }); - }); - - xhr.send(JSON.stringify(data)); - - return { - // 返回响应数据的异步函数 - response: () => responsePromise, - - // 返回响应头的异步函数 - headers: () => responsePromise.then(() => xhr.getAllResponseHeaders()), - abort: () => { - xhr.abort(); - }, - - // 下载进度信息,内部持续调用updateDownloadProgress来更新下载进度 - onDownload: updateDownloadProgress => { - xhr.addEventListener('progress', event => { - // 数据接收进度 - updateDownloadProgress(event.total, event.loaded); - }); - }, - - // 上传进度信息,内部持续调用updateUploadProgress来更新上传进度 - onUpload: updateUploadProgress => { - xhr.upload.onprogress = event => { - updateUploadProgress(event.total, event.loaded); - }; - } - }; -} -``` - -:::note - -更完整的请求适配器细节可以查阅 [GlobalFetch 源码](https://github.com/alovajs/alova/blob/main/src/predefine/GlobalFetch.ts) 来了解。 - -::: - -## 请求适配器类型 - -全局的`beforeRequest`、`responded`拦截器,以及 method 实例创建时的配置对象的类型,都会根据请求适配器提供的类型自动推断,以下是 GlobalFetch 的类型。 - -```javascript -import type { RequestElements, Method, ProgressUpdater } from 'alova'; - -export type GlobalFetch = () => ( - elements: RequestElements, - method: Method -) => { - response: () => Promise, - headers: () => Promise, - onDownload: (handler: ProgressUpdater) => void, - abort: () => void -}; -``` - -在这个类型中分别指定了`RC`、`RE`和`RH`三个类型的值,因此在全局的拦截器中、method 实例配置中等地方将自动推断为请求适配器给定的类型。 - -它们分别表示为: - -- **RC**:*RequestConfig*的缩写,请求配置对象类型 -- **RH**:*ResponseHeader*的缩写,响应头对象类型 -- **RE**:*Response*的缩写,响应类型 - -如果你正在使用 **GlobalFetch**,他们的类型分别会被推断为: - -- **RC**:fetch api 的请求配置对象`RequestInit`; -- **RH**:响应头对象`Headers`; -- **RE**:响应对象`Response`; - -为了方便定义请求适配器的类型,alova 中也提供了一个适配器类型,你只需要根据需要传入`RC/RE/RH`泛型参数即可。 - -```typescript -import type { AlovaRequestAdapter } from 'alova'; -type CustomRequestAdpater = AlovaRequestAdapter; -``` +--- +title: 请求适配器 +sidebar_position: 10 +--- + +还记得如何创建一个 Alova 实例吗? + +```javascript +import adapterFetch from 'alova/fetch'; + +const alovaInstance = createAlova({ + // ... + requestAdapter: adapterFetch() +}); +``` + +`requestAdapter`就是请求适配器,内部的请求发送和接收都将依赖请求适配器,`alova/fetch`就是通过 fetch api 的方式来管理请求的,在大多数情况下我们可以使用它,但是,当`alova`运行在 fetch api 不可用的环境时(如 app、小程序),就需要更换一个支持当前环境的请求适配器。 + +那应该如何自定义一个请求适配器呢?很简单,它其实是一个函数,在每次发起请求时都会调用此函数,并返回一个对象,这个对象内包含如`url`、`method`、`data`、`headers`、`timeout`等请求相关的数据集合,虽然字段较多,但我们只需访问我们需要的数据即可。 + +## 请求适配器结构 + +请求适配器将接收到请求相关的参数,以及当前正在请求的 method 实例,并返回一个响应相关的函数集合。 + +```javascript +function CustomRequestAdapter(requestElements, methodInstance) { + // 在此处发送请求... + + return { + async response() { + // 返回响应数据 + }, + async headers() { + // 返回响应头的异步函数 + }, + abort() { + // 中断请求,当外部调用abort时将触发此函数 + }, + onDownload(updateDownloadProgress) { + // 下载进度信息,内部持续调用updateDownloadProgress来更新下载进度 + }, + onUpload(updateUploadProgress) { + // 上传进度信息,内部持续调用updateUploadProgress来更新上传进度 + } + }; +} +``` + +### 请求参数详细 + +**requestElements** + +发送请求的相关元素,包含以下数据。 + +```typescript +interface RequestElements { + // 请求url,get参数已包含其中 + readonly url: string; + + // 请求类型,如GET、POST、PUT等 + readonly type: MethodType; + + // 请求头信息,对象 + readonly headers: Arg; + + // 请求体信息 + readonly data?: RequestBody; +} +``` + +**methodInstance** + +当前请求的 method 实例 + +### 返回参数详细 + +**response(必填)** + +一个异步函数,函数返回响应值,它将会传递给全局的响应拦截器(responded); + +**headers(必填)** + +一个异步函数,函数返回的响应头对象将传递给 Method 实例的 transformData 转换钩子函数; + +**abort(必填)** + +一个普通函数,它用于中断请求,所有的中断请求最终都将会调用此函数来执行; + +**onDownload(可选)** + +一个普通函数,它接收一个更新下载进度的回调函数,在此函数内自定义进度更新的频率,在此示例中模拟每隔 100 毫秒更新一次。`updateDownloadProgress`回调函数接收两个参数,第一个参数是总大小,第二个参数是已下载大小; + +**onUpload(可选)** + +一个普通函数,它接收一个更新上传进度的回调函数,在此函数内自定义进度更新的频率,在此示例中模拟每隔 100 毫秒更新一次。`updateUploadProgress`回调函数接收两个参数,第一个参数是总大小,第二个参数是已上传大小; + +## XMLHttpRequest 请求适配器示例 + +以下是一个通过 XMLHttpRequest 发送请求的适配器的示例,主要用于演示适配器的写法,代码不完整,不可运行。 + +```javascript +function XMLHttpRequestAdapter(requestElements, methodInstance) { + // 解构出需要用到的数据 + const { url, type, data, headers } = config; + + // 发送请求 + const xhr = new XMLHttpRequest(); + xhr.open(type, url); + for (const key in headers) { + xhr.setRequestHeader(key, headers[key]); + } + const responsePromise = new Promise((resolve, reject) => { + xhr.addEventListener('load', event => { + // 处理响应数据 + resolve(/* ... */); + }); + xhr.addEventListener('error', event => { + // 处理请求错误 + reject(/* ... */); + }); + }); + + xhr.send(JSON.stringify(data)); + + return { + // 返回响应数据的异步函数 + response: () => responsePromise, + + // 返回响应头的异步函数 + headers: () => responsePromise.then(() => xhr.getAllResponseHeaders()), + abort: () => { + xhr.abort(); + }, + + // 下载进度信息,内部持续调用updateDownloadProgress来更新下载进度 + onDownload: updateDownloadProgress => { + xhr.addEventListener('progress', event => { + // 数据接收进度 + updateDownloadProgress(event.total, event.loaded); + }); + }, + + // 上传进度信息,内部持续调用updateUploadProgress来更新上传进度 + onUpload: updateUploadProgress => { + xhr.upload.onprogress = event => { + updateUploadProgress(event.total, event.loaded); + }; + } + }; +} +``` + +:::note + +更完整的请求适配器细节可以查阅 [alova/fetch 源码](https://github.com/alovajs/alova/blob/main/packages/alova/src/predefine/adapterFetch.ts) 来了解。 + +::: + +## 请求适配器类型 + +全局的`beforeRequest`、`responded`拦截器,以及 method 实例创建时的配置对象的类型,都会根据请求适配器提供的类型自动推断,以下是 `alova/fetch` 的类型。 + +```javascript +import type { AlovaRequestAdapter } from 'alova'; + +export type adapterFetch = () => AlovaRequestAdapter; +``` + +在`AlovaRequestAdapter`中的泛型参数分别为`RequestConfig`、`Response`和`ResponseHeader`三个类型的值,用于在全局的拦截器中、method 实例配置中等地方将自动推断为请求适配器给定的类型。 + +它们分别表示为: + +- **RequestConfig**:请求配置对象类型,它将应用在 method 创建时的`config`参数。 +- **Response**:响应类型,例如`alova/fetch`为 Response 类型 +- **ResponseHeader**:响应头对象类型 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/10-custom/03-custom-storage-adapter.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-custom/02-storage-adapter.md similarity index 57% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/10-custom/03-custom-storage-adapter.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-custom/02-storage-adapter.md index 2ccc85e9e..080c78cac 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/10-custom/03-custom-storage-adapter.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-custom/02-storage-adapter.md @@ -1,48 +1,91 @@ ---- -title: 自定义存储适配器 -sidebar_position: 30 ---- - -alova 中涉及多个需要数据持久化的功能,如持久化缓存、静默提交和离线提交。**在默认情况下,alova 会使用`localStorage`来存储持久化数据**,但考虑到非浏览器环境下,因此也支持了自定义。 - -自定义存储适配器同样非常简单,你只需要指定保存数据、获取数据,以及移除数据的函数即可,大致是这样的。 - -```javascript -const customStorageAdapter = { - set(key, value) { - // 保存数据,value为结构化数据,可调用JSON.stringify转换为字符串 - }, - get(key) { - // 获取数据,需要返回结构化数据,可调用JSON.parse转换为对象 - }, - remove(key) { - // 移除数据 - } -}; -``` - -然后在创建`alova`实例时传入这个适配器即可。 - -```javascript -const alovaInstance = createAlova({ - // ... - storageAdapter: customStorageAdapter -}); -``` - -## SessionStorage 存储适配器示例 - -```javascript -const sessionStorageAdapter = { - set(key, value) { - sessionStorage.setItem(key, JSON.stringify(value)); - }, - get(key) { - const data = sessionStorage.getItem(key); - return data ? JSON.parse(data) : data; - }, - remove(key) { - sessionStorage.removeItem(key); - } -}; -``` +--- +title: 存储适配器 +sidebar_position: 20 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +alova 中涉及多个需要数据持久化的功能,如持久化缓存、静默提交和离线提交。**在默认情况下,alova 会使用`localStorage`来存储持久化数据**,但考虑到非浏览器环境下,因此也支持了自定义。 + +自定义存储适配器同样非常简单,你只需要指定保存数据、获取数据,以及移除数据的函数即可,大致是这样的。 + + + + +```javascript +const customStorageAdapter = { + set(key, value) { + // 保存数据,value为结构化数据,可调用JSON.stringify转换为字符串 + }, + get(key) { + // 获取数据,需要返回结构化数据,可调用JSON.parse转换为对象 + }, + remove(key) { + // 移除数据 + }, + clear() { + // 清空数据 + } +}; +``` + +使用自定义适配器。 + +```javascript +const alovaInstance = createAlova({ + // ... + storageAdapter: customStorageAdapter +}); +``` + + + + +```ts +import { AlovaGlobalCacheAdapter } from 'alova'; + +class CustomStorageAdapter implements AlovaGlobalCacheAdapter { + set(key, value) { + // 保存数据,value为结构化数据,可调用JSON.stringify转换为字符串 + } + get(key) { + // 获取数据,需要返回结构化数据,可调用JSON.parse转换为对象 + } + remove(key) { + // 移除数据 + } + clear() { + // 清空数据 + } +} +``` + +使用自定义适配器。 + +```javascript +const alovaInstance = createAlova({ + // ... + storageAdapter: new CustomStorageAdapter() +}); +``` + + + + +## SessionStorage 存储适配器示例 + +```javascript +const sessionStorageAdapter = { + set(key, value) { + sessionStorage.setItem(key, JSON.stringify(value)); + }, + get(key) { + const data = sessionStorage.getItem(key); + return data ? JSON.parse(data) : data; + }, + remove(key) { + sessionStorage.removeItem(key); + } +}; +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-custom/03-client-strategy.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-custom/03-client-strategy.md new file mode 100644 index 000000000..e76722c1b --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-custom/03-client-strategy.md @@ -0,0 +1,256 @@ +--- +title: 客户端策略 +sidebar_position: 30 +--- + +alova 的客户端策略分为 中间件、拦截器、useHook 三种,当你的项目需要自定义时,你可以参考这个章节。 + +## 中间件 + +中间件提供了强大的、几乎能控制一个请求的所有行为的能力,你可以通过它控制请求行为、自定义修改请求状态、错误处理等,详情请前往[请求中间件](/tutorial/advanced/middleware)查看,以下的源码可以告诉你中间件到底能做什么。 + +- [actionDelegationMiddleware](https://github.com/alovajs/alova/blob/main/packages/client/src/middlewares/actionDelegation.ts) 通过中间件中实现了跨组件触发请求。 +- [useSQRequest](https://github.com/alovajs/alova/blob/main/packages/client/src/hooks/silent/useSQRequest.ts) 在中间件中实现立即响应请求,无需等待。 +- [useSerialRequest](https://github.com/alovajs/alova/blob/main/packages/client/src/hooks/serial/useSerialRequest.ts) 在中间件中串行请求并管理多个请求的响应数据。 +- [useRetriableRequest](https://github.com/alovajs/alova/blob/main/packages/client/src/hooks/useRetriableRequest.ts) 在中间件中重试失败的请求。 +- [延迟更新 loading](/tutorial/best-practice/middleware)示例。 + +## 拦截器 + +拦截器控制全局的请求前后行为,我们可以包装全局拦截器实现特定功能的拦截器。 + +以下是一个示例,它在特定条件下中断所有进行中的请求。 + +```js +const methodsAborter = (handler, detector) => { + let requestingMethods = []; + // 返回请求前拦截器 + return method => { + if (detector()) { + requestingMethods.forEach(method => { + method.abort(); + }); + return; + } + requestingMethods.push(method); + method.promise + ?.then(() => { + requestingMethods = requestingMethods.filter(item => item !== method); + }) + .catch(() => {}); // 防止抛出错误 + handler(method); + }; +}; + +createAlova({ + beforeRequest: methodsAborter( + method => { + // 原请求前钩子 + }, + () => { + // 中断请求判断条件 + return false; + } + ) +}); +``` + +一个更复杂的示例,[token 认证拦截器](https://github.com/alovajs/alova/blob/main/packages/client/src/functions/tokenAuthentication/createTokenAuthentication.ts) + +## useHook + +useHook 是 alova 最常用的请求策略,而且它是跨 UI 框架的,当你为特定 UI 框架编写 useHook 时和普通的 useHook 一样编写即可,这里我们主要了解编写跨 UI 框架的 useHook 编写。 + +我们创新性地使用了**状态代理**来抹平 UI 框架状态的差异性,它的用法类似 Vue 的 ref 值,只需要通过简单的访问 v 属性,或对 v 属性赋值,不再需要关心 UI 框架的差异性。 + +为了抹平状态代理的差异性,我们提供了`statesHookHelper`函数来创建辅助函数,通过这些辅助函数来实现跨 UI 框架的 useHook。 + +```js +import { statesHookHelper } from '@alova/shared/function'; +import { promiseStatesHook } from 'alova'; + +function myUseHook(methodHandler, options) { + const { + create, + computed, + ref, + onMounted, + onUnmount, + watch, + objectify, + exposeProvider, + __referingObj + } = statesHookHelper(promiseStatesHook()); +} +``` + +接下来让我们来详细了解他们。 + +### 创建状态 + +使用`create`创建 UI 框架无关状态代理 FrameworkState,以 loading 状态为例。 + +```js +// 参数1:为初始值 +// 参数2:导出key +const loading = create(false, 'loading'); + +// 获取原始值 +const dehydratedLoading = loading.v; +// 更新状态值 +loading.v = true; +// 获取导出值,内部调用`statesHook.export`导出值 +const exportedLoading = loading.e; +// 获取通过statesHook.create创建的,平台相关的状态值 +const platformedState = loading.s; +``` + +### 创建计算属性 + +使用`computed`创建 UI 框架无关的计算属性,为了兼容 react,需要传入计算属性依赖项,它可以是平台相关的状态值和 FrameworkState。 + +```js +// 参数1:计算属性的函数 +// 参数2:计算属性依赖项 +// 参数3:导出key +const computedState = computed(() => !loading.v, [loading], 'states'); + +// 获取原始值 +const dehydratedComputedState = computedState.v; +// 获取导出值,内部调用`statesHook.export`导出值 +const exportedComputedState = computedState.e; +// 获取通过statesHook.computed创建的,平台相关的状态值 +const platformedComputedState = computedState.s; +``` + +### 创建引用值 + +它主要用于跨越 react 的闭包陷阱,内部调用`useRef`,其他框架直接返回`{ current: value }`对象,,通过`unifiedValue.current`访问和更新。 + +```js +const unifiedValue = ref({}); +``` + +### 组件挂载钩子 + +```js +onMounted(() => {}); +``` + +### 组件卸载钩子 + +```js +onUnmount(() => {}); +``` + +### 监听状态变化 + +```js +// 参数1:监听项,用于兼容react,可传平台相关的状态值和FrameworkState +// 参数2:回调函数 +watch([loading, computedState.e], () => {}); +``` + +### 状态对象化 + +将 UI 框架无关的状态代理数组转换为状态对象,一般配合`exposeProvider`使用,以 key 为键,第二个参数还可以指定对象的取值。 + +```js +const states = objectify([loading, data, error]); +/* states的值为 +{ + loading: loading, + data: data, + error: error +} +*/ + +const states = objectify([loading, data, error], 's'); +/* states的值为 +{ + loading: loading.s, + data: data.s, + error: error.s +} +*/ +``` + +### 暴露内部数据 + +如果直接导出 useHook 的内部信息,将变得无法使用,因此提供了`exposeProvider`来导出,它会自动帮我们处理以下内容: + +1. 将状态代理自动转换为对应 UI 框架的状态。 +2. 提供一个统一的 update 函数,如果传入参数中包含了 useRequest 返回的 states 和 update 函数,也会自动兼容这些。 +3. 在 react 中,非 on 开头的函数使用 memorize 包裹,已包裹的不再包裹。 +4. on 开头的视为事件绑定函数,会直接添加到导出对象中。 +5. 导出统一的 referingObject。 + +以下是一个`usePagination`的导出示例: + +```js +export const usePagination = (/* ... */) => { + return exposeProvider({ + // 当前useHook中useWatcher的返回对象 + ...useWatcherReturns, + + // 状态数组转对象 + ...objectify([page, pageSize, data, pageCount, total, isLastPage]), + + // 操作函数 + reset: () => { + // ... + }, + + // 事件绑定函数 + onFetchSuccess: fetchState.onSuccess + // ... + }); +}; +``` + +### `__referingObj` 说明 + +`__referingObj`是为了兼容 options style 和 class style 的 UI 框架,它是一个普通的引用对象,用来同步 options 和 class style 的组件对象,从而让 statesHook 内可以访问到对应组件对象。自定义 useHook 内需要使用同一个 referingObj 对象,这样才能保证一个 useHook 内的 states 可以访问到同一个组件对象。 +`__referingObj` 会在 `statesHookHelper` 中创建并返回,也不需要具体处理,只需要按以下方式导出即可。 + +当 scene hook 内使用了 alova 核心 hook 时,将这个对象传入 hook 内,同时保证在这个 useHook 导出。 + +> 如果不传入 referingObj,则核心 hook 会自动在内部创建 + +```js + +export const useXXX = (...) => { + const { + __referingObj, + // ... + } = statesHookHelper(promiseStatesHook()); + + const states = useReqest(methodHandler, { + __referingObj + }); + + return { + // ... + __referingObj, + } +} +``` + +当 scene hook 内没有使用 alova 核心 hook 时,直接在这个 useHook 导出 referingObj 对象。 + +```js +export const useXXX = (/* ... */) => { + const { + __referingObj + // ... + } = statesHookHelper(promiseStatesHook()); + // ... + + return { + // ... + __referingObj + }; +}; +``` + +当你使用`exposeProvider`导出信息时,它将自动导出`__referingObj`,而不需要我们手动处理。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-custom/04-server-strategy.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-custom/04-server-strategy.md new file mode 100644 index 000000000..01beab4f2 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-custom/04-server-strategy.md @@ -0,0 +1,45 @@ +--- +title: 服务端策略 +sidebar_position: 40 +--- + +服务端策略也被称为`Server hook`,是一个 method 实例的装饰函数。 + +以下是 server hooks 的规范,它接收 method 实例并返回一个新的 method 实例,因此可以很方便的组合多个 server hooks。 + +```ts +export interface AlovaServerHook> { + (method: Method, options: Options): Method; +} +``` + +自定义`Server hook`非常简单,以下是一个简单的请求重试示例。 + +```js +import { HookedMethod } from 'alova/server'; + +const retry = (method, options) => { + const { retry: maxRetryTimes = 3, delay = 1000 } = options; + let retryTimes = 0; + + return new HookedMethod(method, forceRequest => + method.send(forceRequest).then( + value => value, + error => { + if (retryTimes < maxRetryTimes) { + retryTimes += 1; + setTimeout(() => { + method.send(forceRequest).catch(noop); + }, delay); + } + } + ) + ); +}; + +// 使用 +const userData = await retry(alova.Get('/api/user'), { + retry: 5, + delay: 1500 +}); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-custom/05-stateshook.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-custom/05-stateshook.md new file mode 100644 index 000000000..44cf5bdd7 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-custom/05-stateshook.md @@ -0,0 +1,122 @@ +--- +title: States Hook +sidebar_position: 50 +--- + +还记得如何创建一个 Alova 实例吗? + +```javascript +const alovaInstance = createAlova({ + // ... + statesHook: ReactHook +}); +``` + +`statesHook`将决定在请求时返回哪个 UI 库的状态。在大部分情况下你不需要自定义`statesHook`,但如果你需要适配更多 alova 不支持的 MVVM 库,就需要自定义编写`statesHook`了。 + +`statesHook`是一个包含特定函数的普通对象,我们来看看 **VueHook** 是怎么编写的吧。 + +## statesHook 结构 + +statesHook 是一个对象,以下是它的类型定义。 + +```ts +interface StatesHook { + /** + * 创建状态 + * @param initialValue 初始数据 + * @returns 状态值 + */ + create: (initialValue: any, referingObject: ReferingObject) => State; + + /** + * 创建计算状态 + * @param initialValue 初始数据 + * @param referingObject 引用对象 + */ + computed: (getter: () => any, deps: Export[], referingObject: ReferingObject) => Computed; + + /** + * 导出给开发者使用的值 + * @param state 状态值 + * @param referingObject refering object + * @returns 导出的值 + */ + export?: (state: State, referingObject: ReferingObject) => Export; + + /** 将状态转换为普通数据 */ + dehydrate: (state: State, key: string, referingObject: ReferingObject) => any; + + /** + * 更新状态值 + * @param newVal 新的数据集合 + * @param state 原状态值 + * @param @param referingObject refering object + */ + update: (newVal: any, state: State, key: string, referingObject: ReferingObject) => void; + + /** + * 控制执行请求的函数,此函数将在useRequest、useWatcher被调用时执行一次 + * 在useFetcher中的fetch函数中执行一次 + * 当watchingStates为空数组时,执行一次handleRequest函数 + * 当watchingStates为非空数组时,当状态变化时调用,immediate为true时,需立即调用一次 + * hook是use hook的实例,每次use hook调用时都将生成一个hook实例 + * 在vue中直接执行即可,而在react中需要在useEffect中执行 + * removeStates函数为清除当前状态的函数,应该在组件卸载时调用 + */ + effectRequest: ( + effectParams: EffectRequestParams, + referingObject: ReferingObject + ) => void; + + /** + * 包装send、abort等use hooks操作函数 + * 这主要用于优化在react中,每次渲染都会生成新函数的问题,优化性能 + * @param fn use hook操作函数 + * @returns 包装后的操作函数 + */ + memorize?: any>(fn: Callback) => Callback; + + /** + * 创建引用对象 + * @param initialValue 初始值 + * @returns 包含初始值的引用对象 + */ + ref?: (initialValue: D) => { current: D }; + + /** + * 状态监听 + * @param source 监听的状态 + * @param callback 状态改变回调函数 + * @param referingObject refering object + */ + watch: (source: Watched[], callback: () => void, referingObject: ReferingObject) => void; + + /** + * 组件挂载钩子 + * @param callback 回调函数 + * @param referingObject refering object + */ + onMounted: (callback: () => void, referingObject: ReferingObject) => void; + + /** + * 组件卸载钩子 + * @param callback 回调函数 + * @param referingObject refering object + */ + onUnmounted: (callback: () => void, referingObject: ReferingObject) => void; +} +``` + +:::warning 注意 + +如果 statesHook 涉及与`react`相似,当每次重新渲染都会调用`alova`的 use hook 时,需要在`effectRequest`中每次重新渲染时触发`saveStates`函数,这是因为`react`每次重新渲染都会刷新它的状态引用,因此我们需要再次重新保存它们。 + +::: + +以下是留下 UI 框架的 statesHook 源码。 + +- [react hook](https://github.com/alovajs/alova/blob/main/packages/client/src/statesHook/react.ts) +- [vue hook](https://github.com/alovajs/alova/blob/main/packages/client/src/statesHook/vue.ts) +- [svelte hook](https://github.com/alovajs/alova/blob/main/packages/client/src/statesHook/svelte.ts) +- [vue options hook](https://github.com/alovajs/alova/blob/main/packages/vue-options/src/stateHook.ts) diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-custom/README.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-custom/README.md new file mode 100644 index 000000000..b66342b5c --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-custom/README.md @@ -0,0 +1,31 @@ +--- +title: 概览 +--- + +alova 具有很高的扩展性,它除了提供核心的缓存机制、请求共享机制以及状态管理等通用特性外,还提供了各类定制功能以及中间件机制,可以适配不同 js 环境,以及自定义请求策略,在接下来的章节中,我们将会详细介绍。 + +## 自定义适配器 + +为了满足 js 在不同环境下的运行需求,你可以自定义请求适配器、存储适配器,甚至是 UI 框架的状态适配器,在接下来的章节中也会详细介绍。以下列出了一些适配器示例。 + +- [fetch 适配器](https://github.com/alovajs/alova/blob/main/packages/alova/src/predefine/adapterFetch.ts) +- [localStorage 存储适配器](https://github.com/alovajs/alova/blob/main/packages/alova/src/defaults/cacheAdapter.ts) +- [vue states hook](https://github.com/alovajs/alova/blob/main/packages/client/src/statesHook/vue.ts) + +你也可以将多个类型的适配器组成一个集合,例如[Uniapp 适配器](/tutorial/request-adapter/alova-adapter-uniapp)。 + +## 自定义客户端策略 + +alova 提供了 10+个自定义的客户端策略模块,但有时候可能你需要编写自己的策略模块,通常情况下,一个自定义的请求策略是基于`useRequest`、`useWatcher` 和 `useFetcher` 三个核心 useHook 的组合,以及为它们编写[中间件](/tutorial/advanced/middleware)、缓存的操纵函数来控制它们的请求方式,从而实现各种效果的请求策略 + +以下的策略模块具有很好的代表性,强烈建议你参考源码寻找灵感。 + +- [usePagination 源码](https://github.com/alovajs/scene/blob/main/packages/client/src/hooks/pagination/usePagination.ts) +- [useCaptcha 源码](https://github.com/alovajs/scene/blob/main/packages/client/src/hooks/useCaptcha.ts) +- [useForm 源码](https://github.com/alovajs/scene/blob/main/packages/client/src/hooks/useForm.ts) + +## 自定义服务端策略 + +服务端的策略模块是一个简单的函数,以下是一个请求重试的`Server hook`。 + +- [请求重试](https://github.com/alovajs/scene/blob/main/packages/server/src/hooks/retry.ts) diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-custom/_category_.json b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-custom/_category_.json new file mode 100644 index 000000000..0b24b5159 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-custom/_category_.json @@ -0,0 +1,3 @@ +{ + "label": "Custom" +} diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-request-adapter/_category_.json b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-request-adapter/_category_.json deleted file mode 100644 index b2734b673..000000000 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-request-adapter/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "扩展", - "link": { - "type": "generated-index" - } -} diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/09-migration/01-v2-to-v3.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/09-migration/01-v2-to-v3.md new file mode 100644 index 000000000..8c3f7d11a --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/09-migration/01-v2-to-v3.md @@ -0,0 +1,6 @@ +--- +title: v3 升级指南 +sidebar_position: 10 +--- + +alova@3 处于 beta 中,暂不建议在实际项目中升级。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/09-migration/02-from-axios.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/09-migration/02-from-axios.md new file mode 100644 index 000000000..0fd015016 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/09-migration/02-from-axios.md @@ -0,0 +1,6 @@ +--- +title: 从axios迁移 +sidebar_position: 20 +--- + +敬请期待... diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/10-custom/01-overview.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/10-custom/01-overview.md deleted file mode 100644 index 4b9513613..000000000 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/10-custom/01-overview.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: 概览 -sidebar_position: 10 ---- - -alova 具有很高的扩展性,它除了提供核心的缓存机制、请求共享机制以及状态管理等通用特性外,还提供了各类定制功能以及中间件机制,可以适配不同 js 环境,以及自定义请求策略。 - -## 适配器 - -为了满足 js 在不同环境下的运行需求,你可以自定义请求适配器、存储适配器,甚至是 UI 框架的状态适配器,在接下来的章节中也会详细介绍。以下列出了一些适配器示例。 - -- [fetch 适配器](https://github.com/alovajs/alova/blob/main/src/predefine/GlobalFetch.ts) -- [localStorage 存储适配器](https://github.com/alovajs/alova/blob/main/src/predefine/globalLocalStorage.ts) -- [vue states hook](https://github.com/alovajs/alova/blob/main/src/predefine/VueHook.ts) - -你也可以将多个类型的适配器组成一个集合,例如[Uniapp 适配器](/tutorial/request-adapter/alova-adapter-uniapp)。 - -## 编写请求策略 - -alova 的请求策略和 alova 核心库是分开的,目的是为了开发者们也可以利用 alova 的高扩展性来编写自己的请求策略。通常情况下,一个自定义的请求策略是基于`useRequest`、`useWatcher` 和 `useFetcher` 三者的组合,以及为它们编写[中间件](/tutorial/advanced/middleware)、缓存的操纵函数来控制它们的请求方式,从而实现各种效果的请求策略。 - -**@alova/scene** 中的请求策略具有很好的代表性,强烈建议你参考源码寻找灵感。 - -- [分页请求策略源码](https://github.com/alovajs/scene/blob/main/src/hooks/pagination/usePagination.js) -- [验证码策略源码](https://github.com/alovajs/scene/blob/main/src/hooks/useCaptcha.ts) -- [表单提交策略源码](https://github.com/alovajs/scene/blob/main/src/hooks/useForm.ts) diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/10-custom/04-custom-stateshook.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/10-custom/04-custom-stateshook.md deleted file mode 100644 index 7266e4e2f..000000000 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/10-custom/04-custom-stateshook.md +++ /dev/null @@ -1,101 +0,0 @@ ---- -title: 自定义States Hook -sidebar_position: 50 ---- - -还记得如何创建一个 Alova 实例吗? - -```javascript -const alovaInstance = createAlova({ - // ... - statesHook: ReactHook -}); -``` - -`statesHook`将决定在请求时返回哪个 UI 库的状态,alova 目前提供了**VueHook、ReactHook、svelteHook**。 - -在大部分情况下你应该用不到这个功能,但如果你需要适配更多 alova 不支持的 MVVM 库,就需要自定义编写`statesHook`了。 - -`statesHook`是一个包含特定函数的普通对象,不过这些还是基本不涉及算法,我们来看看 **VueHook** 是怎么编写的吧。 - -## statesHook 结构 - -statesHook 使用一个对象进行表示,以下为**VueHook**的示例。 - -```javascript -import { ref, watch, onUnmounted } from 'vue'; - -const VueHook = { - // 状态创建函数 - create: rawData => ref(data), - - // 状态导出函数 - export: state => state, - - // 脱水函数 - dehydrate: state => state.value, - - // 响应式状态更新函数 - update: (newVal, states) => { - Object.keys(newVal).forEach(key => { - states[key].value = newVal[key]; - }); - }, - - // 请求发送控制函数 - effectRequest({ handler, removeStates, saveStates, immediate, frontStates, watchingStates }) { - // 组件卸载时移除对应状态 - onUnmounted(removeStates); - - // 调用useRequest和useFetcher时,watchingStates为undefined - if (!watchingStates) { - handler(); - return; - } - - // 调用useWatcher时,watchingStates为需要监听的状态数组 - // immediate为true时,表示需要立即发送请求 - watch(watchingStates, handler, { immediate }); - } -}; -``` - -## 自定义 statesHook 函数说明 - -> 以下 5 个函数均必须指定。 - -**create** - -响应式状态创建函数,`loading`、`error`、`data`、`downloading`、`uploading`等都是调用此函数创建的,如 vue3 项目下将创建为 ref 值; - -**export** - -状态导出函数,此函数接收 create 函数创建的响应式状态,并导出最终给开发者使用的状态,这里`VueHook`导出的状态是原状态; - -**dehydrate** - -脱水函数,意思是将响应式状态转换为普通数据,与 create 是相反的操作,在`updateState`中; - -**update** - -响应式状态更新函数,`alova`内部维护的状态更新都是通过此函数完成。此函数接收两个参数,第一个参数是新的数据对象,第二个参数是原响应式状态的 map 集合,这里你可以固定写一个循环更新`states`; - -**effectRequest** - -请求发送控制函数,它会在`useRequest`、`useWatcher`、`useFetcher`被调用时立即执行此函数,我们要在这个函数内要完成三件事: - -1. 当前组件卸载时,调用 removeStates 函数移除当前组件涉及到的响应式状态,避免内存溢出; -2. 当调用 useWatcher 时,绑定状态监听,状态改变时调用 sendRequest 函数,你可以用`states`是否为数组判断是否为`useWatcher`被调用,同时,`immediate`参数用于判断`useWatcher`调用时是否需要立即发送请求; -3. 当调用`useRequest`和`useFetcher`时,调用 sendRequest 发出一次请求,此时`states`为`undefined`; - -:::warning 注意 - -如果 statesHook 涉及的库是像`react`,每次重新渲染都会调用`alova`的 use hook 的,那么在`effectRequest`中还需要在每次重新渲染时触发`saveStates`函数,这是因为`react`每次重新渲染都会刷新它的状态引用,因此我们需要再次重新保存它们。 - -::: - -[ReactHook 源码点此查看](https://github.com/alovajs/alova/blob/main/src/predefine/ReactHook.ts) - -## statesHook 类型 - -如果你在自定义 statesHook 时,也希望它可以支持 typescript,可以 [点此查看](/tutorial/combine-framework/typescript) diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/10-custom/_category_.json b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/10-custom/_category_.json deleted file mode 100644 index 505fadaab..000000000 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/10-custom/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Custom", - "link": { - "type": "generated-index" - } -} diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/11-others/03-Q&A.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/11-others/03-Q&A.md deleted file mode 100644 index b1b4684db..000000000 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/11-others/03-Q&A.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -title: 提问&回答 -sidebar_position: 30 ---- - -## 为什么创造 alova? - -数据请求一直是应用程序必不可少的重要部分,自从 XMLHttpRequest 诞生以来请求方案层出不穷,客户端的数据交互探索一直聚焦于请求的简单性,如`$.ajax`、`axios`、`fetch api`以及`react-query`等请求工具,编码形式从回调函数、Promise,再到 usehook 不断发展,这些 js 库在请求简单性上已经做得很好了,不过它们只提供了通用的功能,这意味着对于不同的请求场景例如共享请求、分页请求、表单提交、上传和下载文件等,开发者依然需要自己编写复杂的代码,降低开发效率,性能也无法得到保证,在越来越看重的用户体验的时代,应用的流畅性变得越来越重要。 - -同时,客户端和服务端的协作也是割裂的,前端工程师需要查阅 API 文档并手动编写 API 函数,并且服务端 API 的任何更改都需要主动通知前端工程师,这将会使产品变得更加不可控。 - -**而我们认为还有更简单的方案,即根据请求场景,例如分页、表单提交、断点续传等,选择对应的 useHook,它将帮你管理数据,控制何时应该发送请求**。从而让开发者在编写少量代码也能实现更高效地 Client-Server 数据交互。 - -同时,alova 具有很灵活的扩展能力来实现不同场景下的请求策略,你也可以自定义自己的请求场景,这部分内容在[自定义章节](/category/custom)。 - -为了覆盖更多请求场景,我们还将请求场景抽象成了 [请求场景模型(RSM)](/tutorial/others/RSM),它很好地解释了 alova 的请求策略方案。在未来,alova 将承载着我们对请求策略的探索之路继续前行。 - -## 替代请求库? - -alova 是一个请求策略库,它的创建初衷是对不同请求场景提供特定的请求策略解决方案,从而更简洁优雅地实现流畅的请求体验,而例如`$.ajax`、`axios`和`fetch-api`等对请求发送和响应接收提供了很好的支持,它们是 [RSM](/tutorial/others/RSM) 流程中必不可少的一个环节(请求事件),alova 仍然需要依靠它们进行请求,因此我们可以将 alova 看作是请求库的一种武装,让请求库变得更加强大。 - -## 为什么要深度绑定 UI 框架? - -对一个 js 库来说解耦意味着更多场景下的使用,例如 axios 可以在 nodejs 中使用,但同时意味着开发者需要写更多的模板代码,比如使用 useHooks 封装 axios 等。而 alova 摒弃了解耦带来的更多使用场景,将使用范围定位在与 UI 框架配合使用,以最精简的方式使用 alova,这是为了开发者的收益方面而考量的,在一个 UI 框架盛行的时候,深度绑定可以为开发者提供直接使用的功能,提升开发者的使用体验,而不需要太多的模板代码。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/11-others/04-future.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/11-others/04-future.md deleted file mode 100644 index dbf3efd7d..000000000 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/11-others/04-future.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -title: alova的未来 -sidebar_position: 40 ---- - -alovajs 的定位是轻量级的请求策略库,目前在请求功能和请求策略方面提供了较好的支持,但 alovajs 的未来不止于此。 - -## 更多的请求策略 - -这是一直不变的方向,我们会持续探索基于常见业务下的高效易用的请求策略。 - -## 更多的 UI 框架支持 - -alovajs 虽然是一个基于 UI 框架的请求工具,但它的灵活设计支持我们在各种 UI 框架中使用,最终将会兼容以下的 UI 框架和 js 环境: - -- 函数风格框架,如`react/react-native/vue-composntion/svelte/solid/preact/qwik`。 -- SSR 框架,如`next/nuxt/sveltekit`。 -- class 风格框架,如`angular/lit/stencil`。 -- options 风格框架,如`vue-options/原生小程序(中国🇨🇳)`。 -- 多端适配框架,如`Uniapp/Taro`(中国 🇨🇳)。 - -详情请[前往 UI 框架](/category/framework)中查看。 - -## API 的自动管理和维护 - -在未来,alovajs 还致力于解决前端 API 的问题,并更进一步地简化前端开发的工作流,这就是 alovajs 的下一个发展方向:**API 的自动管理和维护**,具体包含以下三点。 - -1. 自动生成 ts 类型完整的、描述完整的请求函数,无论是 js 项目还是 ts 项目,都调用无需引入,让开发者就像直接调用`location.reload`这样方便,并且请求函数可直接看到完整的描述和请求参数类型提示,这些都可以由 openAPI 自动生成。 - -2. 由于自动生成的请求函数拥有完整的描述和类型提示,开发一个 vscode 插件通过关键字来快速检索需要使用的 API,你也不再需要去查阅 API 文档了。 - -3. 解决前后端协作断层的问题,接口的任何变动前端可知,可以在启动项目时被通知,如果在构建项目时发现存在变动,则会抛出错误停止构建,如果是 ts 项目也会在编译时抛出错误,也可以通过 vscode 插件查看变动记录。 - -## 开发清单 - -[点此查看所有规划的开发清单](/contributing/become-core-member#%E5%8F%AF%E5%8F%82%E4%B8%8E%E7%9A%84%E4%BB%93%E5%BA%93%E5%88%97%E8%A1%A8) diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/11-others/05-react-native.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/11-others/05-react-native.md deleted file mode 100644 index cd224705a..000000000 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/11-others/05-react-native.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -title: 开发React-Native应用 -sidebar_position: 50 ---- - -你同样可以使用 alova 开发 React-Native 应用,甚至可以直接使用 GlobalFetch 请求适配器来作为请求事件处理。 - -但是有以下的注意事项: - -## metro 版本 - -在 alova 中的`package.json`中使用了`exports`来定义多个导出项,因此需要确保这两点: - -1. metro 版本高于 0.76.0 -2. 在`metro.config.js`中开启`resolver.unstable_enablePackageExports`。[详情点此查看](https://facebook.github.io/metro/docs/configuration/#unstable_enablepackageexports-experimental) diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/11-others/06-use-in-static.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/11-others/06-use-in-static.md deleted file mode 100644 index e3eb87d6f..000000000 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/11-others/06-use-in-static.md +++ /dev/null @@ -1,139 +0,0 @@ ---- -title: 在静态 html 中使用 -sidebar_position: 60 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -你可以使用 cdn 的方式使用 alova。 - - - - -```html - - - - - - - - - -
-
Loading...
-
{{ error.message }}
- responseData: {{ data }} -
- - - -``` - -
- - -```html - - - - - - - - - - - -
- - - -``` - -
- - -:::tip - -svelte 依赖于编译工具,不能通过 CDN 直接使用,详情见 [svelte.dev](https://svelte.dev/) - -::: - - - - -```html - - - - - - - - - -
-
Loading...
-
{{ todo.error.message }}
- responseData: {{ todo.data }} -
- - - -``` - -
-
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/11-others/07-hide-recommend-tips.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/11-others/07-hide-recommend-tips.md deleted file mode 100644 index f538b1e3b..000000000 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/11-others/07-hide-recommend-tips.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -title: 隐藏推荐提示 -sidebar_position: 70 ---- - -:::info 版本要求 - -v2.7.0- - -::: - -alova 可以配合扩展库获得更好的开发体验,为了让更多开发者获得更好的开发体验,在使用时将会在控制台中推荐 alova 的扩展。 - -![tips](/img/alova-tips.png) - -这些提示代码将会在构建生产环境包时自动去除,如果你希望在开发环境隐藏它们,可以按以下方式: - -## Vite - -在`.env.development`文件中设置环境变量 **VITE_ALOVA_TIPS=0** - -```bash title=.env.development -VITE_ALOVA_TIPS=0 -``` - -:::warning 警告 -如果信息仍然存在,请尝试将 vite 的依赖缓存删除,它在`node_modules/.vite/deps`。 -::: - -## Webpack - -### Vue - -在`.env.development`文件中设置环境变量 **VUE_APP_ALOVA_TIPS=0** - -```bash title=.env.development -VUE_APP_ALOVA_TIPS=0 -``` - -### React - -在`.env.development`文件中设置环境变量 **REACT_APP_ALOVA_TIPS=0** - -```bash title=.env.development -REACT_APP_ALOVA_TIPS=0 -``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/11-others/_category_.json b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/11-others/_category_.json deleted file mode 100644 index 352959533..000000000 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/11-others/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "其他", - "link": { - "type": "generated-index" - } -} diff --git a/i18n/zh-CN/docusaurus-theme-classic/navbar.json b/i18n/zh-CN/docusaurus-theme-classic/navbar.json index a914aa8e0..af83c08db 100644 --- a/i18n/zh-CN/docusaurus-theme-classic/navbar.json +++ b/i18n/zh-CN/docusaurus-theme-classic/navbar.json @@ -1,42 +1,46 @@ -{ - "title": { - "message": "", - "description": "The title in the navbar" - }, - "item.label.Docs": { - "message": "文档", - "description": "Navbar item with label Document" - }, - "item.label.Example": { - "message": "示例", - "description": "Navbar item with label Example" - }, - "item.label.Contributing": { - "message": "社区贡献", - "description": "Navbar item with label Contributing" - }, - "item.label.Contributing Guidelines": { - "message": "贡献指南", - "description": "Navbar item with label Contributing Guidelines" - }, - "item.label.Become core member": { - "message": "成为核心成员", - "description": "Navbar item with label Become core member" - }, - "item.label.Developing Guidelines": { - "message": "开发指南", - "description": "Navbar item with label Developing Guidelines" - }, - "item.label.Code of conduct": { - "message": "行为准则", - "description": "Navbar item with label Code of Conduct" - }, - "item.label.Releases": { - "message": "发布日志", - "description": "Navbar item with label Releases" - }, - "item.label.GitHub": { - "message": "GitHub", - "description": "Navbar item with label GitHub" - } -} +{ + "title": { + "message": "", + "description": "The title in the navbar" + }, + "item.label.Docs": { + "message": "文档", + "description": "Navbar item with label Document" + }, + "item.label.Example": { + "message": "示例", + "description": "Navbar item with label Example" + }, + "item.label.Contributing": { + "message": "社区贡献", + "description": "Navbar item with label Contributing" + }, + "item.label.About": { + "message": "关于", + "description": "Navbar item with label About" + }, + "item.label.Contributing Guidelines": { + "message": "贡献指南", + "description": "Navbar item with label Contributing Guidelines" + }, + "item.label.Become core member": { + "message": "成为核心成员", + "description": "Navbar item with label Become core member" + }, + "item.label.Developing Guidelines": { + "message": "开发指南", + "description": "Navbar item with label Developing Guidelines" + }, + "item.label.Code of conduct": { + "message": "行为准则", + "description": "Navbar item with label Code of Conduct" + }, + "item.label.Releases": { + "message": "发布日志", + "description": "Navbar item with label Releases" + }, + "item.label.GitHub": { + "message": "GitHub", + "description": "Navbar item with label GitHub" + } +} diff --git a/sidebars.js b/sidebars.js index 5d478d15f..fed55d7ee 100644 --- a/sidebars.js +++ b/sidebars.js @@ -1,35 +1,23 @@ -/** - * Creating a sidebar enables you to: - - create an ordered group of docs - - render a sidebar for each doc of that group - - provide next/previous navigation - - The sidebars can be generated from the filesystem, or explicitly defined here. - - Create as many sidebars as you want. - */ - -// @ts-check - -/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ -const sidebars = { - // By default, Docusaurus generates a sidebar from the docs folder structure - tutorialSidebar: [{ type: 'autogenerated', dirName: 'tutorial' }], - apiSidebar: [{ type: 'autogenerated', dirName: 'api' }], - contributingSidebar: [{ type: 'autogenerated', dirName: 'contributing' }] - - // But you can create a sidebar manually - /* - tutorialSidebar: [ - 'intro', - 'hello', - { - type: 'category', - label: 'Tutorial', - items: ['tutorial-basics/create-a-document'], - }, - ], - */ -}; - -module.exports = sidebars; +/** + * Creating a sidebar enables you to: + - create an ordered group of docs + - render a sidebar for each doc of that group + - provide next/previous navigation + + The sidebars can be generated from the filesystem, or explicitly defined here. + + Create as many sidebars as you want. + */ + +// @ts-check + +/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ +const sidebars = { + // By default, Docusaurus generates a sidebar from the docs folder structure + tutorial: [{ type: 'autogenerated', dirName: 'tutorial' }], + api: [{ type: 'autogenerated', dirName: 'api' }], + contributing: [{ type: 'autogenerated', dirName: 'contributing' }], + resource: [{ type: 'autogenerated', dirName: 'resource' }] +}; + +export default sidebars; From 3816f6c751fe5f4ccc9e421149d4ad1eed038a6e Mon Sep 17 00:00:00 2001 From: MeetinaXD Date: Tue, 18 Jun 2024 22:19:17 +0800 Subject: [PATCH 04/11] docs: update psc docs, fix errors, and add more details --- docs/resource/02-storage-adapter/01-psc.md | 187 +++++++++++++++-- .../resource/02-storage-adapter/01-psc.md | 192 ++++++++++++++++-- 2 files changed, 347 insertions(+), 32 deletions(-) diff --git a/docs/resource/02-storage-adapter/01-psc.md b/docs/resource/02-storage-adapter/01-psc.md index 2e60d4f2a..cf051f595 100644 --- a/docs/resource/02-storage-adapter/01-psc.md +++ b/docs/resource/02-storage-adapter/01-psc.md @@ -3,20 +3,40 @@ title: Process Shared Adapter sidebar_position: 10 --- -Process shared storage adapter allows you to share cache in multi-process mode, which can be used in Nodejs and Electron. +Process shared storage adapter allows you to share cache in multi-process environment. + +We implemented two adapters that can be used in Node.js and Electron. + +:::info Tips + +Only supports Alova 3.0 and above. + +::: + +## Install + +```bash +npm install @alova/psc --save +``` ## Use in Node.js -In Node.js environment, you can use `NodeSyncAdapter` and `createNodeSharedCacheSynchronizer` to achieve cache synchronization between processes. +In Node.js environment, you can use `NodeSyncAdapter` and `createNodeSharedCacheSynchronizer` to synchronize the cache between child processes. 1. Set up the synchronizer in the master process: ```javascript -const { createPSCSynchronizer, NodeSyncAdapter, is } = require('@alova/psc'); +const process = require('node:process'); const cluster = require('cluster'); +const { createPSCSynchronizer, NodeSyncAdapter } = require('@alova/psc'); if (cluster.isMaster) { - createPSCSynchronizer(NodeSyncAdapter()); + // highlight-start + // Make sure to call this before creating child processes + const stopIPC = await createPSCSynchronizer(NodeSyncAdapter()); + // highlight-end + + process.on('SIGTERM', stopIPC); } else { // fork worker processes } @@ -25,44 +45,179 @@ if (cluster.isMaster) { 2. Use the adapter in the child process: ```javascript +const process = require('node:process'); const { createPSCAdapter, NodeSyncAdapter, ExplictCacheAdapter } = require('@alova/psc'); -const pscAdapter = createPSCAdapter(NodeSyncAdapter(), new ExplictCacheAdapter()); +const pscAdapter = createPSCAdapter( + NodeSyncAdapter(stopIPC => { + process.on('SIGTERM', stopIPC); + }) +); createAlova({ // ... l1Cache: pscAdapter }); ``` -> You can also use [lru-cache](https://www.npmjs.com/package/lru-cache) as a cache adapter. +### Sharing Multiple Caches + +Of course, you can share both l1Cache and l2Cache at the same time! Just use the scope option and create different shared storage adapters. + +```javascript +const createScopedPSCAdapter = (scope: string) => createPSCAdapter( + NodeSyncAdapter(stopIPC => { + process.on('SIGTERM', stopIPC); + }), + // This parameter is used to specify the storage adapter. We will introduce this later + undefined, + // highlight-start + { scope } + // highlight-end +); +``` ## Use in Electron -In Electron environment, you need to use `ElectronSyncAdapter` and `createPSCSynchronizer` to achieve cache synchronization between the main process and the rendering process. +In the Electron environment, use ElectronSyncAdapter and createPSCSynchronizer to synchronize the cache between renderer processes. 1. Set up the synchronizer in the main process: ```javascript -import { createPSCSynchronizer, ElectronSyncAdapter } from 'your-module-name'; +// main.js +import { createPSCSynchronizer, ElectronSyncAdapter } from '@alova/psc'; +import { ipcMain } from 'electron'; -function setupMainProcess() { - createPSCSynchronizer(ElectronSyncAdapter()); -} +// Initialize the synchronizer +createPSCSynchronizer(ElectronSyncAdapter(ipcMain)); -setupMainProcess(); +// ...other codes ``` -2. Use the adapter in the renderer process: +2. Use the adapter in the renderer process and expose it to the global object: ```javascript -import { createPSCAdapter, ElectronSyncAdapter } from 'your-module-name'; -import { ipcRenderer } from 'electron'; +// payload.js +import { createPSCAdapter, ElectronSyncAdapter } from '@alova/psc'; +import { ipcRenderer, contextBridge } from 'electron'; const pscAdapter = createPSCAdapter(ElectronSyncAdapter(ipcRenderer)); + +contextBridge.exposeInMainWorld('pscAdapter', pscAdapter); +``` + +3. Use it when creating the Alova instance: + +```javascript +import { createAlova } from 'alova'; + +const alova = createAlova({ + // ... + // highlight-start + l1Cache: window.pscAdapter + // highlight-end +}); +``` + +If you use TypeScript, don't forget to add the global type declaration: + +```typescript +// env.d.ts +declare global { + interface Window { + // ... + // highlight-start + pscAdapter: import('@alova/psc').SyncAdapter; + // highlight-end + } +} +``` + +:::warning note + +When using multiple Alova instances, there is no need to create multiple `PSCAdapter` objects for them. Alova instances are identified by different IDs, so the same channel can be safely reused. + +::: + + +## Custom Storage Adapter + +By passing the second parameter to createPSCAdapter, you can specify the storage adapter to use. + +```typescript +const pscAdapter = createPSCAdapter(ElectronSyncAdapter( + ipcRenderer, + // highlight-start + // Same as passing to l1Cache in createAlova. Use default implementation if passing undefined. + MyStorageAdapter() + // highlight-end +)); + createAlova({ // ... l1Cache: pscAdapter }); ``` +> You can also use [lru-cache](https://www.npmjs.com/package/lru-cache) as a cache adapter. + +## Custom SharedAdapter + +If you want to implement your own process shared adapter, you need to: + +1. Determine the communication method between processes. +2. Implement the `PSCSyncAdapter` interface: + +In detail, the first step is to determine the bidirectional communication method between the main process and the child process. For example, in Node.js, alova uses `node-ipc` to implement communication between the main process and the child process. In Electron, we use `ipcRenderer` and `ipcMain`. + +Next, implement the `SyncAdapter` interface for both the main process and the child process. Use `createSyncAdapter` for type assistance. + +Here is an implementation in Electron: + +```typescript +import { createPSCSynchronizer, createSyncAdapter } from '@/sharedCacheAdapter'; +import type { IpcMain, IpcRenderer } from 'electron'; + +const EventName = { + TO_MAIN: 'alova-ipc-to-main', + TO_CLIENT: 'alova-ipc-to-client' +} as const; + +/** + * Use this function in payload.js/ + */ +export function MyElectronSyncAdapter(ipcRenderer: IpcRenderer) { + // createSyncAdapter is a helper to implement SyncAdapter. do nothing + return createSyncAdapter({ + send(event) { + ipcRenderer.emit(EventName.TO_MAIN, event); + }, + receive(handler) { + ipcRenderer.on(EventName.TO_CLIENT, (_, payload) => handler(payload)); + } + }); +} + +let hasSynchronizer = false; + +/** + * Use this function in main process. + */ +export function createMyElectronSharedCacheSynchronizer(ipcMain: IpcMain) { + if (hasSynchronizer) { + return; + } + hasSynchronizer = true; + + createPSCSynchronizer( + createSyncAdapter({ + send(event) { + ipcMain.emit(EventName.TO_CLIENT, event); + }, + receive(handler) { + ipcMain.on(EventName.TO_MAIN, (_, payload) => handler(payload)); + } + }) + ); +} +``` -This way you can keep the cache in sync between Electron's main process and renderer process. +And refer the example above. diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/02-storage-adapter/01-psc.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/02-storage-adapter/01-psc.md index 426550fa4..65fc8505a 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/02-storage-adapter/01-psc.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/02-storage-adapter/01-psc.md @@ -3,20 +3,40 @@ title: 进程共享适配器 sidebar_position: 10 --- -进程共享存储适配器可以让你在多进程模式中共享缓存,可以在 Nodejs 和 Electron 中使用。 +进程共享存储适配器可以在多进程环境中共享 Alova 的缓存。 + +其中默认实现了两个适配器,可以在 Node.js 和 Electron 环境中使用。 + +:::info Tips + +仅支持 Alova 3.0 以上版本 + +::: + +## 安装 + +```bash +npm install @alova/ psc --save +``` ## 在 Node.js 中使用 -在 Node.js 环境中,您可以使用 `NodeSyncAdapter` 和 `createNodeSharedCacheSynchronizer` 来实现进程间的缓存同步。 +在 Node.js 环境中,使用 `NodeSyncAdapter` 和 `createNodeSharedCacheSynchronizer` 来实现各子进程之间的缓存同步。 1. 在主进程中设置同步器: ```javascript -const { createPSCSynchronizer, NodeSyncAdapter, is } = require('@alova/psc'); +const process = require('node:process'); const cluster = require('cluster'); +const { createPSCSynchronizer, NodeSyncAdapter } = require('@alova/psc'); if (cluster.isMaster) { - createPSCSynchronizer(NodeSyncAdapter()); + // highlight-start + // 确保在创建子进程之前调用 + const stopIPC = await createPSCSynchronizer(NodeSyncAdapter()); + // highlight-end + + process.on('SIGTERM', stopIPC); } else { // fork worker processes } @@ -25,44 +45,184 @@ if (cluster.isMaster) { 2. 在子进程中使用适配器: ```javascript +const process = require('node:process'); const { createPSCAdapter, NodeSyncAdapter, ExplictCacheAdapter } = require('@alova/psc'); -const pscAdapter = createPSCAdapter(NodeSyncAdapter(), new ExplictCacheAdapter()); +const pscAdapter = createPSCAdapter( + NodeSyncAdapter(stopIPC => { + process.on('SIGTERM', stopIPC); + }) +); createAlova({ // ... l1Cache: pscAdapter }); ``` -> 你还可以使用[lru-cache](https://www.npmjs.com/package/lru-cache)作为缓存适配器。 +### 共享多个缓存 + +当然,同时共享 `l1Cache` 和 `l2Cache` 也完全没有问题!使用 `scope` 选项,并创建不同的共享储存适配器即可。 + +```javascript +const createScopedPSCAdapter = (scope: string) => createPSCAdapter( + NodeSyncAdapter(stopIPC => { + process.on('SIGTERM', stopIPC); + }), + // 这个参数用于指定储存适配器,我们稍后会介绍 + undefined, + // highlight-start + { scope } + // highlight-end +); + +createAlova({ + // ... + l1Cache: createScopedPSCAdapter('l1'), + l2Cache: createScopedPSCAdapter('l2') +}); +``` ## 在 Electron 中使用 -Electron 环境下,您需要使用 `ElectronSyncAdapter` 和 `createPSCSynchronizer` 来实现主进程和渲染进程之间的缓存同步。 +在 Electron 环境中,使用 `ElectronSyncAdapter` 和 `createPSCSynchronizer` 来实现各渲染进程之间的缓存同步。 1. 在主进程中设置同步器: ```javascript -import { createPSCSynchronizer, ElectronSyncAdapter } from 'your-module-name'; +// main.js +import { createPSCSynchronizer, ElectronSyncAdapter } from '@alova/psc'; +import { ipcMain } from 'electron'; -function setupMainProcess() { - createPSCSynchronizer(ElectronSyncAdapter()); -} +// 初始化同步器 +createPSCSynchronizer(ElectronSyncAdapter(ipcMain)); -setupMainProcess(); +// ...other codes ``` -2. 在渲染进程中使用适配器: +2. 在渲染进程中使用适配器并暴露到全局对象: ```javascript -import { createPSCAdapter, ElectronSyncAdapter } from 'your-module-name'; -import { ipcRenderer } from 'electron'; +// payload.js +import { createPSCAdapter, ElectronSyncAdapter } from '@alova/psc'; +import { ipcRenderer, contextBridge } from 'electron'; const pscAdapter = createPSCAdapter(ElectronSyncAdapter(ipcRenderer)); + +contextBridge.exposeInMainWorld('pscAdapter', pscAdapter); +``` + +3. 在创建 alova 实例时使用: + +```javascript +import { createAlova } from 'alova'; + +const alova = createAlova({ + // ... + // highlight-start + l1Cache: window.pscAdapter + // highlight-end +}); +``` + +如果使用 typescript,不要忘记补充全局类型: + +```typescript +// env.d.ts +declare global { + interface Window { + // ... + // highlight-start + pscAdapter: import('@alova/psc').SyncAdapter; + // highlight-end + } +} +``` + +:::warning note + +当使用多个 alova 实例时,无需为其创建多个 `PSCAdapter` 对象。Alova 实例之间通过不同的 id 来标识自身,因此可安全地复用同一通道。 + +::: + +## 自定义储存适配器 + +通过传入 `createPSCAdapter` 的第二个参数,可指定使用的储存适配器。 + +```typescript +const pscAdapter = createPSCAdapter(ElectronSyncAdapter( + ipcRenderer, + // highlight-start + // 用法与 createAlova 时传入到 l1Cache 一致。如果传入 undefined,那么将使用默认实现 + MyStorageAdapter() + // highlight-end +)); + createAlova({ // ... l1Cache: pscAdapter }); ``` +> 你还可以使用 [lru-cache](https://www.npmjs.com/package/lru-cache) 作为缓存适配器。 + +## 自定义共享适配器 + +如果你希望自己实现进程共享适配器,你需要: + +1. 确定进程间的通信方式。 +2. 实现 `SyncAdapter` 接口 + +具体而言,首先需要确定主进程与子进程之间的双向通信方式。例如,在 Node.js 中,alova 使用了 [node-ipc](https://www.npmjs.com/package/node-ipc) 实现主进程和子进程的通信;在 Electron 中,则使用 `ipcRenderer` 及 `ipcMain` 对象来实现。 + +随后,分别实现主进程和子进程的 `SyncAdapter` 接口,可通过 `createSyncAdapter` 提供类型帮助。 + +以下是一个在 Electron 中的实现: + +```typescript +import { createPSCSynchronizer, createSyncAdapter } from '@/sharedCacheAdapter'; +import type { IpcMain, IpcRenderer } from 'electron'; + +const EventName = { + TO_MAIN: 'alova-ipc-to-main', + TO_CLIENT: 'alova-ipc-to-client' +} as const; + +/** + * Use this function in payload.js + */ +export function MyElectronSyncAdapter(ipcRenderer: IpcRenderer) { + // createSyncAdapter is a helper to implement SyncAdapter. do nothing + return createSyncAdapter({ + send(event) { + ipcRenderer.emit(EventName.TO_MAIN, event); + }, + receive(handler) { + ipcRenderer.on(EventName.TO_CLIENT, (_, payload) => handler(payload)); + } + }); +} + +let hasSynchronizer = false; + +/** + * Use this function in main process. + */ +export function createMyElectronSharedCacheSynchronizer(ipcMain: IpcMain) { + if (hasSynchronizer) { + return; + } + hasSynchronizer = true; + + createPSCSynchronizer( + createSyncAdapter({ + send(event) { + ipcMain.emit(EventName.TO_CLIENT, event); + }, + receive(handler) { + ipcMain.on(EventName.TO_MAIN, (_, payload) => handler(payload)); + } + }) + ); +} +``` -通过这种方式,您可以在 Electron 的主进程和渲染进程之间保持缓存的同步。 +并参考上述示例使用即可。 From 42b71834f0052a3c4ec25117928e21ac21040568 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=83=A1=E9=95=87?= Date: Wed, 19 Jun 2024 18:30:01 +0800 Subject: [PATCH 05/11] docs: finish the v3.0-beta tutorial --- docs/about/01-RSM.md | 121 +- docs/about/02-comparison.md | 185 +- docs/about/03-Q&A.md | 24 - docs/about/03-qa.md | 133 + docs/api/01-alova.md | 78 +- docs/api/02-method.md | 21 +- docs/api/03-core-hooks.md | 535 +- docs/api/04-cache.md | 13 +- docs/api/05-states.md | 9 +- docs/api/06-global-config.md | 71 +- docs/contributing/01-overview.md | 349 +- docs/contributing/02-become-core-member.md | 63 +- docs/contributing/03-developing-guidelines.md | 275 +- docs/contributing/04-code-of-conduct.md | 273 +- .../01-request-adapter/01-alova-mock.md | 19 +- .../02-alova-adapter-xhr.md | 673 +- .../03-alova-adapter-axios.md | 585 +- .../04-alova-adapter-taro.md | 879 +- .../05-alova-adapter-uniapp.md | 557 +- docs/resource/02-storage-adapter/01-psc.md | 40 +- docs/resource/03-framework/01-vue-options.md | 9 +- docs/resource/03-framework/02-solid.md | 1 - docs/resource/03-framework/03-angular.md | 1 - docs/resource/03-framework/04-native-mp.md | 1 - docs/resource/03-framework/05-preact.md | 1 - docs/resource/03-framework/06-qwik.md | 1 - docs/resource/03-framework/07-lit.md | 1 - docs/resource/03-framework/08-stencil.md | 1 - docs/tutorial/01-example/01-init-page.md | 16 - docs/tutorial/01-example/02-submit-form.md | 16 - .../01-example/03-condition-search.md | 16 - docs/tutorial/01-example/04-memory-cache.md | 21 - .../01-example/05-storage-placeholder.md | 21 - .../tutorial/01-example/06-storage-restore.md | 21 - docs/tutorial/01-example/07-update-state.md | 20 - docs/tutorial/01-example/08-prefetch.md | 21 - docs/tutorial/01-example/09-load-more.md | 23 - docs/tutorial/01-example/10-paginated-list.md | 23 - .../11-controlled-cache-by-indexeddb.md | 23 - .../01-example/12-silent-submit-setting.md | 23 - .../13-silent-submit-simple-list.md | 23 - .../01-example/14-silent-submit-notes.md | 23 - docs/tutorial/01-example/15-form-hook.md | 18 - docs/tutorial/01-example/16-captcha-send.md | 18 - docs/tutorial/01-example/17-retriable-hook.md | 18 - .../18.action-delegation-middleware.md | 18 - docs/tutorial/01-example/19-serial-request.md | 19 - docs/tutorial/01-example/_category_.json | 7 - .../{README.md => 01-introduce.md} | 12 +- .../02-getting-started/02-quick-start.md | 7 +- .../{ => 03-basic}/03-method.md | 635 +- .../{ => 03-basic}/04-alova.md | 157 +- .../{ => 03-basic}/05-global-interceptor.md | 3 +- .../{ => 03-basic}/06-method-metadata.md | 301 +- .../{ => 03-basic}/07-combine-framework.md | 7 +- .../{ => 03-basic}/08-server.md | 7 +- .../03-basic/_category_.json | 3 + .../09-extension-integration.md | 1 - .../02-getting-started/10-congratulations.md | 7 +- .../02-getting-started/_category_.json | 8 +- .../{ => 01-strategy}/01-use-request.md | 11 +- .../{ => 01-strategy}/02-use-watcher.md | 5 +- .../{ => 01-strategy}/03-use-fetcher.md | 7 +- .../{ => 01-strategy}/04-use-pagination.md | 21 +- .../{ => 01-strategy}/05-use-form.md | 17 +- .../06-token-authentication.md | 15 +- .../{ => 01-strategy}/07-use-auto-request.md | 7 +- .../08-action-delegation-middleware.md | 69 +- .../02-virtual-data.md | 1 - .../03-start-silent-factory.md | 1 - .../04-conservative-request.md | 3 +- .../05-modify-response.md | 3 +- .../06-request-retry.md | 287 +- .../07-data-compensation.md | 1 - .../08-edit-item.md | 3 +- .../09-what-more.md | 7 +- .../09-sensorless-data-interaction/README.md | 292 +- .../_category_.json | 6 +- .../{ => 01-strategy}/10-use-captcha.md | 13 +- .../11-use-serial-request.md | 15 +- .../12-use-serial-watcher.md | 15 +- .../13-use-retriable-request.md | 13 +- .../03-client/{ => 01-strategy}/14-use-sse.md | 3 +- .../15-use-breakpoint-uploader.md | 1 - .../{ => 01-strategy}/16-use-uploader.md | 1 - .../17-rate-limit-middleware.md | 1 - .../03-client/{ => 01-strategy}/README.md | 2 +- .../03-client/01-strategy/_category_.json | 3 + .../01-update-across-components.md | 9 +- .../02-in-depth}/02-method-matcher.md | 11 +- .../02-in-depth/03-middleware.md} | 3 +- .../02-in-depth/04-manage-extra-states.md} | 5 +- .../03-client/02-in-depth/_category_.json | 3 + docs/tutorial/03-client/_category_.json | 4 +- .../04-server/{ => 01-strategy}/01-retry.md | 5 +- .../{ => 01-strategy}/02-send-captcha.md | 1 - .../03-rate-limit-middleware.md | 1 - .../04-server/{ => 01-strategy}/README.md | 0 .../04-server/01-strategy/_category_.json | 3 + docs/tutorial/04-server/_category_.json | 4 +- docs/tutorial/05-cache/01-mode.md | 47 +- docs/tutorial/05-cache/02-auto-invalidate.md | 1 - .../05-cache/03-manually-invalidate.md | 5 +- docs/tutorial/05-cache/04-force-request.md | 3 +- docs/tutorial/05-cache/05-set-and-query.md | 5 +- docs/tutorial/05-cache/06-controlled-cache.md | 1 - docs/tutorial/05-cache/_category_.json | 4 +- .../{ => 01-in-depth}/05-custom-method-key.md | 1 - .../{ => 01-in-depth}/07-cache-logger.md | 1 - .../06-advanced/{ => 01-in-depth}/09-ssr.md | 5 +- .../01-in-depth/10-typescript.md} | 332 +- .../06-advanced/{ => 01-in-depth}/README.md | 0 .../06-advanced/01-in-depth/_category_.json | 3 + .../02-custom}/01-http-adapter.md | 1 - .../02-custom}/02-storage-adapter.md | 1 - .../02-custom}/03-client-strategy.md | 5 +- .../02-custom}/04-server-strategy.md | 1 - .../02-custom}/05-stateshook.md | 1 - .../02-custom}/README.md | 4 +- .../02-custom}/_category_.json | 0 docs/tutorial/06-advanced/_category_.json | 8 +- .../01-best-practice}/01-manage-apis.md | 307 +- .../01-best-practice}/02-skills.md | 28 +- .../03-manage-cache-by-indexeddb.md | 245 +- .../01-best-practice}/04-multiple-servers.md | 39 +- .../01-best-practice}/05-middleware.md | 57 +- .../01-best-practice}/06-parallel-request.md | 1 - .../01-best-practice}/07-serial-request.md | 1 - .../01-best-practice}/08-l2-storage.md | 0 .../07-project/01-best-practice/README.md | 9 + .../01-best-practice/_category_.json | 3 + .../02-migration}/01-v2-to-v3.md | 1 - .../02-migration}/02-from-axios.md | 1 - .../02-migration}/_category_.json | 0 docs/tutorial/07-project/_category_.json | 5 + docusaurus.config.ts | 70 +- i18n/zh-CN/code.json | 1346 +- .../current.json | 34 +- .../current/about/01-RSM.md | 121 +- .../current/about/02-comparison.md | 185 +- .../current/about/03-qa.md | 132 + .../current/api/01-alova.md | 78 +- .../current/api/02-method.md | 21 +- .../current/api/03-core-hooks.md | 535 +- .../current/api/04-cache.md | 13 +- .../current/api/05-states.md | 9 +- .../current/api/06-global-config.md | 71 +- .../current/contributing/01-overview.md | 351 +- .../contributing/02-become-core-member.md | 71 +- .../contributing/03-developing-guidelines.md | 275 +- .../contributing/04-code-of-conduct.md | 179 +- .../01-request-adapter/01-alova-mock.md | 15 +- .../02-alova-adapter-xhr.md | 673 +- .../03-alova-adapter-axios.md | 581 +- .../04-alova-adapter-taro.md | 879 +- .../05-alova-adapter-uniapp.md | 557 +- .../resource/02-storage-adapter/01-psc.md | 39 +- .../resource/03-framework/01-vue-options.md | 9 +- .../current/resource/03-framework/02-solid.md | 1 - .../resource/03-framework/03-angular.md | 1 - .../resource/03-framework/04-native-mp.md | 1 - .../resource/03-framework/05-preact.md | 1 - .../current/resource/03-framework/06-qwik.md | 1 - .../current/resource/03-framework/07-lit.md | 1 - .../resource/03-framework/08-stencil.md | 1 - .../01-example/{ => 01-vue}/01-init-page.md | 31 +- .../01-example/{ => 01-vue}/02-submit-form.md | 31 +- .../{ => 01-vue}/03-condition-search.md | 27 +- .../{ => 01-vue}/04-memory-cache.md | 41 +- .../{ => 01-vue}/05-storage-placeholder.md | 41 +- .../{ => 01-vue}/06-storage-restore.md | 41 +- .../{ => 01-vue}/07-update-state.md | 39 +- .../01-example/{ => 01-vue}/08-prefetch.md | 41 +- .../01-example/{ => 01-vue}/09-load-more.md | 45 +- .../{ => 01-vue}/10-paginated-list.md | 45 +- .../11-controlled-cache-by-indexeddb.md | 45 +- .../13-silent-submit-simple-list.md | 45 +- .../{ => 01-vue}/19-serial-request.md | 37 +- .../{ => 02-react}/14-silent-submit-notes.md | 45 +- .../01-example/{ => 02-react}/15-form-hook.md | 35 +- .../{ => 02-react}/16-captcha-send.md | 35 +- .../{ => 02-react}/17-retriable-hook.md | 35 +- .../18.action-delegation-middleware.md | 35 +- .../12-silent-submit-setting.md | 45 +- .../tutorial/01-example/_category_.json | 7 - .../{README.md => 01-introduce.md} | 12 +- .../02-getting-started/02-quick-start.md | 7 +- .../{ => 03-basic}/03-method.md | 629 +- .../{ => 03-basic}/04-alova.md | 157 +- .../{ => 03-basic}/05-global-interceptor.md | 3 +- .../{ => 03-basic}/06-method-metadata.md | 301 +- .../{ => 03-basic}/07-combine-framework.md | 7 +- .../{ => 03-basic}/08-server.md | 7 +- .../09-extension-integration.md | 1 - .../02-getting-started/10-congratulations.md | 7 +- .../{ => 01-strategy}/01-use-request.md | 11 +- .../{ => 01-strategy}/02-use-watcher.md | 5 +- .../{ => 01-strategy}/03-use-fetcher.md | 7 +- .../{ => 01-strategy}/04-use-pagination.md | 21 +- .../{ => 01-strategy}/05-use-form.md | 17 +- .../06-token-authentication.md | 13 +- .../{ => 01-strategy}/07-use-auto-request.md | 7 +- .../08-action-delegation-middleware.md | 69 +- .../02-virtual-data.md | 1 - .../03-start-silent-factory.md | 1 - .../04-conservative-request.md | 3 +- .../05-modify-response.md | 3 +- .../06-request-retry.md | 287 +- .../07-data-compensation.md | 1 - .../08-edit-item.md | 3 +- .../09-what-more.md | 7 +- .../09-sensorless-data-interaction/README.md | 292 +- .../_category_.json | 0 .../{ => 01-strategy}/10-use-captcha.md | 13 +- .../11-use-serial-request.md | 15 +- .../12-use-serial-watcher.md | 15 +- .../13-use-retriable-request.md | 13 +- .../03-client/{ => 01-strategy}/14-use-sse.md | 3 +- .../15-use-breakpoint-uploader.md | 1 - .../{ => 01-strategy}/16-use-uploader.md | 3 +- .../17-rate-limit-middleware.md | 1 - .../03-client/{ => 01-strategy}/README.md | 2 +- .../01-update-across-components.md | 9 +- .../02-in-depth}/02-method-matcher.md | 11 +- .../02-in-depth/03-middleware.md} | 3 +- .../02-in-depth/04-manage-extra-states.md} | 3 +- .../tutorial/03-client/_category_.json | 3 - .../04-server/{ => 01-strategy}/01-retry.md | 5 +- .../{ => 01-strategy}/02-send-captcha.md | 1 - .../03-rate-limit-middleware.md | 1 - .../04-server/{ => 01-strategy}/README.md | 0 .../tutorial/04-server/_category_.json | 3 - .../current/tutorial/05-cache/01-mode.md | 9 +- .../tutorial/05-cache/02-auto-invalidate.md | 1 - .../05-cache/03-manually-invalidate.md | 5 +- .../tutorial/05-cache/04-force-request.md | 3 +- .../tutorial/05-cache/05-set-and-query.md | 5 +- .../tutorial/05-cache/06-controlled-cache.md | 1 - .../{ => 01-in-depth}/05-custom-method-key.md | 1 - .../{ => 01-in-depth}/07-cache-logger.md | 1 - .../06-advanced/{ => 01-in-depth}/09-ssr.md | 5 +- .../01-in-depth/10-typescript.md} | 332 +- .../06-advanced/{ => 01-in-depth}/README.md | 0 .../02-custom}/01-http-adapter.md | 1 - .../02-custom}/02-storage-adapter.md | 1 - .../02-custom}/03-client-strategy.md | 5 +- .../02-custom}/04-server-strategy.md | 1 - .../02-custom}/05-stateshook.md | 1 - .../02-custom}/README.md | 4 +- .../02-custom}/_category_.json | 0 .../tutorial/06-advanced/_category_.json | 3 - .../01-best-practice}/01-manage-apis.md | 307 +- .../01-best-practice}/02-skills.md | 30 +- .../03-manage-cache-by-indexeddb.md | 245 +- .../01-best-practice}/04-multiple-servers.md | 39 +- .../01-best-practice}/05-middleware.md | 1 - .../01-best-practice}/06-parallel-request.md | 1 - .../01-best-practice}/07-serial-request.md | 1 - .../01-best-practice}/08-l2-storage.md | 1 - .../07-project/01-best-practice/README.md | 9 + .../02-migration}/01-v2-to-v3.md | 1 - .../02-migration}/02-from-axios.md | 1 - .../tutorial/07-project/_category_.json | 3 + .../version-2.x.json | 70 + .../version-2.x/about/01-RSM.md | 60 + .../version-2.x/about/02-comparison.md | 92 + .../{current => version-2.x}/about/03-Q&A.md | 46 +- .../version-2.x/api/01-alova.md | 25 +- .../version-2.x/api/02-method.md | 11 +- .../version-2.x/api/03-core-hooks.md | 535 +- .../version-2.x/api/04-cache.md | 1 - .../version-2.x/api/05-states.md | 1 - .../version-2.x/api/06-global-config.md | 71 +- .../version-2.x/contributing/01-overview.md | 351 +- .../contributing/02-become-core-member.md | 71 +- .../contributing/03-developing-guidelines.md | 275 +- .../contributing/04-code-of-conduct.md | 179 +- .../tutorial/01-example/01-init-page.md | 31 +- .../tutorial/01-example/02-submit-form.md | 31 +- .../01-example/03-condition-search.md | 27 +- .../tutorial/01-example/04-memory-cache.md | 41 +- .../01-example/05-storage-placeholder.md | 41 +- .../tutorial/01-example/06-storage-restore.md | 41 +- .../tutorial/01-example/07-update-state.md | 39 +- .../tutorial/01-example/08-prefetch.md | 41 +- .../tutorial/01-example/09-load-more.md | 45 +- .../tutorial/01-example/10-paginated-list.md | 45 +- .../11-controlled-cache-by-indexeddb.md | 45 +- .../01-example/12-silent-submit-setting.md | 45 +- .../13-silent-submit-simple-list.md | 45 +- .../01-example/14-silent-submit-notes.md | 45 +- .../tutorial/01-example/15-form-hook.md | 35 +- .../tutorial/01-example/16-captcha-send.md | 35 +- .../tutorial/01-example/17-retriable-hook.md | 35 +- .../18.action-delegation-middleware.md | 35 +- .../tutorial/01-example/19-serial-request.md | 37 +- .../02-getting-started/02-quick-start.md | 229 +- .../tutorial/02-getting-started/03-method.md | 611 +- .../tutorial/02-getting-started/04-alova.md | 133 +- .../05-global-interceptor.md | 273 +- .../02-getting-started/06-method-metadata.md | 313 +- .../03-combine-framework/02-use-request.md | 1 - .../03-combine-framework/03-use-watcher.md | 655 +- .../03-combine-framework/04-initial-data.md | 1 - .../03-combine-framework/05-response.md | 233 +- .../03-combine-framework/06-abort-request.md | 1 - .../07-download-upload-progress.md | 413 +- .../03-combine-framework/08-receive-params.md | 5 +- .../03-combine-framework/10-typescript.md | 317 +- .../version-2.x/tutorial/04-cache/01-mode.md | 435 +- .../tutorial/04-cache/02-auto-invalidate.md | 181 +- .../04-cache/03-manually-invalidate.md | 203 +- .../tutorial/04-cache/04-force-request.md | 1 - .../tutorial/04-cache/05-set-and-query.md | 579 +- .../tutorial/04-cache/06-controlled-cache.md | 165 +- .../02-virtual-data.md | 353 +- .../03-start-silent-factory.md | 213 +- .../04-conservative-request.md | 271 +- .../05-modify-response.md | 517 +- .../06-request-retry.md | 287 +- .../07-data-compensation.md | 111 +- .../08-edit-item.md | 221 +- .../09-what-more.md | 685 +- .../tutorial/05-strategy/02-usePagination.md | 1 - .../tutorial/05-strategy/03-useForm.md | 1 - .../05-strategy/04-tokenAuthentication.md | 1 - .../tutorial/05-strategy/05-useUploader.md | 353 +- .../tutorial/05-strategy/06-useAutoRequest.md | 1 - .../05-strategy/07-useBreakpointUploader.md | 11 +- .../tutorial/05-strategy/08-useCaptcha.md | 1 - .../09-actionDelegationMiddleware.md | 1 - .../05-strategy/10-useSerialRequest.md | 1 - .../05-strategy/11-useSerialWatcher.md | 1 - .../05-strategy/12-useRetriableRequest.md | 1 - .../tutorial/05-strategy/13-useSSE.md | 1 - .../05-strategy/14-rateLimitMiddleware.md | 15 +- .../tutorial/06-advanced/01-use-fetcher.md | 613 +- .../02-update-across-components.md | 203 +- .../tutorial/06-advanced/03-method-matcher.md | 281 +- .../tutorial/06-advanced/04-middleware.md | 809 +- .../06-advanced/05-custom-method-key.md | 51 +- .../tutorial/06-advanced/06-error-logger.md | 101 +- .../tutorial/06-advanced/07-cache-logger.md | 119 +- .../06-advanced/08-manage-extra-states.md | 397 +- .../tutorial/06-advanced/09-ssr.md | 455 +- .../07-best-practice/01-manage-apis.md | 307 +- .../tutorial/07-best-practice/02-skills.md | 22 +- .../03-manage-cache-by-indexeddb.md | 245 +- .../07-best-practice/04-multiple-servers.md | 39 +- .../07-best-practice/05-middleware.md | 57 +- .../07-best-practice/12-parallel-request.md | 127 +- .../07-best-practice/13-serial-request.md | 102 +- .../08-request-adapter/01-alova-mock.md | 1 - .../02-alova-adapter-xhr.md | 673 +- .../03-alova-adapter-axios.md | 581 +- .../04-alova-adapter-taro.md | 879 +- .../05-alova-adapter-uniapp.md | 557 +- .../tutorial/09-framework/01-vue-options.md | 1 - .../tutorial/09-framework/02-solid.md | 1 - .../tutorial/09-framework/03-angular.md | 1 - .../tutorial/09-framework/04-native-mp.md | 1 - .../tutorial/09-framework/05-preact.md | 1 - .../tutorial/09-framework/06-qwik.md | 1 - .../tutorial/09-framework/07-lit.md | 1 - .../tutorial/09-framework/08-stencil.md | 1 - .../tutorial/10-custom/01-overview.md | 51 +- .../10-custom/02-custom-http-adapter.md | 385 +- .../10-custom/03-custom-storage-adapter.md | 95 +- .../10-custom/04-custom-stateshook.md | 201 +- .../version-2.x/tutorial/11-others/01-RSM.md | 121 +- .../tutorial/11-others/02-comparison.md | 185 +- .../version-2.x/tutorial/11-others/03-Q&A.md | 47 +- .../tutorial/11-others/04-future.md | 71 +- .../tutorial/11-others/05-react-native.md | 29 +- .../tutorial/11-others/06-use-in-static.md | 9 +- .../11-others/07-hide-recommend-tips.md | 91 +- .../docusaurus-theme-classic/footer.json | 88 +- .../docusaurus-theme-classic/navbar.json | 20 + package-lock.json | 55292 ++++++++-------- package.json | 4 +- src/css/custom.css | 255 - src/css/custom.scss | 275 + src/pages/_indexComponent/Strategy/data.tsx | 703 +- src/pages/index.tsx | 243 +- src/theme/AnnouncementBar/Content/index.js | 38 +- .../AnnouncementBar/Content/styles.module.css | 10 - .../Content/styles.module.scss | 31 + static/img/announcement_bg.png | Bin 0 -> 194871 bytes versioned_docs/version-2.x/about/01-RSM.md | 60 + .../version-2.x/about/02-comparison.md | 92 + versioned_docs/version-2.x/about/03-Q&A.md | 133 + .../version-2.x/about}/_category_.json | 2 +- versioned_docs/version-2.x/api/01-alova.md | 25 +- versioned_docs/version-2.x/api/02-method.md | 11 +- .../version-2.x/api/03-core-hooks.md | 535 +- versioned_docs/version-2.x/api/04-cache.md | 1 - versioned_docs/version-2.x/api/05-states.md | 1 - .../version-2.x/api/06-global-config.md | 71 +- .../version-2.x/contributing/01-overview.md | 349 +- .../contributing/02-become-core-member.md | 63 +- .../contributing/03-developing-guidelines.md | 275 +- .../contributing/04-code-of-conduct.md | 273 +- .../tutorial/01-example/01-init-page.md | 31 +- .../tutorial/01-example/02-submit-form.md | 31 +- .../01-example/03-condition-search.md | 31 +- .../tutorial/01-example/04-memory-cache.md | 41 +- .../01-example/05-storage-placeholder.md | 41 +- .../tutorial/01-example/06-storage-restore.md | 41 +- .../tutorial/01-example/07-update-state.md | 39 +- .../tutorial/01-example/08-prefetch.md | 41 +- .../tutorial/01-example/09-load-more.md | 45 +- .../tutorial/01-example/10-paginated-list.md | 45 +- .../11-controlled-cache-by-indexeddb.md | 45 +- .../01-example/12-silent-submit-setting.md | 45 +- .../13-silent-submit-simple-list.md | 45 +- .../01-example/14-silent-submit-notes.md | 45 +- .../tutorial/01-example/15-form-hook.md | 35 +- .../tutorial/01-example/16-captcha-send.md | 35 +- .../tutorial/01-example/17-retriable-hook.md | 35 +- .../18.action-delegation-middleware.md | 35 +- .../tutorial/01-example/19-serial-request.md | 37 +- .../02-getting-started/02-quick-start.md | 229 +- .../tutorial/02-getting-started/03-method.md | 611 +- .../tutorial/02-getting-started/04-alova.md | 133 +- .../05-global-interceptor.md | 273 +- .../02-getting-started/06-method-metadata.md | 313 +- .../03-combine-framework/02-use-request.md | 1 - .../03-combine-framework/03-use-watcher.md | 637 +- .../03-combine-framework/04-initial-data.md | 1 - .../03-combine-framework/05-response.md | 1 - .../03-combine-framework/06-abort-request.md | 1 - .../07-download-upload-progress.md | 413 +- .../03-combine-framework/08-receive-params.md | 5 +- .../03-combine-framework/10-typescript.md | 317 +- .../version-2.x/tutorial/04-cache/01-mode.md | 429 +- .../tutorial/04-cache/02-auto-invalidate.md | 181 +- .../04-cache/03-manually-invalidate.md | 203 +- .../tutorial/04-cache/04-force-request.md | 149 +- .../tutorial/04-cache/05-set-and-query.md | 579 +- .../tutorial/04-cache/06-controlled-cache.md | 165 +- .../02-virtual-data.md | 353 +- .../03-start-silent-factory.md | 213 +- .../04-conservative-request.md | 271 +- .../05-modify-response.md | 517 +- .../06-request-retry.md | 287 +- .../07-data-compensation.md | 111 +- .../08-edit-item.md | 221 +- .../09-what-more.md | 685 +- .../tutorial/05-strategy/02-usePagination.md | 1 - .../tutorial/05-strategy/03-useForm.md | 1 - .../05-strategy/04-tokenAuthentication.md | 1 - .../tutorial/05-strategy/05-useUploader.md | 11 +- .../tutorial/05-strategy/06-useAutoRequest.md | 1 - .../05-strategy/07-useBreakpointUploader.md | 11 +- .../tutorial/05-strategy/08-useCaptcha.md | 1 - .../09-actionDelegationMiddleware.md | 1 - .../05-strategy/10-useSerialRequest.md | 1 - .../05-strategy/11-useSerialWatcher.md | 1 - .../05-strategy/12-useRetriableRequest.md | 1 - .../tutorial/05-strategy/13-useSSE.md | 1 - .../05-strategy/14-rateLimitMiddleware.md | 15 +- .../tutorial/06-advanced/01-use-fetcher.md | 613 +- .../02-update-across-components.md | 203 +- .../tutorial/06-advanced/03-method-matcher.md | 281 +- .../tutorial/06-advanced/04-middleware.md | 809 +- .../06-advanced/05-custom-method-key.md | 51 +- .../tutorial/06-advanced/06-error-logger.md | 73 +- .../tutorial/06-advanced/07-cache-logger.md | 119 +- .../06-advanced/08-manage-extra-states.md | 395 +- .../tutorial/06-advanced/09-ssr.md | 455 +- .../07-best-practice/01-manage-apis.md | 307 +- .../tutorial/07-best-practice/02-skills.md | 22 +- .../03-manage-cache-by-indexeddb.md | 245 +- .../07-best-practice/04-multiple-servers.md | 39 +- .../07-best-practice/05-middleware.md | 57 +- .../08-request-adapter/01-alova-mock.md | 3 +- .../02-alova-adapter-xhr.md | 673 +- .../03-alova-adapter-axios.md | 585 +- .../04-alova-adapter-taro.md | 879 +- .../05-alova-adapter-uniapp.md | 557 +- .../tutorial/09-framework/01-vue-options.md | 1 - .../tutorial/09-framework/02-solid.md | 1 - .../tutorial/09-framework/03-angular.md | 1 - .../tutorial/09-framework/04-native-mp.md | 1 - .../tutorial/09-framework/05-preact.md | 1 - .../tutorial/09-framework/06-qwik.md | 1 - .../tutorial/09-framework/07-lit.md | 1 - .../tutorial/09-framework/08-stencil.md | 1 - .../tutorial/10-custom/01-overview.md | 51 +- .../10-custom/02-custom-http-adapter.md | 385 +- .../10-custom/03-custom-storage-adapter.md | 95 +- .../10-custom/04-custom-stateshook.md | 201 +- .../version-2.x/tutorial/11-others/01-RSM.md | 121 +- .../tutorial/11-others/02-comparison.md | 185 +- .../version-2.x/tutorial/11-others/03-Q&A.md | 47 +- .../tutorial/11-others/04-future.md | 71 +- .../tutorial/11-others/05-react-native.md | 29 +- .../tutorial/11-others/06-use-in-static.md | 9 +- .../11-others/07-hide-recommend-tips.md | 91 +- versioned_sidebars/version-2.x-sidebars.json | 6 +- 500 files changed, 55928 insertions(+), 55363 deletions(-) delete mode 100644 docs/about/03-Q&A.md create mode 100644 docs/about/03-qa.md delete mode 100644 docs/tutorial/01-example/01-init-page.md delete mode 100644 docs/tutorial/01-example/02-submit-form.md delete mode 100644 docs/tutorial/01-example/03-condition-search.md delete mode 100644 docs/tutorial/01-example/04-memory-cache.md delete mode 100644 docs/tutorial/01-example/05-storage-placeholder.md delete mode 100644 docs/tutorial/01-example/06-storage-restore.md delete mode 100644 docs/tutorial/01-example/07-update-state.md delete mode 100644 docs/tutorial/01-example/08-prefetch.md delete mode 100644 docs/tutorial/01-example/09-load-more.md delete mode 100644 docs/tutorial/01-example/10-paginated-list.md delete mode 100644 docs/tutorial/01-example/11-controlled-cache-by-indexeddb.md delete mode 100644 docs/tutorial/01-example/12-silent-submit-setting.md delete mode 100644 docs/tutorial/01-example/13-silent-submit-simple-list.md delete mode 100644 docs/tutorial/01-example/14-silent-submit-notes.md delete mode 100644 docs/tutorial/01-example/15-form-hook.md delete mode 100644 docs/tutorial/01-example/16-captcha-send.md delete mode 100644 docs/tutorial/01-example/17-retriable-hook.md delete mode 100644 docs/tutorial/01-example/18.action-delegation-middleware.md delete mode 100644 docs/tutorial/01-example/19-serial-request.md delete mode 100644 docs/tutorial/01-example/_category_.json rename docs/tutorial/02-getting-started/{README.md => 01-introduce.md} (93%) rename docs/tutorial/02-getting-started/{ => 03-basic}/03-method.md (92%) rename docs/tutorial/02-getting-started/{ => 03-basic}/04-alova.md (78%) rename docs/tutorial/02-getting-started/{ => 03-basic}/05-global-interceptor.md (98%) rename docs/tutorial/02-getting-started/{ => 03-basic}/06-method-metadata.md (96%) rename docs/tutorial/02-getting-started/{ => 03-basic}/07-combine-framework.md (95%) rename docs/tutorial/02-getting-started/{ => 03-basic}/08-server.md (90%) create mode 100644 docs/tutorial/02-getting-started/03-basic/_category_.json rename docs/tutorial/03-client/{ => 01-strategy}/01-use-request.md (93%) rename docs/tutorial/03-client/{ => 01-strategy}/02-use-watcher.md (96%) rename docs/tutorial/03-client/{ => 01-strategy}/03-use-fetcher.md (96%) rename docs/tutorial/03-client/{ => 01-strategy}/04-use-pagination.md (96%) rename docs/tutorial/03-client/{ => 01-strategy}/05-use-form.md (96%) rename docs/tutorial/03-client/{ => 01-strategy}/06-token-authentication.md (97%) rename docs/tutorial/03-client/{ => 01-strategy}/07-use-auto-request.md (92%) rename docs/tutorial/03-client/{ => 01-strategy}/08-action-delegation-middleware.md (52%) rename docs/tutorial/03-client/{ => 01-strategy}/09-sensorless-data-interaction/02-virtual-data.md (99%) rename docs/tutorial/03-client/{ => 01-strategy}/09-sensorless-data-interaction/03-start-silent-factory.md (99%) rename docs/tutorial/03-client/{ => 01-strategy}/09-sensorless-data-interaction/04-conservative-request.md (95%) rename docs/tutorial/03-client/{ => 01-strategy}/09-sensorless-data-interaction/05-modify-response.md (98%) rename docs/tutorial/03-client/{ => 01-strategy}/09-sensorless-data-interaction/06-request-retry.md (96%) rename docs/tutorial/03-client/{ => 01-strategy}/09-sensorless-data-interaction/07-data-compensation.md (99%) rename docs/tutorial/03-client/{ => 01-strategy}/09-sensorless-data-interaction/08-edit-item.md (97%) rename docs/tutorial/03-client/{ => 01-strategy}/09-sensorless-data-interaction/09-what-more.md (95%) rename docs/tutorial/03-client/{ => 01-strategy}/09-sensorless-data-interaction/README.md (90%) rename docs/tutorial/03-client/{ => 01-strategy}/09-sensorless-data-interaction/_category_.json (93%) rename docs/tutorial/03-client/{ => 01-strategy}/10-use-captcha.md (90%) rename docs/tutorial/03-client/{ => 01-strategy}/11-use-serial-request.md (80%) rename docs/tutorial/03-client/{ => 01-strategy}/12-use-serial-watcher.md (80%) rename docs/tutorial/03-client/{ => 01-strategy}/13-use-retriable-request.md (96%) rename docs/tutorial/03-client/{ => 01-strategy}/14-use-sse.md (97%) rename docs/tutorial/03-client/{ => 01-strategy}/15-use-breakpoint-uploader.md (69%) rename docs/tutorial/03-client/{ => 01-strategy}/16-use-uploader.md (72%) rename docs/tutorial/03-client/{ => 01-strategy}/17-rate-limit-middleware.md (89%) rename docs/tutorial/03-client/{ => 01-strategy}/README.md (80%) create mode 100644 docs/tutorial/03-client/01-strategy/_category_.json rename docs/tutorial/{06-advanced => 03-client/02-in-depth}/01-update-across-components.md (85%) rename docs/tutorial/{06-advanced => 03-client/02-in-depth}/02-method-matcher.md (90%) rename docs/tutorial/{06-advanced/04-middleware.md => 03-client/02-in-depth/03-middleware.md} (99%) rename docs/tutorial/{06-advanced/08-manage-extra-states.md => 03-client/02-in-depth/04-manage-extra-states.md} (91%) create mode 100644 docs/tutorial/03-client/02-in-depth/_category_.json rename docs/tutorial/04-server/{ => 01-strategy}/01-retry.md (97%) rename docs/tutorial/04-server/{ => 01-strategy}/02-send-captcha.md (90%) rename docs/tutorial/04-server/{ => 01-strategy}/03-rate-limit-middleware.md (89%) rename docs/tutorial/04-server/{ => 01-strategy}/README.md (100%) create mode 100644 docs/tutorial/04-server/01-strategy/_category_.json rename docs/tutorial/06-advanced/{ => 01-in-depth}/05-custom-method-key.md (98%) rename docs/tutorial/06-advanced/{ => 01-in-depth}/07-cache-logger.md (98%) rename docs/tutorial/06-advanced/{ => 01-in-depth}/09-ssr.md (96%) rename docs/tutorial/{03-client/20-typescript.md => 06-advanced/01-in-depth/10-typescript.md} (86%) rename docs/tutorial/06-advanced/{ => 01-in-depth}/README.md (100%) create mode 100644 docs/tutorial/06-advanced/01-in-depth/_category_.json rename docs/tutorial/{08-custom => 06-advanced/02-custom}/01-http-adapter.md (99%) rename docs/tutorial/{08-custom => 06-advanced/02-custom}/02-storage-adapter.md (99%) rename docs/tutorial/{08-custom => 06-advanced/02-custom}/03-client-strategy.md (97%) rename docs/tutorial/{08-custom => 06-advanced/02-custom}/04-server-strategy.md (98%) rename docs/tutorial/{08-custom => 06-advanced/02-custom}/05-stateshook.md (99%) rename docs/tutorial/{08-custom => 06-advanced/02-custom}/README.md (88%) rename docs/tutorial/{08-custom => 06-advanced/02-custom}/_category_.json (100%) rename docs/tutorial/{07-best-practice => 07-project/01-best-practice}/01-manage-apis.md (93%) rename docs/tutorial/{07-best-practice => 07-project/01-best-practice}/02-skills.md (93%) rename docs/tutorial/{07-best-practice => 07-project/01-best-practice}/03-manage-cache-by-indexeddb.md (90%) rename docs/tutorial/{07-best-practice => 07-project/01-best-practice}/04-multiple-servers.md (93%) rename docs/tutorial/{07-best-practice => 07-project/01-best-practice}/05-middleware.md (94%) rename docs/tutorial/{07-best-practice => 07-project/01-best-practice}/06-parallel-request.md (98%) rename docs/tutorial/{07-best-practice => 07-project/01-best-practice}/07-serial-request.md (98%) rename docs/tutorial/{07-best-practice => 07-project/01-best-practice}/08-l2-storage.md (100%) create mode 100644 docs/tutorial/07-project/01-best-practice/README.md create mode 100644 docs/tutorial/07-project/01-best-practice/_category_.json rename docs/tutorial/{09-migration => 07-project/02-migration}/01-v2-to-v3.md (84%) rename docs/tutorial/{09-migration => 07-project/02-migration}/02-from-axios.md (70%) rename docs/tutorial/{09-migration => 07-project/02-migration}/_category_.json (100%) create mode 100644 docs/tutorial/07-project/_category_.json create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/about/03-qa.md rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/{ => 01-vue}/01-init-page.md (70%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/{ => 01-vue}/02-submit-form.md (64%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/{ => 01-vue}/03-condition-search.md (74%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/{ => 01-vue}/04-memory-cache.md (78%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/{ => 01-vue}/05-storage-placeholder.md (81%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/{ => 01-vue}/06-storage-restore.md (82%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/{ => 01-vue}/07-update-state.md (74%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/{ => 01-vue}/08-prefetch.md (77%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/{ => 01-vue}/09-load-more.md (74%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/{ => 01-vue}/10-paginated-list.md (75%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/{ => 01-vue}/11-controlled-cache-by-indexeddb.md (74%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/{ => 01-vue}/13-silent-submit-simple-list.md (72%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/{ => 01-vue}/19-serial-request.md (52%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/{ => 02-react}/14-silent-submit-notes.md (71%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/{ => 02-react}/15-form-hook.md (61%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/{ => 02-react}/16-captcha-send.md (54%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/{ => 02-react}/17-retriable-hook.md (60%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/{ => 02-react}/18.action-delegation-middleware.md (64%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/{ => 03-svelte}/12-silent-submit-setting.md (70%) delete mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/_category_.json rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/{README.md => 01-introduce.md} (92%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/{ => 03-basic}/03-method.md (92%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/{ => 03-basic}/04-alova.md (77%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/{ => 03-basic}/05-global-interceptor.md (97%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/{ => 03-basic}/06-method-metadata.md (95%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/{ => 03-basic}/07-combine-framework.md (95%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/{ => 03-basic}/08-server.md (88%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{ => 01-strategy}/01-use-request.md (92%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{ => 01-strategy}/02-use-watcher.md (96%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{ => 01-strategy}/03-use-fetcher.md (96%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{ => 01-strategy}/04-use-pagination.md (96%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{ => 01-strategy}/05-use-form.md (96%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{ => 01-strategy}/06-token-authentication.md (97%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{ => 01-strategy}/07-use-auto-request.md (90%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{ => 01-strategy}/08-action-delegation-middleware.md (55%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{ => 01-strategy}/09-sensorless-data-interaction/02-virtual-data.md (99%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{ => 01-strategy}/09-sensorless-data-interaction/03-start-silent-factory.md (99%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{ => 01-strategy}/09-sensorless-data-interaction/04-conservative-request.md (95%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{ => 01-strategy}/09-sensorless-data-interaction/05-modify-response.md (98%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{ => 01-strategy}/09-sensorless-data-interaction/06-request-retry.md (96%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{ => 01-strategy}/09-sensorless-data-interaction/07-data-compensation.md (99%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{ => 01-strategy}/09-sensorless-data-interaction/08-edit-item.md (95%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{ => 01-strategy}/09-sensorless-data-interaction/09-what-more.md (94%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{ => 01-strategy}/09-sensorless-data-interaction/README.md (88%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{ => 01-strategy}/09-sensorless-data-interaction/_category_.json (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{ => 01-strategy}/10-use-captcha.md (89%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{ => 01-strategy}/11-use-serial-request.md (78%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{ => 01-strategy}/12-use-serial-watcher.md (80%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{ => 01-strategy}/13-use-retriable-request.md (96%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{ => 01-strategy}/14-use-sse.md (97%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{ => 01-strategy}/15-use-breakpoint-uploader.md (69%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{ => 01-strategy}/16-use-uploader.md (98%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{ => 01-strategy}/17-rate-limit-middleware.md (85%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/{ => 01-strategy}/README.md (89%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{06-advanced => 03-client/02-in-depth}/01-update-across-components.md (84%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{06-advanced => 03-client/02-in-depth}/02-method-matcher.md (89%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{06-advanced/04-middleware.md => 03-client/02-in-depth/03-middleware.md} (98%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{06-advanced/08-manage-extra-states.md => 03-client/02-in-depth/04-manage-extra-states.md} (91%) delete mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/_category_.json rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/{ => 01-strategy}/01-retry.md (97%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/{ => 01-strategy}/02-send-captcha.md (89%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/{ => 01-strategy}/03-rate-limit-middleware.md (86%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/{ => 01-strategy}/README.md (100%) delete mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/_category_.json rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/{ => 01-in-depth}/05-custom-method-key.md (98%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/{ => 01-in-depth}/07-cache-logger.md (98%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/{ => 01-in-depth}/09-ssr.md (96%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{03-client/20-typescript.md => 06-advanced/01-in-depth/10-typescript.md} (86%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/{ => 01-in-depth}/README.md (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{08-custom => 06-advanced/02-custom}/01-http-adapter.md (99%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{08-custom => 06-advanced/02-custom}/02-storage-adapter.md (98%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{08-custom => 06-advanced/02-custom}/03-client-strategy.md (97%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{08-custom => 06-advanced/02-custom}/04-server-strategy.md (98%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{08-custom => 06-advanced/02-custom}/05-stateshook.md (99%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{08-custom => 06-advanced/02-custom}/README.md (88%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{08-custom => 06-advanced/02-custom}/_category_.json (100%) delete mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/_category_.json rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{07-best-practice => 07-project/01-best-practice}/01-manage-apis.md (91%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{07-best-practice => 07-project/01-best-practice}/02-skills.md (92%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{07-best-practice => 07-project/01-best-practice}/03-manage-cache-by-indexeddb.md (89%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{07-best-practice => 07-project/01-best-practice}/04-multiple-servers.md (92%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{07-best-practice => 07-project/01-best-practice}/05-middleware.md (97%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{07-best-practice => 07-project/01-best-practice}/06-parallel-request.md (98%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{07-best-practice => 07-project/01-best-practice}/07-serial-request.md (98%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{07-best-practice => 07-project/01-best-practice}/08-l2-storage.md (99%) create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-project/01-best-practice/README.md rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{09-migration => 07-project/02-migration}/01-v2-to-v3.md (82%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/{09-migration => 07-project/02-migration}/02-from-axios.md (69%) create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-project/_category_.json create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x.json create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/about/01-RSM.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/about/02-comparison.md rename i18n/zh-CN/docusaurus-plugin-content-docs/{current => version-2.x}/about/03-Q&A.md (83%) delete mode 100644 src/css/custom.css create mode 100644 src/css/custom.scss delete mode 100644 src/theme/AnnouncementBar/Content/styles.module.css create mode 100644 src/theme/AnnouncementBar/Content/styles.module.scss create mode 100644 static/img/announcement_bg.png create mode 100644 versioned_docs/version-2.x/about/01-RSM.md create mode 100644 versioned_docs/version-2.x/about/02-comparison.md create mode 100644 versioned_docs/version-2.x/about/03-Q&A.md rename {docs/tutorial/07-best-practice => versioned_docs/version-2.x/about}/_category_.json (59%) diff --git a/docs/about/01-RSM.md b/docs/about/01-RSM.md index 854517793..63820c100 100644 --- a/docs/about/01-RSM.md +++ b/docs/about/01-RSM.md @@ -1,61 +1,60 @@ ---- -title: Request Scene Model (RSM) -sidebar_position: 10 ---- - -## What is the request scene model - -The request scenario model is based on the perspective of the client. It describes the abstract model of the client from triggering the request intent to receiving the request result. It consists of four stages: request timing, request behavior, request event, and response management. For example, when making a request, you often need to think about the following questions, - -1. When the request is made; -2. Whether to display the request status; -3. Do you need to retry the request on failure; -4. How to process the response data; -5. Do you need to encrypt the request parameters; -6. Whether to cache the frequently used response data; -7. How to operate data across pages; -8. How to process requests in a weak or disconnected network environment; -9. ... - -`fetch` or `axios` are often more focused on how to interact with the server, but we always need to deal with the above problems by ourselves. These functions that are beneficial to application performance and stability will always allow programmers to write low-maintenance sexual code. The request scene model is to abstract all links from preparing the request to processing the response data, so as to cover the model of the entire CS interaction life cycle from the perspective of the front end. `alova` is a library that requests scene models, it is a supplement to request libraries such as `axios`, not a substitute. - -> CS interaction: generally refers to data interaction between all client types and servers - -## Request scene model - -![RSM](/img/rsm-en.png) - -## request timing - -Describe when a request needs to be made, implemented with `useHook` in `alova`. - -- Initialize display data, such as just entering a certain interface or sub-interface; -- Human-computer interaction triggers CS interaction, and the request needs to be changed again, such as page turning, filtering, sorting, fuzzy search, etc.; -- Send requests in an anti-shake manner, avoid view data flickering, and reduce server pressure -- Preloading data, such as preloading the content of the next page in a page, predicting that the user clicks a button to pre-fetch data; -- To operate server data, it is necessary to issue a request for addition, deletion, modification and query, such as submitting data, deleting data, etc.; -- Synchronize server status, such as polling requests in scenarios where data changes rapidly, and re-pull data after operating a certain data; - -## Request Behavior - -Describes how to process the request, implemented as a Method abstraction in `alova`. - -- Placeholder request, when requesting, display loading, skeleton diagram, or real data used last time; -- Cache high-frequency responses, multiple execution requests will use fresh data; -- Multi-request serial and parallel; -- The retry mechanism of important interfaces reduces the probability of request failure caused by network instability; -- Submit silently. When you only care about submitting data, directly respond to the success event after submitting the request, and the background guarantees that the request is successful; -- Offline submission, the submitted data will be temporarily stored locally when offline, and then submitted after the network connection; - -## request event - -Indicates sending a request with request parameters and getting a response. `alova` can work with any request library or native solution such as `axios`, `fetch`, `XMLHttpRequest`. - -## Response management - -`alova` makes the response data stateful and manages it in a unified manner, refreshes the view data and operates the cache at the request level, avoids operations at the component level, and is more elegant and unified. - -- Remove the cached response data, which will be pulled from the server when the request is made again; -- Update the cached response data, which can update the response data at any location, which is very helpful for updating data across pages; -- Refresh the response data, which can re-refresh the response data at any position, and is also very helpful for updating data across pages; -- Customize the cache setting. When requesting batch data, you can manually set the cache for the batch data one by one, so as to meet the cache hit of subsequent single data; +--- +title: Request Scene Model (RSM) +--- + +## What is the request scene model + +The request scenario model is based on the perspective of the client. It describes the abstract model of the client from triggering the request intent to receiving the request result. It consists of four stages: request timing, request behavior, request event, and response management. For example, when making a request, you often need to think about the following questions, + +1. When the request is made; +2. Whether to display the request status; +3. Do you need to retry the request on failure; +4. How to process the response data; +5. Do you need to encrypt the request parameters; +6. Whether to cache the frequently used response data; +7. How to operate data across pages; +8. How to process requests in a weak or disconnected network environment; +9. ... + +`fetch` or `axios` are often more focused on how to interact with the server, but we always need to deal with the above problems by ourselves. These functions that are beneficial to application performance and stability will always allow programmers to write low-maintenance sexual code. The request scene model is to abstract all links from preparing the request to processing the response data, so as to cover the model of the entire CS interaction life cycle from the perspective of the front end. `alova` is a library that requests scene models, it is a supplement to request libraries such as `axios`, not a substitute. + +> CS interaction: generally refers to data interaction between all client types and servers + +## Request scene model + +![RSM](/img/rsm-en.png) + +## request timing + +Describe when a request needs to be made, implemented with `useHook` in `alova`. + +- Initialize display data, such as just entering a certain interface or sub-interface; +- Human-computer interaction triggers CS interaction, and the request needs to be changed again, such as page turning, filtering, sorting, fuzzy search, etc.; +- Send requests in an anti-shake manner, avoid view data flickering, and reduce server pressure +- Preloading data, such as preloading the content of the next page in a page, predicting that the user clicks a button to pre-fetch data; +- To operate server data, it is necessary to issue a request for addition, deletion, modification and query, such as submitting data, deleting data, etc.; +- Synchronize server status, such as polling requests in scenarios where data changes rapidly, and re-pull data after operating a certain data; + +## Request Behavior + +Describes how to process the request, implemented as a Method abstraction in `alova`. + +- Placeholder request, when requesting, display loading, skeleton diagram, or real data used last time; +- Cache high-frequency responses, multiple execution requests will use fresh data; +- Multi-request serial and parallel; +- The retry mechanism of important interfaces reduces the probability of request failure caused by network instability; +- Submit silently. When you only care about submitting data, directly respond to the success event after submitting the request, and the background guarantees that the request is successful; +- Offline submission, the submitted data will be temporarily stored locally when offline, and then submitted after the network connection; + +## request event + +Indicates sending a request with request parameters and getting a response. `alova` can work with any request library or native solution such as `axios`, `fetch`, `XMLHttpRequest`. + +## Response management + +`alova` makes the response data stateful and manages it in a unified manner, refreshes the view data and operates the cache at the request level, avoids operations at the component level, and is more elegant and unified. + +- Remove the cached response data, which will be pulled from the server when the request is made again; +- Update the cached response data, which can update the response data at any location, which is very helpful for updating data across pages; +- Refresh the response data, which can re-refresh the response data at any position, and is also very helpful for updating data across pages; +- Customize the cache setting. When requesting batch data, you can manually set the cache for the batch data one by one, so as to meet the cache hit of subsequent single data; diff --git a/docs/about/02-comparison.md b/docs/about/02-comparison.md index c7099fc71..60cc9b198 100644 --- a/docs/about/02-comparison.md +++ b/docs/about/02-comparison.md @@ -1,93 +1,92 @@ ---- -title: Compare with other libraries -sidebar_position: 20 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -## Compare with axios - -axios provides a very simple and easy-to-use HTTP request function based on promise. It only requires a simple line of code to send and receive requests, and can run in the browser and nodejs environment. It is a very excellent request js library. - -But axios focuses on sending requests and receiving responses, which means that if you need to write more code by yourself to actively optimize the request function, and alova is like a weapon of axios, combining axios with alova can get more powerful request capabilities. The following are the request management capabilities added by alova to axios. - -### alova provides automated request status management for axios - -When you only use axios, you usually need to maintain request-related status by yourself. You can get automated request status management capabilities by using alova's use hook. - - - - -```javascript -// vue3 example -const loading = ref(false); -const data = ref({}); -const error = ref(null); -const request = async () => { - try { - loading.value = true; - data.value = await axios.get('/xxx'); - } catch (e) { - error.value = e; - } - loading.value = false; -}; -mounted(request); -``` - - - - -```javascript -// Use axios as alova's request adapter -const { loading, data, error } = useRequest(alova.Get('/xxx')); -``` - - - - -### alova provides high-performance request strategies out of the box - -alova provides you with [multiple high-performance request strategy modules](/tutorial/strategy). You can use different modules according to different request scenarios, which axios does not have. - -### alova provides response data cache for axios - -alova provides 3 caching modes to meet different caching scenarios, namely memory mode, cache occupying mode, and recovery mode. They are component-independent and can hit the cache as long as the request address and parameters are the same, unless you turn it off. Response data caching can greatly improve request fluency and reduce server pressure. - -### alova provides request sharing function for axios - -Request sharing will reuse the same request when sending multiple identical requests at the same time. It can also improve application fluency and reduce server pressure. - -### alova provides data pre-fetching for axios - -Requesting the data to be used in advance can also greatly improve application fluency. - -### alova can manage request states - -You can use alova across any component hierarchy to access stateful data in other components, which allows you to reduce some of the trouble of cross-component communication. - -## Compared with react-query and swr - -react-query is a powerful asynchronous state management, and swr is a React Hooks library for data requests. Their common feature is the use of use hooks to send and manage requests, and data caching functions. For them, alova has the following differences at. - -### alova has different goals - -In fact, alova's use hook also refers to the design of react-query and swr, but alova chooses the direction of the request strategy library. You can use different request strategy modules in different request scenarios, allowing you to write less code. At the same time, more efficient Client-Server data interaction can also be achieved. - -### Method proxy design - -Both react-query and swr use `axios` or `fetch api` directly in use hook to send requests, while alova uses the `Method` proxy design mode. This design has the following 3 benefits: - -1. Unified usage without different usage depending on the platform or UI framework. -2. Request libraries such as `axios` and `fetch api` are decoupled from each API in the form of request adapters, which allows alova to provide a unified development experience and perfect coding migration. -3. Each `Method` instance represents an API, you can aggregate the request parameters and request behavior parameters of the same API into the same `Method` instance without spreading them to different files, which is more suitable for managing a large number of APIs. -4. alova realizes automatic management of response data cache by serializing request parameters on the `Method` instance. You do not need to specify the cache key, and both react-query and swr need to customize the `queryKey` to manage the cache. - -### High flexibility - -Alova achieves high flexibility through various adapters and middleware. It can not only run in any js environment, but also support users to customize request modules in different scenarios. - -### Lightweight - -alova is very lightweight, and its size is only 30%+ of react-query and axios. Similar in size to swr, but provides richer functionality. +--- +title: Compare with other libraries +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## Compare with axios + +axios provides a very simple and easy-to-use HTTP request function based on promise. It only requires a simple line of code to send and receive requests, and can run in the browser and nodejs environment. It is a very excellent request js library. + +But axios focuses on sending requests and receiving responses, which means that if you need to write more code by yourself to actively optimize the request function, and alova is like a weapon of axios, combining axios with alova can get more powerful request capabilities. The following are the request management capabilities added by alova to axios. + +### alova provides automated request status management for axios + +When you only use axios, you usually need to maintain request-related status by yourself. You can get automated request status management capabilities by using alova's use hook. + + + + +```javascript +// vue3 example +const loading = ref(false); +const data = ref({}); +const error = ref(null); +const request = async () => { + try { + loading.value = true; + data.value = await axios.get('/xxx'); + } catch (e) { + error.value = e; + } + loading.value = false; +}; +mounted(request); +``` + + + + +```javascript +// Use axios as alova's request adapter +const { loading, data, error } = useRequest(alova.Get('/xxx')); +``` + + + + +### alova provides high-performance request strategies out of the box + +alova provides you with [multiple high-performance request strategy modules](/next/tutorial/client/strategy). You can use different modules according to different request scenarios, which axios does not have. + +### alova provides response data cache for axios + +alova provides 3 caching modes to meet different caching scenarios, namely memory mode, cache occupying mode, and recovery mode. They are component-independent and can hit the cache as long as the request address and parameters are the same, unless you turn it off. Response data caching can greatly improve request fluency and reduce server pressure. + +### alova provides request sharing function for axios + +Request sharing will reuse the same request when sending multiple identical requests at the same time. It can also improve application fluency and reduce server pressure. + +### alova provides data pre-fetching for axios + +Requesting the data to be used in advance can also greatly improve application fluency. + +### alova can manage request states + +You can use alova across any component hierarchy to access stateful data in other components, which allows you to reduce some of the trouble of cross-component communication. + +## Compared with react-query and swr + +react-query is a powerful asynchronous state management, and swr is a React Hooks library for data requests. Their common feature is the use of use hooks to send and manage requests, and data caching functions. For them, alova has the following differences at. + +### alova has different goals + +In fact, alova's use hook also refers to the design of react-query and swr, but alova chooses the direction of the request strategy library. You can use different request strategy modules in different request scenarios, allowing you to write less code. At the same time, more efficient Client-Server data interaction can also be achieved. + +### Method proxy design + +Both react-query and swr use `axios` or `fetch api` directly in use hook to send requests, while alova uses the `Method` proxy design mode. This design has the following 3 benefits: + +1. Unified usage without different usage depending on the platform or UI framework. +2. Request libraries such as `axios` and `fetch api` are decoupled from each API in the form of request adapters, which allows alova to provide a unified development experience and perfect coding migration. +3. Each `Method` instance represents an API, you can aggregate the request parameters and request behavior parameters of the same API into the same `Method` instance without spreading them to different files, which is more suitable for managing a large number of APIs. +4. alova realizes automatic management of response data cache by serializing request parameters on the `Method` instance. You do not need to specify the cache key, and both react-query and swr need to customize the `queryKey` to manage the cache. + +### High flexibility + +Alova achieves high flexibility through various adapters and middleware. It can not only run in any js environment, but also support users to customize request modules in different scenarios. + +### Lightweight + +alova is very lightweight, and its size is only 30%+ of react-query and axios. Similar in size to swr, but provides richer functionality. diff --git a/docs/about/03-Q&A.md b/docs/about/03-Q&A.md deleted file mode 100644 index 2b0f56323..000000000 --- a/docs/about/03-Q&A.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -title: Question & Answer -sidebar_position: 30 ---- - -## Why to create alova? - -Data requests have always been an indispensable and important part of applications. Since the birth of XMLHttpRequest, request schemes have emerged in endlessly. Client-side data interaction exploration has always focused on the simplicity of requests, such as `$.ajax`, `axios`, `fetch api` and Request tools such as `react-query`, the coding form continues to evolve from callback functions, Promise, to usehook. These js libraries have done a good job in making requests simple, but they only provide common functions, which means For different request scenarios such as sharing requests, paging requests, form submissions, uploading and downloading files, etc., developers still need to write complex codes themselves, which reduces development efficiency and performance cannot be guaranteed. As user experience becomes more and more important, In this era, application fluency has become more and more important. - -Additionally, the collaboration between client and server is also separated. Front-end engineers need to consult API documents and manually write API functions, and any changes of APIs need to actively notify front-end engineers, which will make your product more uncontrollable. - -**We think there is a simpler solution is that based on your request scenarios such as pagination, form submission, breakpoint resumption, etc., select the corresponding useHook, which will help you manage data and control when requests should be sent**. This allows developers to achieve more efficient Client-Server data interaction while writing little code. - -Additionally, alova has very flexible expansion capabilities to implement request strategies in different scenarios. You can also customize your own request scenarios. This part is in [Custom Chapter](/category/custom). - -In order to cover more request scenarios, we also abstracted the request scenarios into [Request scene model(RSM)](/tutorial/others/RSM), which explains alova's request strategy scheme well. In the future, alova will continue to carry forward our exploration of request strategies. - -## Alternative to the request libraries? - -alova is a request strategy library, which was originally created to provide specific request strategy solutions for different request scenarios, so as to achieve a smooth request experience more concisely and elegantly, such as `$.ajax`, `axios` and `fetch- api`, etc. provide good support for request sending and response receiving, they are an essential part of the [RSM](/tutorial/others/RSM) process (request events), alova still needs to dependent on them to make requests, so we can Think of alova as an weaponry of the request library, making the request library more powerful. - -## Why binding UI framework? - -Decoupling a js library means using it in more scenarios. For example, axios can be used in nodejs, but it also means that developers need to write more template code, such as using useHooks to encapsulate axios. However, alova abandons more usage scenarios brought about by decoupling, and positions the scope of use in conjunction with the UI framework to use alova in the most streamlined way. This is for the benefit of developers and is prevalent in a UI framework. Sometimes, deep binding can provide developers with direct-use functions and improve the developer's experience without requiring too much template code. diff --git a/docs/about/03-qa.md b/docs/about/03-qa.md new file mode 100644 index 000000000..700caa69f --- /dev/null +++ b/docs/about/03-qa.md @@ -0,0 +1,133 @@ +--- +title: Question & Answer +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## Why to create alova? + +Data requests have always been an indispensable and important part of applications. Since the birth of XMLHttpRequest, request schemes have emerged in endlessly. Client-side data interaction exploration has always focused on the simplicity of requests, such as `$.ajax`, `axios`, `fetch api` and Request tools such as `react-query`, the coding form continues to evolve from callback functions, Promise, to usehook. These js libraries have done a good job in making requests simple, but they only provide common functions, which means For different request scenarios such as sharing requests, paging requests, form submissions, uploading and downloading files, etc., developers still need to write complex codes themselves, which reduces development efficiency and performance cannot be guaranteed. As user experience becomes more and more important, In this era, application fluency has become more and more important. + +Additionally, the collaboration between client and server is also separated. Front-end engineers need to consult API documents and manually write API functions, and any changes of APIs need to actively notify front-end engineers, which will make your product more uncontrollable. + +**We think there is a simpler solution is that based on your request scenarios such as pagination, form submission, breakpoint resumption, etc., select the corresponding useHook, which will help you manage data and control when requests should be sent**. This allows developers to achieve more efficient Client-Server data interaction while writing little code. + +Additionally, alova has very flexible expansion capabilities to implement request strategies in different scenarios. You can also customize your own request scenarios. This part is in [Custom Chapter](/next/tutorial/advanced/custom). + +In order to cover more request scenarios, we also abstracted the request scenarios into [Request scene model(RSM)](/about/RSM), which explains alova's request strategy scheme well. In the future, alova will continue to carry forward our exploration of request strategies. + +## Alternative to the request libraries? + +alova is a request strategy library, which was originally created to provide specific request strategy solutions for different request scenarios, so as to achieve a smooth request experience more concisely and elegantly, such as `$.ajax`, `axios` and `fetch- api`, etc. provide good support for request sending and response receiving, they are an essential part of the [RSM](/about/RSM) process (request events), alova still needs to dependent on them to make requests, so we can Think of alova as an weaponry of the request library, making the request library more powerful. + +## Why binding UI framework? + +Decoupling a js library means using it in more scenarios. For example, axios can be used in nodejs, but it also means that developers need to write more template code, such as using useHooks to encapsulate axios. However, alova abandons more usage scenarios brought about by decoupling, and positions the Scope of usage in conjunction with the UI framework to use alova in the most streamlined way. This is for the benefit of developers and is prevalent in a UI framework. Sometimes, deep binding can provide developers with direct-use functions and improve the developer's experience without requiring too much template code. + +## How to use alova via cdn? + + + + +```html + + + + + + + + + +
+
Loading...
+
{{ error.message }}
+ responseData: {{ data }} +
+ + + +``` + +
+ + +```html + + + + + + + + + + + +
+ + + +``` + +
+ + +:::tip + +svelte depends on the compilation tool and cannot be used directly through CDN. For details, see [svelte.dev](https://svelte.dev/) + +::: + + +
+ +## What to pay attention to in React-Native? + +When using alova to develop React-Native applications, you can also use `alova/fetch`. + +But there are the following precautions: + +**Metro version** + +In alova's `package.json`, `exports` is used to define multiple export items, so you need to ensure these two points: + +1. Metro version is higher than 0.76.0 + +2. Enable `resolver.unstable_enablePackageExports` in `metro.config.js`. [Click here for details](https://facebook.github.io/metro/docs/configuration/#unstable_enablepackageexports-experimental) diff --git a/docs/api/01-alova.md b/docs/api/01-alova.md index 08201547c..a23583020 100644 --- a/docs/api/01-alova.md +++ b/docs/api/01-alova.md @@ -1,6 +1,5 @@ --- title: alova instance -sidebar_position: 10 --- ## createAlova() @@ -17,19 +16,18 @@ function createAlova(options?: AlovaOptions): Alova; 1. config: configuration parameters -| Parameter name | Type | Description | -| -------------- | --------------------------- | --------------------------------------------------------------------------------------------------------- | -| baseURL | string | Base path, empty by default, [View details](/tutorial/getting-started/alova) | -| statesHook | object | State management hook, optional, [View details](/tutorial/combine-framework) | -| requestAdapter | object | Request adapter, required, [View details](/tutorial/custom/custom-http-adapter) | -| timeout | number | Timeout time, no timeout by default, [View details](/tutorial/getting-started/alova) | -| localCache | object | Local cache configuration, default GET has 5000ms cache, [View details](/tutorial/cache/mode) | -| storageAdapter | object | Local storage adapter, default is `localStorage`, [View details](/tutorial/custom/custom-storage-adapter) | -| beforeRequest | function | Before request hook, [View details](/tutorial/getting-started/global-interceptor) | -| responded | object \| function | Request response hook, [View details](/tutorial/getting-started/global-interceptor) | -| shareRequest | boolean | Share request, [View details](/tutorial/getting-started/alova) | -| errorLogger | boolean\| null \| function | Error log, [View details](/tutorial/advanced/error-logger) | -| cacheLogger | boolean \| null \| function | Cache log, [View details](/tutorial/advanced/cache-logger) | +| Parameter name | Type | Description | +| -------------- | --------------------------- | ---------------------------------------------------------------------------------------------------------------- | +| baseURL | string | Base path, empty by default, [View details](/next/tutorial/getting-started/basic/alova) | +| statesHook | object | State management hook, optional, [View details](/next/tutorial/getting-started/basic/combine-framework) | +| requestAdapter | object | Request adapter, required, [View details](/next/tutorial/advanced/custom/http-adapter) | +| timeout | number | Timeout time, no timeout by default, [View details](/next/tutorial/getting-started/basic/alova) | +| localCache | object | Local cache configuration, default GET has 5000ms cache, [View details](/next/tutorial/cache/mode) | +| storageAdapter | object | Local storage adapter, default is `localStorage`, [View details](/next/tutorial/advanced/custom/storage-adapter) | +| beforeRequest | function | Before request hook, [View details](/next/tutorial/getting-started/basic/global-interceptor) | +| responded | object \| function | Request response hook, [View details](/next/tutorial/getting-started/basic/global-interceptor) | +| shareRequest | boolean | Share request, [View details](/next/tutorial/getting-started/basic/alova) | +| cacheLogger | boolean \| null \| function | Cache log, [View details](/next/tutorial/advanced/in-depth/cache-logger) | - **return** @@ -39,11 +37,13 @@ Alova instance ```ts import { createAlova } from 'alova'; +import adapterFetch from 'alova/fetch'; +import VueHook from 'alova/vue'; const alova = createAlova({ baseURL: 'https://example.com', statesHook: VueHook, - requestAdapter: GlobalFetch(), + requestAdapter: adapterFetch(), timeout: 3000 // ... }); @@ -51,7 +51,7 @@ const alova = createAlova({ ## alova.id -The alova instance id is used to distinguish different alova instances. It can accurately match the method instance of the specified alova in [method instance matcher](/tutorial/advanced/method-matcher). +The alova instance id is used to distinguish different alova instances. It can accurately match the method instance of the specified alova in [method instance matcher](/next/tutorial/client/in-depth/method-matcher). - **Type**: string @@ -108,18 +108,16 @@ interface Alova { 1. url: request address 2. config: configuration parameters -| Parameter name | Type | Description | -| -------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| headers | object | Request headers, [View details](/tutorial/getting-started/method) | -| params | object | Request parameters, [View details](/tutorial/getting-started/method) | -| name | string | method object name, in [updateState](/tutorial/advanced/update-across-components), [invalidateCache](/tutorial/cache/manually-invalidate), [setCache](/tutorial/cache/set-and-query), and [fetch function](/tutorial/advanced/use-fetcher), you can obtain the corresponding method instance by name or wildcard | -| timeout | number | Request timeout, [View details](/tutorial/getting-started/method) | -| localCache | LocalCacheConfig | Response cache time, [View details](/tutorial/cache/mode) | -| hitSource | string | Hit the source method instance. When the source method instance request is successful, the cache of the current method instance will be invalidated. [View details](/tutorial/cache/auto-invalidate) | -| enableDownload | boolean | Enable download progress information, [View details](/tutorial/combine-framework/download-upload-progress) | -| enableUpload | boolean | Enable upload progress information, [View details](/tutorial/combine-framework/download-upload-progress) | -| transformData | function | Transform response data, [View details](/tutorial/combine-framework/response) | -| shareRequest | boolean | Request-level sharing request switch, [View details](/tutorial/getting-started/method) | +| Parameter name | Type | Description | +| -------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| headers | object | Request headers, [View details](/next/tutorial/getting-started/basic/method) | +| params | object | Request parameters, [View details](/next/tutorial/getting-started/basic/method) | +| name | string | method object name, in [updateState](/next/tutorial/client/in-depth/update-across-components), [invalidateCache](/next/tutorial/cache/manually-invalidate), [setCache](/next/tutorial/cache/set-and-query), and [fetch function](/next/tutorial/client/strategy/use-fetcher), you can obtain the corresponding method instance by name or wildcard | +| timeout | number | Request timeout, [View details](/next/tutorial/getting-started/basic/method) | +| localCache | LocalCacheConfig | Response cache time, [View details](/next/tutorial/cache/mode) | +| hitSource | string | Hit the source method instance. When the source method instance request is successful, the cache of the current method instance will be invalidated. [View details](/next/tutorial/cache/auto-invalidate) | +| transformData | function | Transform response data, [View details](/next/tutorial/getting-started/basic/method) | +| shareRequest | boolean | Request-level sharing request switch, [View details](/next/tutorial/getting-started/basic/method) | > In addition to the configurable parameters above, other parameters supported by the request adapter are also supported. @@ -146,7 +144,11 @@ Create a method instance for the POST request. ```ts interface Alova { - Post(url: string, data?: object | FormData | string | null, config?: AlovaMethodCreateConfig): Method; + Post( + url: string, + data?: object | FormData | string | null, + config?: AlovaMethodCreateConfig + ): Method; } ``` @@ -184,7 +186,11 @@ Create a method instance for the DELETE request. ```ts interface Alova { - Delete(url: string, data?: object | FormData | string | null, config?: AlovaMethodCreateConfig): Method; + Delete( + url: string, + data?: object | FormData | string | null, + config?: AlovaMethodCreateConfig + ): Method; } ``` @@ -220,7 +226,11 @@ Create a method instance for the PUT request. ```ts interface Alova { - Put(url: string, data?: object | FormData | string | null, config?: AlovaMethodCreateConfig): Method; + Put( + url: string, + data?: object | FormData | string | null, + config?: AlovaMethodCreateConfig + ): Method; } ``` @@ -278,7 +288,11 @@ Create a method instance for the PATCH request. ```ts interface Alova { - Patch(url: string, data?: object | FormData | string | null, config?: AlovaMethodCreateConfig): Method; + Patch( + url: string, + data?: object | FormData | string | null, + config?: AlovaMethodCreateConfig + ): Method; } ``` diff --git a/docs/api/02-method.md b/docs/api/02-method.md index 4cda9714b..7c91ddf74 100644 --- a/docs/api/02-method.md +++ b/docs/api/02-method.md @@ -1,6 +1,5 @@ --- title: method instance -sidebar_position: 20 --- A method instance corresponds to a request information description, which has the URL, request headers, request parameters of a request, as well as request behavior parameters such as response data processing and cache data processing. Through method instances, you can feel a unified usage experience in any js environment, and it can run normally with very few changes. In addition, method instances put request parameters and request behavior parameters together, making it easier for APIs management instead of spreading it across multiple code files. @@ -52,7 +51,7 @@ interface MethodConstructor { 1. `type`: request type 2. `context`: alova instance 3. `url`: request url -4. `config`: Configuration parameters, the type is the same as config parameter type of [alova.Get](/api/alova#alovaget) +4. `config`: Configuration parameters, the type is the same as config parameter type of [alova.Get](/next/api/alova#alovaget) 5. `data`: request body data - **Example** @@ -97,7 +96,7 @@ const methodKey = getMethodKey(method); ## matchSnapshotMethod() -Obtain the requested method instance snapshot using the matching method of [method instance matcher](/tutorial/advanced/method-matcher) and return the matching result. +Obtain the requested method instance snapshot using the matching method of [method instance matcher](/next/tutorial/client/in-depth/method-matcher) and return the matching result. - **type** @@ -110,7 +109,10 @@ type MethodFilter = filter?: MethodFilterHandler; alova?: Alova; }; -function matchSnapshotMethod(matcher: MethodFilter, matchAll?: boolean): Method[] | Method | undefined; +function matchSnapshotMethod( + matcher: MethodFilter, + matchAll?: boolean +): Method[] | Method | undefined; ``` - **Parameters** @@ -151,7 +153,7 @@ interface Method { ## method.baseURL -The base path of the request, inherited from [alova instance](/api/alova). +The base path of the request, inherited from [alova instance](/next/api/alova). - **type** @@ -223,7 +225,7 @@ interface Method { ## method.meta -The metadata of method is used to record request feature information, [View details](/tutorial/getting-started/method-metadata). +The metadata of method is used to record request feature information, [View details](/next/tutorial/getting-started/basic/method-metadata). - **type** @@ -235,7 +237,7 @@ interface Method { ## method.config -Configuration information when creating a method through `alova.Get/alova.Post` and other methods, [View details](/api/alova#alovaget). +Configuration information when creating a method through `alova.Get/alova.Post` and other methods, [View details](/next/api/alova#alovaget). - **type** @@ -311,7 +313,10 @@ After `[v2.16.0]`, the method instance is a PromiseLike instance. You can direct ```ts interface Method { - then(onFulfilled?: (value: Response) => any, onRejected?: (reason: any) => any): Promise; + then( + onFulfilled?: (value: Response) => any, + onRejected?: (reason: any) => any + ): Promise; } ``` diff --git a/docs/api/03-core-hooks.md b/docs/api/03-core-hooks.md index 99327dd7a..78c3f5db4 100644 --- a/docs/api/03-core-hooks.md +++ b/docs/api/03-core-hooks.md @@ -1,268 +1,267 @@ ---- -title: Core useHooks -sidebar_position: 30 ---- - -## useRequest - -Represents the sending of a request. When executing useRequest, a request will be sent by default, and stateful request-related data will be created and maintained, such as `loading/data/error`, etc. It is the most commonly used method when obtaining initial data on the page. It also supports turning off its default request sending, which is very useful in request scenarios triggered by click events such as submitting data. - -> Go to [Send Request](/tutorial/combine-framework/use-request) for details. - -### type - -```ts -function useRequest( - methodHandler: Method | (...args: any[]) => Method, - config?: RequestHookConfig -): UseHookReturnType; -``` - -### Parameters - -1. `methodHandler`: can be passed in two forms: method instance and function. When specified as a function, it can receive parameters passed in by `send` and require a method instance to be returned. -2. `config`: hook configuration parameters. - -| Name | Description | Type | Default | Version | -| ------------- | -------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ------- | ------- | -| immediate | Whether to initiate the request immediately | boolean | true | - | -| initialData | The initial data value. The data value is the initial value before the first response. If it is not set, it is `undefined` | any | - | - | -| force | Whether to force the request, it can be set to the function to dynamically return a boolean value | boolean \| (...args: any[]) => boolean \| false | - | - | -| managedStates | Additional managed states, which can be updated via updateState | Record\ | - | - | -| middleware | Middleware function, [Understanding alova middleware](/tutorial/advanced/middleware) | (context: [AlovaFrontMiddlewareContext](#alovafrontmiddlewarecontext), next: [AlovaGuardNext](#alovaguardnext)) => Promise\ | - | - | - -#### AlovaFrontMiddlewareContext - -| Name | Description | Type | Version | -| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | -| method | The method object of the current request | Method | - | -| cachedResponse | Hit cached data | any | - | -| config | Current use hook configuration | Record\ | - | -| sendArgs | Parameters of the response processing callback, which are passed in by send of use hooks | any[] | - | -| frontStates | use hook front-end state collection, such as data, loading, error, etc. | [FrontRequestState](#frontrequeststate) | - | -| send | Send request function | (...args: any[]) => Promise | - | -| abort | abort function | () => void | - | -| decorateSuccess | Decorate success callback function | (decorator: (
handler: (event: [AlovaSuccessEvent](#alovasuccessevent)) => void,
event: [AlovaSuccessEvent](#alovasuccessevent),
index: number,
length: number
) => void) => void | - | -| decorateError | Decoration failure callback function | (decorator: (
handler: (event: [AlovaErrorEvent](#alovaerrorevent)) => void,
event: [AlovaErrorEvent](#alovaerrorevent),
index: number,
length: number
) => void) => void | - | -| decorateComplete | Decoration completion callback function | (decorator: (
handler: (event: [AlovaCompleteEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovacompleteevent),
index: number,
length: number
) => void) => void | - | -| update | Function to update the current use hook front-end state, more useful in react | (newFrontStates: [FrontRequestState](#frontrequeststate)) => void; | - | -| controlLoading | Customize the loading state of the control. The call will no longer trigger changes in the loading state. When the incoming control is false, the control will be cancelled. | (control?: boolean) => void | - | - -#### AlovaGuardNext - -```typescript -type AlovaGuardNext = (guardNextConfig?: { - force?: boolean | (...args: any[]) => boolean; - method?: Method; -}): Promise; -``` - -#### FrontRequestState - -The following attribute values will automatically infer the responsive data type of the corresponding UI framework based on `statesHook`, which is the `Ref` type in vue3, the normal value in react, and the `Writable` type in svelte - -| Name | Description | Type | Version | -| ----------- | ----------------------------- | ------------------ | ------- | -| loading | request loading status | boolean | - | -| data | response data | any | - | -| error | Request error information | Error \| undefined | - | -| downloading | Download progress information | Object | - | -| uploading | Upload progress information | Object | - | - -#### AlovaSuccessEvent - -| Name | Description | Type | Version | -| --------- | ---------------------------------------------------------------------------------------- | ------- | ------- | -| method | The method object of the current request | Method | - | -| sendArgs | Parameters of the response processing callback, which are passed in by send of use hooks | any[] | - | -| data | response data | any | - | -| fromCache | Whether the response data comes from cache | boolean | - | - -#### AlovaErrorEvent - -| Name | Description | Type | Version | -| -------- | ---------------------------------------------------------------------------------------- | ------ | ------- | -| method | The method object of the current request | Method | - | -| sendArgs | Parameters of the response processing callback, which are passed in by send of use hooks | any[] | - | -| error | Response error instance | Error | - | - -#### AlovaCompleteEvent - -| Name | Description | Type | Version | -| --------- | ---------------------------------------------------------------------------------------- | -------------------- | ------- | -| method | The method object of the current request | Method | - | -| sendArgs | Parameters of the response processing callback, which are passed in by send of use hooks | any[] | - | -| status | Response status, success when successful, error when failure | 'success' \| 'error' | - | -| data | response data, with value when successful | any | - | -| fromCache | Whether the response data comes from the cache, a value if successful | boolean | - | -| error | Response error instance, with value in case of failure | Error | - | - -### return value - -`UseHookReturnType` contains response data and request-related states, operation functions and event binding functions. They will automatically infer the responsive data type of the corresponding UI framework based on `statesHook`, which is the `Ref` type in vue3 and the `Ref` type in react. It is a normal value and is of `Writable` type in svelte. - -#### Responsive data - -| Name | Description | Type | Version | -| ----------- | ----------------------------- | ------------------ | ------- | -| loading | request loading status | boolean | - | -| data | response data | any | - | -| error | Request error information | Error \| undefined | - | -| downloading | Download progress information | Object | - | -| uploading | Upload progress information | Object | - | - -#### Operation function - -| name | description | function parameters | return value | version | -| ------ | ----------------------------------------------------------------------------- | ------------------------------------------------------- | ------------ | ------- | -| send | Send request function | ...args: any[] | - | - | -| abort | abort function | - | Promise | - | -| update | Function to update the current use hook front-end state, more useful in react | newFrontStates: [FrontRequestState](#frontrequeststate) | - | - -#### event - -| name | description | callback parameters | version | -| ---------- | -------------------------------- | ------------------------------------------------ | ------- | -| onSuccess | Request success event binding | event: [AlovaSuccessEvent](#alovasuccessevent) | - | -| onError | Request error event binding | event: [AlovaErrorEvent](#alovaerrorevent) | - | -| onComplete | Request completion event binding | event: [AlovaCompleteEvent](#alovacompleteevent) | - | - -## useWatcher - -Monitor the status and initiate a request after the status changes. In some scenarios that require re-requesting as the data changes, such as paging, data filtering, and fuzzy search. - -> Go to [State Change Request](/tutorial/combine-framework/use-watcher) for details. - -### type - -```typescript -function useWatcher( - handler: Method | (...args: any[]) => Method, - watchingStates: State[], - config?:WatcherHookConfig -): UseHookReturnType; -``` - -### Parameters - -1. `handler`: can be passed in two forms: method instance and function. When specified as a function, it can receive parameters passed in by `send` and require a method instance to be returned. -2. `config`: hook configuration parameters. - -| Name | Description | Type | Default | Version | -| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- | ---------- | ------- | -| immediate | Whether to initiate the request immediately | boolean | true | - | -| initialData | The initial data value. The data value is the initial value before the first response. If it is not set, it is `undefined` | any | - | - | -| force | Whether to force the request, it can be set to the function to dynamically return a boolean value | boolean \| (...args: any[]) => boolean \| false | -| managedStates | Additional managed states, which can be updated via updateState | Record\ | - | -| debounce | Request debounce time (milliseconds), when passing in the array, you can set the debounce time individually in the order of watchingStates | number \| number[] | - | -| middleware | Middleware function, [Understanding alova middleware](/tutorial/advanced/middleware) | (context: [AlovaFrontMiddlewareContext](#alovafrontmiddlewarecontext), next: [AlovaGuardNext](#alovaguardnext)) => Promise\ | - | - | -| sendable | Whether to send a request when the monitored state changes | (methodInstance: AlovaEvent) => boolean | () => true | - | -| abortLast | Whether to abort the last unresponsive request | boolean | true | - | - -### return value - -`UseHookReturnType` contains response data and request-related states, operation functions and event binding functions. They will automatically infer the responsive data type of the corresponding UI framework based on statesHook. It is Ref type in vue3 and ordinary value in react. In svelte it is Writable type. - -#### Responsive data - -| Name | Description | Type | Version | -| ----------- | ----------------------------- | ------------------ | ------- | -| loading | request loading status | boolean | - | -| data | response data | any | - | -| error | Request error information | Error \| undefined | - | -| downloading | Download progress information | Object | - | -| uploading | Upload progress information | Object | - | - -#### Operation function - -| name | description | function parameters | return value | version | -| ------ | ----------------------------------------------------------------------------- | ------------------------------------------------------- | ------------ | ------- | -| send | Send request function | ...args: any[] | Promise | - | -| abort | abort function | - | - | - | -| update | Function to update the current use hook front-end state, more useful in react | newFrontStates: [FrontRequestState](#frontrequeststate) | - | - -#### event - -| name | description | callback parameters | version | -| ---------- | -------------------------------- | ------------------------------------------------ | ------- | -| onSuccess | Request success event binding | event: [AlovaSuccessEvent](#alovasuccessevent) | - | -| onError | Request error event binding | event: [AlovaErrorEvent](#alovaerrorevent) | - | -| onComplete | Request completion event binding | event: [AlovaCompleteEvent](#alovacompleteevent) | - | - -## useFetcher - -It is used to pull data through `useFetcher`, which is useful when preloading data and updating status across modules. - -> Go to [Data Fetching](/tutorial/advanced/use-fetcher) to view details. - -### type - -```typescript -function useFetcher(config?: FetcherHookConfig): UseFetchHookReturnType; -``` - -### Parameters - -1. `config`: hook configuration parameters. - -| Name | Description | Type | Default | Version | -| ---------- | ------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ | ------- | -| force | Whether to force the request, it can be set to the function to dynamically return a boolean value | boolean | (...args: any[]) => boolean \| false | - | -| middleware | Middleware function, [Understand alova middleware](/tutorial/advanced/middleware) | (context: [AlovaFetcherMiddlewareContext](#alovafetchermiddlewarecontext), next: [AlovaGuardNext](#alovaguardnext)) => Promise\ | - | - | - -#### AlovaFetcherMiddlewareContext - -| Name | Description | Type | Version | -| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | -| method | The method object of the current request | Method | - | -| cachedResponse | Hit cached data | any | - | -| config | Current use hook configuration | Record\ | - | -| fetchArgs | Parameters of the response processing callback, which are passed in by fetch of useFetcher | any[] | - | -| fetchStates | use hook preload state collection, such as fetching, error, etc. | [FetchRequestState](#fetchrequeststate) | - | -| fetch | Data preloading function | (method: Method, ...args: any[]) => Promise | - | -| abort | abort function | () => void | - | -| decorateSuccess | Decorate success callback function | (decorator: (
handler: (event: [AlovaSuccessEvent](#alovasuccessevent)) => void,
event: [AlovaSuccessEvent](#alovasuccessevent),
index: number,
length: number
) => void) => void | - | -| decorateError | Decoration failure callback function | (decorator: (
handler: (event: [AlovaErrorEvent](#alovaerrorevent)) => void,
event: [AlovaErrorEvent](#alovaerrorevent),
index: number,
length: number
) => void) => void | - | -| decorateComplete | Decoration completion callback function | (decorator: (
handler: (event: [AlovaCompleteEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovacompleteevent),
index: number,
length: number
) => void) => void | - | -| update | Function to update the current use hook preloading state, more useful in react | (newFrontStates: [FetchRequestState](#fetchrequeststate)) => void; | - | -| controlFetching | After calling, the state of fetching will be customized and the change of fetching state will no longer be triggered internally. When the incoming control is false, the control will be canceled | (control?: boolean) => void | - | - -#### FetchRequestState - -The following attribute values will automatically infer the responsive data type of the corresponding UI framework based on `statesHook`, which is the `Ref` type in vue3, the normal value in react, and the `Writable` type in svelte - -| Name | Description | Type | Version | -| ----------- | ----------------------------- | ------------------ | ------- | -| fetching | preloading request status | boolean | - | -| error | Request error information | Error \| undefined | - | -| downloading | Download progress information | Object | - | -| uploading | Upload progress information | Object | - | - -### return value - -`UseFetchHookReturnType` contains request-related states, operation functions and event binding functions. They will automatically infer the responsive data type of the corresponding UI framework based on statesHook. It is Ref type in vue3, ordinary value in react, and ordinary value in svelte. For Writable type. - -#### Responsive data - -| Name | Description | Type | Version | -| ----------- | ----------------------------- | ------------------ | ------- | -| fetching | preloading request status | boolean | - | -| error | Request error information | Error \| undefined | - | -| downloading | Download progress information | Object | - | -| uploading | Upload progress information | Object | - | - -#### Operation function - -| name | description | function parameters | return value | version | -| ------ | ----------------------------------------------------------------------------- | ---------------------------------------------------------- | ------------ | ------- | -| fetch | Data preloading function | 1. method: preloaded Method instance
2. ...args: any[] | Promise | - | -| abort | abort function | - | - | - | -| update | Function to update the current use hook front-end state, more useful in react | newFrontStates: [FrontRequestState](#frontrequeststate) | - | - -#### event - -| name | description | callback parameters | version | -| ---------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------- | ------- | -| onSuccess | Request success event binding | event: [AlovaSuccessEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovasuccessevent) | - | -| onError | Request error event binding | event: [AlovaErrorEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovaerrorevent) | - | -| onComplete | Request completion event binding | event: [AlovaCompleteEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovacompleteevent) | - | +--- +title: Core useHooks +--- + +## useRequest + +Represents the sending of a request. When executing useRequest, a request will be sent by default, and stateful request-related data will be created and maintained, such as `loading/data/error`, etc. It is the most commonly used method when obtaining initial data on the page. It also supports turning off its default request sending, which is very useful in request scenarios triggered by click events such as submitting data. + +> Go to [Send Request](/next/tutorial/client/strategy/use-request) for details. + +### type + +```ts +function useRequest( + methodHandler: Method | (...args: any[]) => Method, + config?: RequestHookConfig +): UseHookReturnType; +``` + +### Parameters + +1. `methodHandler`: can be passed in two forms: method instance and function. When specified as a function, it can receive parameters passed in by `send` and require a method instance to be returned. +2. `config`: hook configuration parameters. + +| Name | Description | Type | Default | Version | +| ------------- | -------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ------- | ------- | +| immediate | Whether to initiate the request immediately | boolean | true | - | +| initialData | The initial data value. The data value is the initial value before the first response. If it is not set, it is `undefined` | any | - | - | +| force | Whether to force the request, it can be set to the function to dynamically return a boolean value | boolean \| (...args: any[]) => boolean \| false | - | - | +| managedStates | Additional managed states, which can be updated via updateState | Record\ | - | - | +| middleware | Middleware function, [Understanding alova middleware](/next/tutorial/client/in-depth/middleware) | (context: [AlovaFrontMiddlewareContext](#alovafrontmiddlewarecontext), next: [AlovaGuardNext](#alovaguardnext)) => Promise\ | - | - | + +#### AlovaFrontMiddlewareContext + +| Name | Description | Type | Version | +| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | +| method | The method object of the current request | Method | - | +| cachedResponse | Hit cached data | any | - | +| config | Current use hook configuration | Record\ | - | +| sendArgs | Parameters of the response processing callback, which are passed in by send of use hooks | any[] | - | +| frontStates | use hook front-end state collection, such as data, loading, error, etc. | [FrontRequestState](#frontrequeststate) | - | +| send | Send request function | (...args: any[]) => Promise | - | +| abort | abort function | () => void | - | +| decorateSuccess | Decorate success callback function | (decorator: (
handler: (event: [AlovaSuccessEvent](#alovasuccessevent)) => void,
event: [AlovaSuccessEvent](#alovasuccessevent),
index: number,
length: number
) => void) => void | - | +| decorateError | Decoration failure callback function | (decorator: (
handler: (event: [AlovaErrorEvent](#alovaerrorevent)) => void,
event: [AlovaErrorEvent](#alovaerrorevent),
index: number,
length: number
) => void) => void | - | +| decorateComplete | Decoration completion callback function | (decorator: (
handler: (event: [AlovaCompleteEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovacompleteevent),
index: number,
length: number
) => void) => void | - | +| update | Function to update the current use hook front-end state, more useful in react | (newFrontStates: [FrontRequestState](#frontrequeststate)) => void; | - | +| controlLoading | Customize the loading state of the control. The call will no longer trigger changes in the loading state. When the incoming control is false, the control will be cancelled. | (control?: boolean) => void | - | + +#### AlovaGuardNext + +```typescript +type AlovaGuardNext = (guardNextConfig?: { + force?: boolean | (...args: any[]) => boolean; + method?: Method; +}): Promise; +``` + +#### FrontRequestState + +The following attribute values will automatically infer the responsive data type of the corresponding UI framework based on `statesHook`, which is the `Ref` type in vue3, the normal value in react, and the `Writable` type in svelte + +| Name | Description | Type | Version | +| ----------- | ----------------------------- | ------------------ | ------- | +| loading | request loading status | boolean | - | +| data | response data | any | - | +| error | Request error information | Error \| undefined | - | +| downloading | Download progress information | Object | - | +| uploading | Upload progress information | Object | - | + +#### AlovaSuccessEvent + +| Name | Description | Type | Version | +| --------- | ---------------------------------------------------------------------------------------- | ------- | ------- | +| method | The method object of the current request | Method | - | +| sendArgs | Parameters of the response processing callback, which are passed in by send of use hooks | any[] | - | +| data | response data | any | - | +| fromCache | Whether the response data comes from cache | boolean | - | + +#### AlovaErrorEvent + +| Name | Description | Type | Version | +| -------- | ---------------------------------------------------------------------------------------- | ------ | ------- | +| method | The method object of the current request | Method | - | +| sendArgs | Parameters of the response processing callback, which are passed in by send of use hooks | any[] | - | +| error | Response error instance | Error | - | + +#### AlovaCompleteEvent + +| Name | Description | Type | Version | +| --------- | ---------------------------------------------------------------------------------------- | -------------------- | ------- | +| method | The method object of the current request | Method | - | +| sendArgs | Parameters of the response processing callback, which are passed in by send of use hooks | any[] | - | +| status | Response status, success when successful, error when failure | 'success' \| 'error' | - | +| data | response data, with value when successful | any | - | +| fromCache | Whether the response data comes from the cache, a value if successful | boolean | - | +| error | Response error instance, with value in case of failure | Error | - | + +### return value + +`UseHookReturnType` contains response data and request-related states, operation functions and event binding functions. They will automatically infer the responsive data type of the corresponding UI framework based on `statesHook`, which is the `Ref` type in vue3 and the `Ref` type in react. It is a normal value and is of `Writable` type in svelte. + +#### Responsive data + +| Name | Description | Type | Version | +| ----------- | ----------------------------- | ------------------ | ------- | +| loading | request loading status | boolean | - | +| data | response data | any | - | +| error | Request error information | Error \| undefined | - | +| downloading | Download progress information | Object | - | +| uploading | Upload progress information | Object | - | + +#### Operation function + +| name | description | function parameters | return value | version | +| ------ | ----------------------------------------------------------------------------- | ------------------------------------------------------- | ------------ | ------- | +| send | Send request function | ...args: any[] | - | - | +| abort | abort function | - | Promise | - | +| update | Function to update the current use hook front-end state, more useful in react | newFrontStates: [FrontRequestState](#frontrequeststate) | - | + +#### event + +| name | description | callback parameters | version | +| ---------- | -------------------------------- | ------------------------------------------------ | ------- | +| onSuccess | Request success event binding | event: [AlovaSuccessEvent](#alovasuccessevent) | - | +| onError | Request error event binding | event: [AlovaErrorEvent](#alovaerrorevent) | - | +| onComplete | Request completion event binding | event: [AlovaCompleteEvent](#alovacompleteevent) | - | + +## useWatcher + +Monitor the status and initiate a request after the status changes. In some scenarios that require re-requesting as the data changes, such as paging, data filtering, and fuzzy search. + +> Go to [State Change Request](/next/tutorial/client/strategy/use-watcher) for details. + +### type + +```typescript +function useWatcher( + handler: Method | (...args: any[]) => Method, + watchingStates: State[], + config?:WatcherHookConfig +): UseHookReturnType; +``` + +### Parameters + +1. `handler`: can be passed in two forms: method instance and function. When specified as a function, it can receive parameters passed in by `send` and require a method instance to be returned. +2. `config`: hook configuration parameters. + +| Name | Description | Type | Default | Version | +| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- | ---------- | ------- | +| immediate | Whether to initiate the request immediately | boolean | true | - | +| initialData | The initial data value. The data value is the initial value before the first response. If it is not set, it is `undefined` | any | - | - | +| force | Whether to force the request, it can be set to the function to dynamically return a boolean value | boolean \| (...args: any[]) => boolean \| false | +| managedStates | Additional managed states, which can be updated via updateState | Record\ | - | +| debounce | Request debounce time (milliseconds), when passing in the array, you can set the debounce time individually in the order of watchingStates | number \| number[] | - | +| middleware | Middleware function, [Understanding alova middleware](/next/tutorial/client/in-depth/middleware) | (context: [AlovaFrontMiddlewareContext](#alovafrontmiddlewarecontext), next: [AlovaGuardNext](#alovaguardnext)) => Promise\ | - | - | +| sendable | Whether to send a request when the monitored state changes | (methodInstance: AlovaEvent) => boolean | () => true | - | +| abortLast | Whether to abort the last unresponsive request | boolean | true | - | + +### return value + +`UseHookReturnType` contains response data and request-related states, operation functions and event binding functions. They will automatically infer the responsive data type of the corresponding UI framework based on statesHook. It is Ref type in vue3 and ordinary value in react. In svelte it is Writable type. + +#### Responsive data + +| Name | Description | Type | Version | +| ----------- | ----------------------------- | ------------------ | ------- | +| loading | request loading status | boolean | - | +| data | response data | any | - | +| error | Request error information | Error \| undefined | - | +| downloading | Download progress information | Object | - | +| uploading | Upload progress information | Object | - | + +#### Operation function + +| name | description | function parameters | return value | version | +| ------ | ----------------------------------------------------------------------------- | ------------------------------------------------------- | ------------ | ------- | +| send | Send request function | ...args: any[] | Promise | - | +| abort | abort function | - | - | - | +| update | Function to update the current use hook front-end state, more useful in react | newFrontStates: [FrontRequestState](#frontrequeststate) | - | + +#### event + +| name | description | callback parameters | version | +| ---------- | -------------------------------- | ------------------------------------------------ | ------- | +| onSuccess | Request success event binding | event: [AlovaSuccessEvent](#alovasuccessevent) | - | +| onError | Request error event binding | event: [AlovaErrorEvent](#alovaerrorevent) | - | +| onComplete | Request completion event binding | event: [AlovaCompleteEvent](#alovacompleteevent) | - | + +## useFetcher + +It is used to pull data through `useFetcher`, which is useful when preloading data and updating status across modules. + +> Go to [Data Fetching](/next/tutorial/client/strategy/use-fetcher) to view details. + +### type + +```typescript +function useFetcher(config?: FetcherHookConfig): UseFetchHookReturnType; +``` + +### Parameters + +1. `config`: hook configuration parameters. + +| Name | Description | Type | Default | Version | +| ---------- | ------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ | ------- | +| force | Whether to force the request, it can be set to the function to dynamically return a boolean value | boolean | (...args: any[]) => boolean \| false | - | +| middleware | Middleware function, [Understand alova middleware](/next/tutorial/client/in-depth/middleware) | (context: [AlovaFetcherMiddlewareContext](#alovafetchermiddlewarecontext), next: [AlovaGuardNext](#alovaguardnext)) => Promise\ | - | - | + +#### AlovaFetcherMiddlewareContext + +| Name | Description | Type | Version | +| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | +| method | The method object of the current request | Method | - | +| cachedResponse | Hit cached data | any | - | +| config | Current use hook configuration | Record\ | - | +| fetchArgs | Parameters of the response processing callback, which are passed in by fetch of useFetcher | any[] | - | +| fetchStates | use hook preload state collection, such as fetching, error, etc. | [FetchRequestState](#fetchrequeststate) | - | +| fetch | Data preloading function | (method: Method, ...args: any[]) => Promise | - | +| abort | abort function | () => void | - | +| decorateSuccess | Decorate success callback function | (decorator: (
handler: (event: [AlovaSuccessEvent](#alovasuccessevent)) => void,
event: [AlovaSuccessEvent](#alovasuccessevent),
index: number,
length: number
) => void) => void | - | +| decorateError | Decoration failure callback function | (decorator: (
handler: (event: [AlovaErrorEvent](#alovaerrorevent)) => void,
event: [AlovaErrorEvent](#alovaerrorevent),
index: number,
length: number
) => void) => void | - | +| decorateComplete | Decoration completion callback function | (decorator: (
handler: (event: [AlovaCompleteEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovacompleteevent),
index: number,
length: number
) => void) => void | - | +| update | Function to update the current use hook preloading state, more useful in react | (newFrontStates: [FetchRequestState](#fetchrequeststate)) => void; | - | +| controlFetching | After calling, the state of fetching will be customized and the change of fetching state will no longer be triggered internally. When the incoming control is false, the control will be canceled | (control?: boolean) => void | - | + +#### FetchRequestState + +The following attribute values will automatically infer the responsive data type of the corresponding UI framework based on `statesHook`, which is the `Ref` type in vue3, the normal value in react, and the `Writable` type in svelte + +| Name | Description | Type | Version | +| ----------- | ----------------------------- | ------------------ | ------- | +| fetching | preloading request status | boolean | - | +| error | Request error information | Error \| undefined | - | +| downloading | Download progress information | Object | - | +| uploading | Upload progress information | Object | - | + +### return value + +`UseFetchHookReturnType` contains request-related states, operation functions and event binding functions. They will automatically infer the responsive data type of the corresponding UI framework based on statesHook. It is Ref type in vue3, ordinary value in react, and ordinary value in svelte. For Writable type. + +#### Responsive data + +| Name | Description | Type | Version | +| ----------- | ----------------------------- | ------------------ | ------- | +| fetching | preloading request status | boolean | - | +| error | Request error information | Error \| undefined | - | +| downloading | Download progress information | Object | - | +| uploading | Upload progress information | Object | - | + +#### Operation function + +| name | description | function parameters | return value | version | +| ------ | ----------------------------------------------------------------------------- | ---------------------------------------------------------- | ------------ | ------- | +| fetch | Data preloading function | 1. method: preloaded Method instance
2. ...args: any[] | Promise | - | +| abort | abort function | - | - | - | +| update | Function to update the current use hook front-end state, more useful in react | newFrontStates: [FrontRequestState](#frontrequeststate) | - | + +#### event + +| name | description | callback parameters | version | +| ---------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------- | ------- | +| onSuccess | Request success event binding | event: [AlovaSuccessEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovasuccessevent) | - | +| onError | Request error event binding | event: [AlovaErrorEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovaerrorevent) | - | +| onComplete | Request completion event binding | event: [AlovaCompleteEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovacompleteevent) | - | diff --git a/docs/api/04-cache.md b/docs/api/04-cache.md index 86414f8e0..9f2747649 100644 --- a/docs/api/04-cache.md +++ b/docs/api/04-cache.md @@ -1,13 +1,12 @@ --- title: Cache operation -sidebar_position: 40 --- ## invalidateCache() Active cache invalidation. -> Go to [Manually invalidate cache](/tutorial/cache/manually-invalidate) for details. +> Go to [Manually invalidate cache](/next/tutorial/cache/manually-invalidate) for details. - **type** @@ -25,7 +24,7 @@ function invalidateCache(matcher?: Method | Method[] | MethodFilter): void; - **Parameters** -1. `matcher`: Cache invalid matcher, the value is a method instance or array, or it can be set to [method instance matcher](/tutorial/advanced/method-matcher). +1. `matcher`: Cache invalid matcher, the value is a method instance or array, or it can be set to [method instance matcher](/next/tutorial/client/in-depth/method-matcher). - **return** @@ -48,7 +47,7 @@ invalidateCache({ Set up response caching. -> Go to [Cache Update and Query](/tutorial/cache/set-and-query) for details. +> Go to [Cache Update and Query](/next/tutorial/cache/set-and-query) for details. - **type** @@ -69,7 +68,7 @@ function setCache( - **Parameters** -1. `matcher`: The value is method instance, method name string, method name regular expression. It can also be set to [method instance matcher](/tutorial/advanced/method-matcher), which will match all matching The method instance of the condition sets the cached data. +1. `matcher`: The value is method instance, method name string, method name regular expression. It can also be set to [method instance matcher](/next/tutorial/client/in-depth/method-matcher), which will match all matching The method instance of the condition sets the cached data. 2. `dataOrUpdater`: Cache data or update function. If it is a function, it needs to return new cached data. If it returns `undefined` or does not return, the update will be cancelled. - **return** @@ -96,7 +95,7 @@ setCache( Query cache. -> Go to [Cache Update and Query](/tutorial/cache/set-and-query) for details. +> Go to [Cache Update and Query](/next/tutorial/cache/set-and-query) for details. - **type** @@ -114,7 +113,7 @@ function queryCache(matcher?: Method | MethodFilter): R | undefined; - **Parameters** -1. `matcher`: The value is method instance, method name string, method name regular expression. It can also be set to [method instance matcher](/tutorial/advanced/method-matcher), which will meet the conditions. The first method instance queries cached data. +1. `matcher`: The value is method instance, method name string, method name regular expression. It can also be set to [method instance matcher](/next/tutorial/client/in-depth/method-matcher), which will meet the conditions. The first method instance queries cached data. - **return** diff --git a/docs/api/05-states.md b/docs/api/05-states.md index a32f22de5..ac749c754 100644 --- a/docs/api/05-states.md +++ b/docs/api/05-states.md @@ -1,6 +1,5 @@ --- title: Response states operation -sidebar_position: 50 --- ## updateState @@ -14,11 +13,11 @@ By default, `updateState` will look for the response state created by alova's us This problem often occurs when updating status across pages, because what we tend to overlook when the page jumps is that the previous page has been destroyed by default. Therefore, if you want to update status across pages, here are two suggestions : 1. Persist the page components to ensure that the updated status can still be found; -2. Use [setCache](/tutorial/cache/set-and-query) instead of `updateState`. The principle is that when the request for the previous page exists in the cache, update its cache to ensure that when the page is created again, the The request can hit the updated cache to achieve the same effect. +2. Use [setCache](/next/tutorial/cache/set-and-query) instead of `updateState`. The principle is that when the request for the previous page exists in the cache, update its cache to ensure that when the page is created again, the The request can hit the updated cache to achieve the same effect. -> Go to [Cross-page/module update response states](/tutorial/advanced/update-across-components) for details. +> Go to [Cross-page/module update response states](/next/tutorial/client/in-depth/update-across-components) for details. -> To use updateState to manage extra states, please refer to [Extra State Management](/tutorial/advanced/manage-extra-states). +> To use updateState to manage extra states, please refer to [Extra State Management](/next/tutorial/client/in-depth/manage-extra-states). - **type** @@ -40,7 +39,7 @@ function updateState( - **Parameters** -- `matcher`: The value is method instance, method name string, method name regular expression, it can also be set to [method instance matcher](/tutorial/advanced/method-matcher), if it matches the qualified method, `handleUpdate` will be called. +- `matcher`: The value is method instance, method name string, method name regular expression, it can also be set to [method instance matcher](/next/tutorial/client/in-depth/method-matcher), if it matches the qualified method, `handleUpdate` will be called. - `handleUpdate`: update function or update function collection. If it is a function collection, the corresponding update function on the collection will be called and the return value will be used as the update result. - `options`: optional options. diff --git a/docs/api/06-global-config.md b/docs/api/06-global-config.md index d359d10c6..a3feb3094 100644 --- a/docs/api/06-global-config.md +++ b/docs/api/06-global-config.md @@ -1,36 +1,35 @@ ---- -title: Global configuration -sidebar_position: 60 ---- - -## globalConfig() - -Global configuration. - -- **type** - -```ts -function globalConfig(config: AlovaGlobalConfig): void; -``` - -- **Parameters** - -1. config: configuration - -| Parameter name | Type | Description | -| -------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------- | -| limitSnapshots | number | method snapshot number limit, set to 0 to disable saving snapshots. After closing, the method snapshot matcher will be unavailable | - -- **return** - -none - -- **Example** - -```ts -import { globalConfig } from 'alova'; - -globalConfig({ - limitSnapshots: 10 -}); -``` +--- +title: Global configuration +--- + +## globalConfig() + +Global configuration. + +- **type** + +```ts +function globalConfig(config: AlovaGlobalConfig): void; +``` + +- **Parameters** + +1. config: configuration + +| Parameter name | Type | Description | +| -------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------- | +| limitSnapshots | number | method snapshot number limit, set to 0 to disable saving snapshots. After closing, the method snapshot matcher will be unavailable | + +- **return** + +none + +- **Example** + +```ts +import { globalConfig } from 'alova'; + +globalConfig({ + limitSnapshots: 10 +}); +``` diff --git a/docs/contributing/01-overview.md b/docs/contributing/01-overview.md index 4a74bfb3d..2e0356d13 100644 --- a/docs/contributing/01-overview.md +++ b/docs/contributing/01-overview.md @@ -1,175 +1,174 @@ ---- -title: Contribution Guidelines -sidebar_position: 10 ---- - -# alova Contribution Guidelines - -Hello, I'm glad to meet you here. This is a detailed alova contribution guidelines, which includes detailed guidance on contributing to all aspects of alova. Please continue reading. - -## Preface - -In the past period of time, we have received active participation information from developers from all over the world in Github issues and Github Discussion, which means that alova is being loved by more and more developers. Even so, alova is still a rookie, and it still has a long way to go. - -**We expect to make alova a common project for everyone who is willing to participate, and we encourage everyone to become a contributor to the alova community with an open and inclusive attitude. Moreover, we believe that contributing to alova is not limited to code contributions, but participating in any activities that are beneficial to the development of alova is considered to contribute to alova.** Participating in contributions now can win you more effective contribution opportunities, and it allows you to contribute to the world Even if you are a junior developer, as long as the idea is in line with [alova's mission and design principles](#alova-mission-and-design-principles), please generously participate! - -> There is a [Code of conduct](./code-of-conduct) here, please refer to it. - -## Contribution directory - -Here are 13 places where you can contribute, but not limited to these, you can choose the part you want to participate in, and link to the corresponding location for detailed reading: - -- [use alova in your project](#use-alova-in-your-project) -- [star alova](#star-alova) -- [report bug](#report-bug) -- [Propose new feature ideas](#propose-new-feature-ideas) -- [Pull Request](#pull-request) -- [Create an adapter or strategy library based on alova](#create-an-adapter-or-strategy-library-based-on-alova) -- [Participate in community/PR review](#participate-in-community-pr-review) -- [Publish and disseminate information about alova](#publish-and-disseminate-information-about-alova) -- [Share experience](#share-experience) -- [Collaboration](#collaboration) -- [Donation](#donation) -- [Correct or add docs](#correct-or-add-docs) -- [Translate docs](#Translate-docs) - -## alova mission and design principles - -### alova Mission - -alova's mission gives it a clear development direction, which clearly defines what alova should do. - -alova is a lightweight request strategy library, **Its mission is to enable developers to achieve more efficient Client-Server data interaction by less codes**. - -For developers, alova provides them with a simple API and out-of-the-box advanced request functions, as well as various simple and high-performance request strategy modules. For application users, they can enjoy the benefits of alova The smooth experience brought by high-performance data interaction, therefore, alova has the following features: - -1. The api design is similar to axios, allowing users to learn at a lower cost; -2. Deep binding of the UI framework, greatly improving the benefits of developers; -3. Out-of-the-box advanced functions to avoid repeated packaging, such as request sharing, request caching, etc., to reduce developers' repeated packaging; -4. The platform-independent coding way, and it can perfectly migrate in different platform; -5. High scalability design, which can encapsulate high-reusability and high-performance business-related request strategies; -6. Highly aggregated and low-coupling method design improves API code maintainability; - -### alova design principles - -The design principles points out how it should be designed, the following is the core design concept of alova. - -1. `Method` proxy design, high aggregation, platform-independent design, throughout the request, you should be able to access it in any request function, from another perspective, the information related to the request should also be placed in the method instance ; -2. Lightweight, try to keep the source code concise in coding, such as avoiding repeated code, merging variable declarations, prototype chain function encapsulation, no similar API, tree shaking, but long variable names are allowed, because it will be used when compiling will be replaced by a single letter; -3. Highly scalable design. First, the design of alova uses a large number of adapter patterns and hook functions. For example, adapters include `requestAdapter`, `storageAdapter`, etc., and hook functions include `beforeRequest`, `responded`, `transformData`, `localCache`, etc., and most of them have default behaviors, which are designed to be easy to use while retaining high scalability; second, global request parameters can be overwritten, such as `timeout`, `shareRequest`, etc., for These parameters can be set individually for special requests. -4. The api design is universal. First, it means that the function of this api has a high level of abstraction, rather than a specific business. Second, the api design is scalable to adapt to the needs of the api iteration - -> The api universal design is only applicable to the alova library. If you are conceiving a request strategy, you can design it according to the specific business. - -## Select the contribution point you are interested in - -### Use alova in your project - -We believe that your use of alova in the project is also a contributor to alova, which is also telling people that alova is a trustworthy open source project, please open [this issue](https://github.com/alovajs/alova/issues/165), which may give you the opportunity to display your project on the alova official website. - -### Star alova - -Although this may be considered insignificant, it represents your recognition of alova, and every star is very important to alova. star alova in the right top of [alova's Github repository](https://github.com/alovajs/alova), which is important to us. - -### Report bug - -Please move to [Github new issue](https://github.com/alovajs/alova/issues/new/choose) and select the corresponding template to submit. Detailed instructions will be displayed in the submitted issue. - -**PLEASE NOTE:** If you want to ask questions about alova, please go to [Github Discussion](https://github.com/alovajs/alova/discussions) to create a question. That would be closed immediately when ask a question in issues. - -### Propose new feature ideas - -In order for alova to achieve its value and goals, before submitting a new feature idea, please carefully read [alova mission and design principles](#alova-mission-and-design-principles), and ensure that your new idea is in line with alova's mission and design philosophy design concept. - -Then, please submit it in [🚀 New Feature Proposal](https://github.com/alovajs/alova/issues/new?assignees=&labels=feature-request&projects=&template=FEATURE_REQUEST_en.yml), the detailed description will be Displayed when submitting an issue. - -### Pull Request - -You can contribute the following 3 aspects of code through pull request. If you are a new partner who is interested in participating, all `good first issue` issues are listed in [Github contribution list](https://github.com/alovajs/alova/contribute), it is used to Tell new partners who are interested in contributing that this is a good start. - -#### Bug fix - -Issues marked as [`bug:confirmed`](https://github.com/alovajs/alova/labels/bug%3Aconfirmed) in Github issues are all confirmed bugs, you can choose freely. - -If you encounter a bug yourself, please also [report a bug](#report-bug) first to ensure that the bug is confirmed to avoid invalid pull requests. - -#### New feature development - -Issues marked as [`feature-request:confirmed`](https://github.com/alovajs/alova/labels/feature-request%3Aconfirmed) in Github issues are new features that have been confirmed, you You can choose freely. - -If you have an idea for adding a new feature, please also [submit an issue of a new feature idea](#propose-new-feature-ideas) to ensure that the idea is confirmed to avoid invalid pull requests. - -#### Project configuration - -If you are very good at project configuration and find deficiencies in the alova project, such as incomplete configuration, too old configuration version, insufficient automation (including project development automation and Github warehouse management automation), you can also press [New Feature development](#new-feature-development) process to contribute. - -:::warning IMPORTANT - -1. Please read the [Developing Guidelines](./developing-guidelines) carefully before developing, it can guide you step by step on how to contribute code. -2. When you identify an issue that needs to be resolved, please make sure that it has not been flagged by someone else's pull request, which means that it has been pre-occupied. - -::: - -### Create an adapter or strategy library based on alova - -alova provides high-extensibility features, and you can write your own js library based on it. - -#### Custom Adapter - -Customize various adapters to meet the operating requirements in different environments. The following directions are available for reference: - -1. Customize statesHook, which can be executed under different UI frameworks, such as `solid/qwik`, currently supports `react/vue/svelte`, please read [Custom statesHook](/tutorial/custom/custom-stateshook); -2. Customize the request adapter, so that alova can cooperate with more request schemes, such as `GraphQL/SSE`, etc.; -3. Customize the storage adapter to meet the storage requirements of different environments, such as `react-native`; -4. Any combination of the above, such as the official [uniapp adapter](https://github.com/alovajs/adapter-uniapp), which includes request adapters and storage adapters. - -#### Custom request strategy - -Request strategies can help developers write high-performance functions more efficiently. Although the official [@alova/scene](/tutorial/strategy) provides some common request strategies, it is not enough It is a good choice to customize your own reusable request strategy based on alova to meet the business scenarios related to various requests of developers, and you can also publish them on npm for everyone to use. - -:::tip Submit your project - -If you have written an alova-based js library, please submit your project in [this issue](https://github.com/alovajs/alova/issues/165), which will allow your project to be displayed on the alova official website Opportunity. - -::: - -### Participate in community/PR review - -If you are interested in technical communication, then it may be more suitable for you to participate in more community communication. You can participate in the discussion of bugs and new features in Github issues, or in [Github Discussion](https://github.com/alovajs/alova/discussions), [Discord](https://discord.gg/S47QGJgkVb) or [QQ channel](https://pd.qq.com/s/1cdjx0nnw) to answer questions for others, which allows you to communicate with people from all over the world, which is a very interesting thing. - -At the same time, you can also participate in PR review in [pull request](https://github.com/alovajs/alova/pulls), which is also a topic of communication. - -### Publish and disseminate information about alova - -You can publish or repost any information that is beneficial to the development of alova on any social platform, short video platform, or technology sharing platform, which will help increase the influence of alova. We will filter out relevant articles or videos and display them on the alova official website. Here are some good articles: - -- [It’s time to replace your axios](https://medium.com/@huzhen555/its-time-to-replace-your-axios-12c014833b04) -- [Alova.js 筆記-試用相較 axios 更輕量、更高集成的請求庫](https://uu9924079.medium.com/alova-js-%E7%AD%86%E8%A8%98-%E8%A9%A6%E7%94%A8%E7%9B%B8%E8%BC%83-axios-%E6%9B%B4%E8%BC%95%E9%87%8F-%E6%9B%B4%E9%AB%98%E9%9B%86%E6%88%90%E7%9A%84%E8%AB%8B%E6%B1%82%E5%BA%AB-546ec5424df9) - -### Share experience - -If you have alova experience worth sharing, or better practice cases, you can share it in [Github Discussion's Practices channel](https://github.com/alovajs/alova/discussions/categories/practices), better The sharing of will also be displayed in the official documentation. - -### Collaboration - -We welcome project collaboration with any organization or individual, which can help us expand alova's influence and accelerate the development of the project. If you have any suggestions or intentions for cooperation, please contact us via email **hujou555@gmail.com**. - -### Donation - -You can donate to the project through the following 3 ways. Please go to the donation page to view the donation privileges. - -1. [Github sponsors](https://github.com/sponsors/alovajs) -2. [OpenCollective](https://opencollective.com/alova) -3. [afdian](https://afdian.net/a/huzhen555) - -### Correct or add docs - -If you need to add new documentation content, or find errors in alova's documentation, such as wrong examples, wrong words, incorrect descriptions, or unmentioned content, you can [create a new document repository issue](https://github.com/alovajs/alovajs.github.io/issues/new), or [new document warehouse pull request](https://github.com/alovajs/alovajs.github.io/fork) directly modify the error, which should be the better option, and we welcome any suggestions or contributions to improve the documentation. - -### Translate Documentation - -If you are proficient in different languages, you are welcome to translate the alova documentation, which will help expand the use and audience of alova. - -## Become a core team member - -Check [HERE](./become-core-member) for details +--- +title: Contribution Guidelines +--- + +# alova Contribution Guidelines + +Hello, I'm glad to meet you here. This is a detailed alova contribution guidelines, which includes detailed guidance on contributing to all aspects of alova. Please continue reading. + +## Preface + +In the past period of time, we have received active participation information from developers from all over the world in Github issues and Github Discussion, which means that alova is being loved by more and more developers. Even so, alova is still a rookie, and it still has a long way to go. + +**We expect to make alova a common project for everyone who is willing to participate, and we encourage everyone to become a contributor to the alova community with an open and inclusive attitude. Moreover, we believe that contributing to alova is not limited to code contributions, but participating in any activities that are beneficial to the development of alova is considered to contribute to alova.** Participating in contributions now can win you more effective contribution opportunities, and it allows you to contribute to the world Even if you are a junior developer, as long as the idea is in line with [alova's mission and design principles](#alova-mission-and-design-principles), please generously participate! + +> There is a [Code of conduct](./code-of-conduct) here, please refer to it. + +## Contribution directory + +Here are 13 places where you can contribute, but not limited to these, you can choose the part you want to participate in, and link to the corresponding location for detailed reading: + +- [use alova in your project](#use-alova-in-your-project) +- [star alova](#star-alova) +- [report bug](#report-bug) +- [Propose new feature ideas](#propose-new-feature-ideas) +- [Pull Request](#pull-request) +- [Create an adapter or strategy library based on alova](#create-an-adapter-or-strategy-library-based-on-alova) +- [Participate in community/PR review](#participate-in-community-pr-review) +- [Publish and disseminate information about alova](#publish-and-disseminate-information-about-alova) +- [Share experience](#share-experience) +- [Collaboration](#collaboration) +- [Donation](#donation) +- [Correct or add docs](#correct-or-add-docs) +- [Translate docs](#Translate-docs) + +## alova mission and design principles + +### alova Mission + +alova's mission gives it a clear development direction, which clearly defines what alova should do. + +alova is a lightweight request strategy library, **Its mission is to enable developers to achieve more efficient Client-Server data interaction by less codes**. + +For developers, alova provides them with a simple API and out-of-the-box advanced request functions, as well as various simple and high-performance request strategy modules. For application users, they can enjoy the benefits of alova The smooth experience brought by high-performance data interaction, therefore, alova has the following features: + +1. The api design is similar to axios, allowing users to learn at a lower cost; +2. Deep binding of the UI framework, greatly improving the benefits of developers; +3. Out-of-the-box advanced functions to avoid repeated packaging, such as request sharing, request caching, etc., to reduce developers' repeated packaging; +4. The platform-independent coding way, and it can perfectly migrate in different platform; +5. High scalability design, which can encapsulate high-reusability and high-performance business-related request strategies; +6. Highly aggregated and low-coupling method design improves API code maintainability; + +### alova design principles + +The design principles points out how it should be designed, the following is the core design concept of alova. + +1. `Method` proxy design, high aggregation, platform-independent design, throughout the request, you should be able to access it in any request function, from another perspective, the information related to the request should also be placed in the method instance ; +2. Lightweight, try to keep the source code concise in coding, such as avoiding repeated code, merging variable declarations, prototype chain function encapsulation, no similar API, tree shaking, but long variable names are allowed, because it will be used when compiling will be replaced by a single letter; +3. Highly scalable design. First, the design of alova uses a large number of adapter patterns and hook functions. For example, adapters include `requestAdapter`, `storageAdapter`, etc., and hook functions include `beforeRequest`, `responded`, `transformData`, `localCache`, etc., and most of them have default behaviors, which are designed to be easy to use while retaining high scalability; second, global request parameters can be overwritten, such as `timeout`, `shareRequest`, etc., for These parameters can be set individually for special requests. +4. The api design is universal. First, it means that the function of this api has a high level of abstraction, rather than a specific business. Second, the api design is scalable to adapt to the needs of the api iteration + +> The api universal design is only applicable to the alova library. If you are conceiving a request strategy, you can design it according to the specific business. + +## Select the contribution point you are interested in + +### Use alova in your project + +We believe that your use of alova in the project is also a contributor to alova, which is also telling people that alova is a trustworthy open source project, please open [this issue](https://github.com/alovajs/alova/issues/165), which may give you the opportunity to display your project on the alova official website. + +### Star alova + +Although this may be considered insignificant, it represents your recognition of alova, and every star is very important to alova. star alova in the right top of [alova's Github repository](https://github.com/alovajs/alova), which is important to us. + +### Report bug + +Please move to [Github new issue](https://github.com/alovajs/alova/issues/new/choose) and select the corresponding template to submit. Detailed instructions will be displayed in the submitted issue. + +**PLEASE NOTE:** If you want to ask questions about alova, please go to [Github Discussion](https://github.com/alovajs/alova/discussions) to create a question. That would be closed immediately when ask a question in issues. + +### Propose new feature ideas + +In order for alova to achieve its value and goals, before submitting a new feature idea, please carefully read [alova mission and design principles](#alova-mission-and-design-principles), and ensure that your new idea is in line with alova's mission and design philosophy design concept. + +Then, please submit it in [🚀 New Feature Proposal](https://github.com/alovajs/alova/issues/new?assignees=&labels=feature-request&projects=&template=FEATURE_REQUEST_en.yml), the detailed description will be Displayed when submitting an issue. + +### Pull Request + +You can contribute the following 3 aspects of code through pull request. If you are a new partner who is interested in participating, all `good first issue` issues are listed in [Github contribution list](https://github.com/alovajs/alova/contribute), it is used to Tell new partners who are interested in contributing that this is a good start. + +#### Bug fix + +Issues marked as [`bug:confirmed`](https://github.com/alovajs/alova/labels/bug%3Aconfirmed) in Github issues are all confirmed bugs, you can choose freely. + +If you encounter a bug yourself, please also [report a bug](#report-bug) first to ensure that the bug is confirmed to avoid invalid pull requests. + +#### New feature development + +Issues marked as [`feature-request:confirmed`](https://github.com/alovajs/alova/labels/feature-request%3Aconfirmed) in Github issues are new features that have been confirmed, you You can choose freely. + +If you have an idea for adding a new feature, please also [submit an issue of a new feature idea](#propose-new-feature-ideas) to ensure that the idea is confirmed to avoid invalid pull requests. + +#### Project configuration + +If you are very good at project configuration and find deficiencies in the alova project, such as incomplete configuration, too old configuration version, insufficient automation (including project development automation and Github warehouse management automation), you can also press [New Feature development](#new-feature-development) process to contribute. + +:::warning IMPORTANT + +1. Please read the [Developing Guidelines](./developing-guidelines) carefully before developing, it can guide you step by step on how to contribute code. +2. When you identify an issue that needs to be resolved, please make sure that it has not been flagged by someone else's pull request, which means that it has been pre-occupied. + +::: + +### Create an adapter or strategy library based on alova + +alova provides high-extensibility features, and you can write your own js library based on it. + +#### Custom Adapter + +Customize various adapters to meet the operating requirements in different environments. The following directions are available for reference: + +1. Customize statesHook, which can be executed under different UI frameworks, such as `solid/qwik`, currently supports `react/vue/svelte`, please read [Custom statesHook](/next/tutorial/advanced/custom/stateshook); +2. Customize the request adapter, so that alova can cooperate with more request schemes, such as `GraphQL/SSE`, etc.; +3. Customize the storage adapter to meet the storage requirements of different environments, such as `react-native`; +4. Any combination of the above, such as the official [uniapp adapter](https://github.com/alovajs/adapter-uniapp), which includes request adapters and storage adapters. + +#### Custom request strategy + +Request strategies can help developers write high-performance functions more efficiently. Although the official [@alova/scene](/next/tutorial/client/strategy) provides some common request strategies, it is not enough It is a good choice to customize your own reusable request strategy based on alova to meet the business scenarios related to various requests of developers, and you can also publish them on npm for everyone to use. + +:::tip Submit your project + +If you have written an alova-based js library, please submit your project in [this issue](https://github.com/alovajs/alova/issues/165), which will allow your project to be displayed on the alova official website Opportunity. + +::: + +### Participate in community/PR review + +If you are interested in technical communication, then it may be more suitable for you to participate in more community communication. You can participate in the discussion of bugs and new features in Github issues, or in [Github Discussion](https://github.com/alovajs/alova/discussions), [Discord](https://discord.gg/S47QGJgkVb) or [QQ channel](https://pd.qq.com/s/1cdjx0nnw) to answer questions for others, which allows you to communicate with people from all over the world, which is a very interesting thing. + +At the same time, you can also participate in PR review in [pull request](https://github.com/alovajs/alova/pulls), which is also a topic of communication. + +### Publish and disseminate information about alova + +You can publish or repost any information that is beneficial to the development of alova on any social platform, short video platform, or technology sharing platform, which will help increase the influence of alova. We will filter out relevant articles or videos and display them on the alova official website. Here are some good articles: + +- [It’s time to replace your axios](https://medium.com/@huzhen555/its-time-to-replace-your-axios-12c014833b04) +- [Alova.js 筆記-試用相較 axios 更輕量、更高集成的請求庫](https://uu9924079.medium.com/alova-js-%E7%AD%86%E8%A8%98-%E8%A9%A6%E7%94%A8%E7%9B%B8%E8%BC%83-axios-%E6%9B%B4%E8%BC%95%E9%87%8F-%E6%9B%B4%E9%AB%98%E9%9B%86%E6%88%90%E7%9A%84%E8%AB%8B%E6%B1%82%E5%BA%AB-546ec5424df9) + +### Share experience + +If you have alova experience worth sharing, or better practice cases, you can share it in [Github Discussion's Practices channel](https://github.com/alovajs/alova/discussions/categories/practices), better The sharing of will also be displayed in the official documentation. + +### Collaboration + +We welcome project collaboration with any organization or individual, which can help us expand alova's influence and accelerate the development of the project. If you have any suggestions or intentions for cooperation, please contact us via email **hujou555@gmail.com**. + +### Donation + +You can donate to the project through the following 3 ways. Please go to the donation page to view the donation privileges. + +1. [Github sponsors](https://github.com/sponsors/alovajs) +2. [OpenCollective](https://opencollective.com/alova) +3. [afdian](https://afdian.net/a/huzhen555) + +### Correct or add docs + +If you need to add new documentation content, or find errors in alova's documentation, such as wrong examples, wrong words, incorrect descriptions, or unmentioned content, you can [create a new document repository issue](https://github.com/alovajs/alovajs.github.io/issues/new), or [new document warehouse pull request](https://github.com/alovajs/alovajs.github.io/fork) directly modify the error, which should be the better option, and we welcome any suggestions or contributions to improve the documentation. + +### Translate Documentation + +If you are proficient in different languages, you are welcome to translate the alova documentation, which will help expand the use and audience of alova. + +## Become a core team member + +Check [HERE](./become-core-member) for details diff --git a/docs/contributing/02-become-core-member.md b/docs/contributing/02-become-core-member.md index 501677561..0ab350025 100644 --- a/docs/contributing/02-become-core-member.md +++ b/docs/contributing/02-become-core-member.md @@ -1,32 +1,31 @@ ---- -title: Become a core team member -sidebar_position: 20 ---- - -🤝🤝🤝 If you also agree with alovajs’ philosophy, then let us create the next generation request library together! - -## Responsibilities - -1. Responsible for the development and maintenance of vscode plug-in projects, request scenario modules and adapter projects, as well as the Hongmeng version of alovajs. -2. Write and optimize relevant development documents. -3. In-depth participation in the entire life cycle of project design and implementation, promotion and maintenance. -4. Participate in project development decision-making and commercialization exploration. - -## Revenue - -1. With the title of core member, you have the opportunity to enhance your reputation in the front-end field, thereby broadening your career development path. -2. More opportunities for public sharing and opportunities to contact more front-end personnel. -3. Broaden project thinking and technical experience through in-depth participation in project design, implementation and operation. -4. Decision-making power on projects. -5. Profit sharing. - -## Require - -1. Agree with alova’s design philosophy and development direction, and be willing to work hard to move forward together. -2. Passion for technology, sense of responsibility, and teamwork. -3. Technical capabilities only need to cover existing needs. -4. Have some spare time. - -## How to join - -If you are interested, please send an email to [hujou555@gmail.com](mailto:hujou555@gmail.com), and be sure to add `alova core member application` at the beginning of email. +--- +title: Become a core team member +--- + +🤝🤝🤝 If you also agree with alovajs’ philosophy, then let us create the next generation request library together! + +## Responsibilities + +1. Responsible for the development and maintenance of vscode plug-in projects, request scenario modules and adapter projects, as well as the Hongmeng version of alovajs. +2. Write and optimize relevant development documents. +3. In-depth participation in the entire life cycle of project design and implementation, promotion and maintenance. +4. Participate in project development decision-making and commercialization exploration. + +## Revenue + +1. With the title of core member, you have the opportunity to enhance your reputation in the front-end field, thereby broadening your career development path. +2. More opportunities for public sharing and opportunities to contact more front-end personnel. +3. Broaden project thinking and technical experience through in-depth participation in project design, implementation and operation. +4. Decision-making power on projects. +5. Profit sharing. + +## Require + +1. Agree with alova’s design philosophy and development direction, and be willing to work hard to move forward together. +2. Passion for technology, sense of responsibility, and teamwork. +3. Technical capabilities only need to cover existing needs. +4. Have some spare time. + +## How to join + +If you are interested, please send an email to [hujou555@gmail.com](mailto:hujou555@gmail.com), and be sure to add `alova core member application` at the beginning of email. diff --git a/docs/contributing/03-developing-guidelines.md b/docs/contributing/03-developing-guidelines.md index 357669268..b13c40096 100644 --- a/docs/contributing/03-developing-guidelines.md +++ b/docs/contributing/03-developing-guidelines.md @@ -1,138 +1,137 @@ ---- -title: Developing Guidelines -sidebar_position: 30 ---- - -:::info version required - -Node.js 16+, npm 8+ - -::: - -## 1. Fork repository - -[Open alova fork page](https://github.com/alovajs/alova/fork), click "Create fork" and clone the forked repository to local. - -## 2. Clone to local - -Use the `git clone` command line, or the `Github Desktop` application to clone forked project. - -## 3. New pull request - -You can [create pull request through a forked repo](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork) after writing code. You can also commit code in arbitrary batches, without commiting a complete code. - -## 4. Code something in your computer - -### Install dependencies - -Install dependencies using `npm install`. - -### Install the recommended plugin(vscode) - -If you are using vscode, it is recommended that you install the following plugins: - -- eslint: check code quality. -- prettier: formatting code. -- jest: Automatically execute unit test cases, and execute individual collection or unit test cases. -- EditorConfig: Make sure the file format is consistent. - -### Project Structure - -``` -|-.github -| |-ISSUE_TEMPLATE -> github issues template -| |-workflows -> github action -|-.husky -> husky configuration -|-.vscode -> vscode configuration -|-config -> rollup package files -|-src -> source code -|-test -> unit test suits -| |-browser -> browser environment unit test suits -| |-server -> SSR unit test suits -| |-components -> Unit test components -| |-mockServer.ts -> mock apis (msw) -|-typings -> ts declaration -|- Other configuration files - -``` - -### Coding specifications - -#### Code format - -If you install the `prettier` plugin, it will automatically format codes every time you save files, so you don't have to worry about the format. - -#### Minimize code - -Lightweight is one of the alova's features, so it is necessary to minimize the amount of coding when coding. Here are a few coding specifications that need to be followed: - -1. Avoid the same code block, which can reduce the amount of code in the library, but two lines of code may not be worth encapsulating; -2. Use a variable declarator to aggregate variable declarations, for example: - -```javascript -// ❌ -const a = 1; -const b = 2; - -// ✅ -const a = 1, - b = 2; -``` - -3. Use constants to save js built-in values and prototype methods to reduce the amount of code in the compilation phase of `uglify`. built-in values and prototype methods that often used are defined in `src/utils/variables.ts`. - -```javascript -// ❌ -if (a === false) { - //... -} -arr.forEach(item => { - //... -}); - -// ✅ -import { falseValue, forEach } from '@/utils/variables'; -if (a === falseValue) { - //... -} -forEach(arr, item => { - //... -}); -``` - -## 5. Unit Testing Guidelines - -After finish code, it is necessary to add corresponding unit tests. - -The alova project uses **jest** as the unit test framework, and msw as the mock server. It is recommended to use the TDD mode. After modifying code every time, please pass the corresponding unit test. - -:::warning IMPORTANT - -When you're ready to commit your code, make sure all your unit tests are passed. When you're working on a pull request, you can have multiple small commits, and GitHub can automatically squash them before merging them. - -::: - -1. To add browser-related unit test cases, please add them to the corresponding test collection in `test/browser`, if there is no suitable test suits, you can create one by yourself; -2. Add SSR-related unit test cases, please add them to the corresponding test collection in `test/server`, if there is no suitable test suits, you can create one by yourself; - -### Run and debug a single unit test or suits - -It is recommended to use the **jest** plugin (one of the plugins recommended above) to test a single use case or a suit. You can right-click the specified unit test, select `Run Test` to run it, and select `Debug Test` to debug it with breakpoint. - -![image](https://github.com/alovajs/alova/assets/29848971/a94ba9db-c100-472f-b870-6bcecb031bea) - -### Run all unit tests - -1. Use the **jest** plugin to run: - -![image](https://github.com/alovajs/alova/assets/29848971/5af3ff15-16b7-4b28-9ae6-d0b5a236b181) - -2. Run the browser unit tests with command line `npm run test:browser`, run the SSR unit tests with `npm run test:node`, and run both at the same time with `npm run test`. - -## 6. Commit codes - -alova uses [semantic-release](https://semantic-release.gitbook.io) as an automatic release tool, which can automatically release new version packages after merging code into `main` , and generate `CHANGELOG`, but you need to ensure that the committed message format follows [commit information convention](https://www.conventionalcommits.org/en/v1.0.0/), it is recommended that use `npm run commit` to automatically generate a git message that conforms to the specification. - -## 7. Writing docs - -If you are adding a new feature, you can try to add the relevant documentation of the new feature. For details, please read [Correcting or add docs](/contributing/overview#correct-or-add-docs), otherwise please explain it in the pull request. +--- +title: Developing Guidelines +--- + +:::info version required + +Node.js 16+, npm 8+ + +::: + +## 1. Fork repository + +[Open alova fork page](https://github.com/alovajs/alova/fork), click "Create fork" and clone the forked repository to local. + +## 2. Clone to local + +Use the `git clone` command line, or the `Github Desktop` application to clone forked project. + +## 3. New pull request + +You can [create pull request through a forked repo](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork) after writing code. You can also commit code in arbitrary batches, without commiting a complete code. + +## 4. Code something in your computer + +### Install dependencies + +Install dependencies using `npm install`. + +### Install the recommended plugin(vscode) + +If you are using vscode, it is recommended that you install the following plugins: + +- eslint: check code quality. +- prettier: formatting code. +- jest: Automatically execute unit test cases, and execute individual collection or unit test cases. +- EditorConfig: Make sure the file format is consistent. + +### Project Structure + +``` +|-.github +| |-ISSUE_TEMPLATE -> github issues template +| |-workflows -> github action +|-.husky -> husky configuration +|-.vscode -> vscode configuration +|-config -> rollup package files +|-src -> source code +|-test -> unit test suits +| |-browser -> browser environment unit test suits +| |-server -> SSR unit test suits +| |-components -> Unit test components +| |-mockServer.ts -> mock apis (msw) +|-typings -> ts declaration +|- Other configuration files + +``` + +### Coding specifications + +#### Code format + +If you install the `prettier` plugin, it will automatically format codes every time you save files, so you don't have to worry about the format. + +#### Minimize code + +Lightweight is one of the alova's features, so it is necessary to minimize the amount of coding when coding. Here are a few coding specifications that need to be followed: + +1. Avoid the same code block, which can reduce the amount of code in the library, but two lines of code may not be worth encapsulating; +2. Use a variable declarator to aggregate variable declarations, for example: + +```javascript +// ❌ +const a = 1; +const b = 2; + +// ✅ +const a = 1, + b = 2; +``` + +3. Use constants to save js built-in values and prototype methods to reduce the amount of code in the compilation phase of `uglify`. built-in values and prototype methods that often used are defined in `src/utils/variables.ts`. + +```javascript +// ❌ +if (a === false) { + //... +} +arr.forEach(item => { + //... +}); + +// ✅ +import { falseValue, forEach } from '@/utils/variables'; +if (a === falseValue) { + //... +} +forEach(arr, item => { + //... +}); +``` + +## 5. Unit Testing Guidelines + +After finish code, it is necessary to add corresponding unit tests. + +The alova project uses **jest** as the unit test framework, and msw as the mock server. It is recommended to use the TDD mode. After modifying code every time, please pass the corresponding unit test. + +:::warning IMPORTANT + +When you're ready to commit your code, make sure all your unit tests are passed. When you're working on a pull request, you can have multiple small commits, and GitHub can automatically squash them before merging them. + +::: + +1. To add browser-related unit test cases, please add them to the corresponding test collection in `test/browser`, if there is no suitable test suits, you can create one by yourself; +2. Add SSR-related unit test cases, please add them to the corresponding test collection in `test/server`, if there is no suitable test suits, you can create one by yourself; + +### Run and debug a single unit test or suits + +It is recommended to use the **jest** plugin (one of the plugins recommended above) to test a single use case or a suit. You can right-click the specified unit test, select `Run Test` to run it, and select `Debug Test` to debug it with breakpoint. + +![image](https://github.com/alovajs/alova/assets/29848971/a94ba9db-c100-472f-b870-6bcecb031bea) + +### Run all unit tests + +1. Use the **jest** plugin to run: + +![image](https://github.com/alovajs/alova/assets/29848971/5af3ff15-16b7-4b28-9ae6-d0b5a236b181) + +2. Run the browser unit tests with command line `npm run test:browser`, run the SSR unit tests with `npm run test:node`, and run both at the same time with `npm run test`. + +## 6. Commit codes + +alova uses [semantic-release](https://semantic-release.gitbook.io) as an automatic release tool, which can automatically release new version packages after merging code into `main` , and generate `CHANGELOG`, but you need to ensure that the committed message format follows [commit information convention](https://www.conventionalcommits.org/en/v1.0.0/), it is recommended that use `npm run commit` to automatically generate a git message that conforms to the specification. + +## 7. Writing docs + +If you are adding a new feature, you can try to add the relevant documentation of the new feature. For details, please read [Correcting or add docs](/next/contributing/overview#correct-or-add-docs), otherwise please explain it in the pull request. diff --git a/docs/contributing/04-code-of-conduct.md b/docs/contributing/04-code-of-conduct.md index 580d03abe..833a9ab0c 100644 --- a/docs/contributing/04-code-of-conduct.md +++ b/docs/contributing/04-code-of-conduct.md @@ -1,137 +1,136 @@ ---- -title: Code of Conduct -sidebar_position: 40 ---- - -# Contributor Covenant Code of Conduct - -## Our Pledge - -We as members, contributors, and leaders pledge to make participation in our -community a harassment-free experience for everyone, regardless of age, body -size, visible or invisible disability, ethnicity, sex characteristics, gender -identity and expression, level of experience, education, socio-economic status, -nationality, personal appearance, race, caste, color, religion, or sexual -identity and orientation. - -We pledge to act and interact in ways that contribute to an open, welcoming, -diverse, inclusive, and healthy community. - -## Our Standards - -Examples of behavior that contributes to a positive environment for our -community include: - -- Demonstrating empathy and kindness toward other people -- Being respectful of differing opinions, viewpoints, and experiences -- Giving and gracefully accepting constructive feedback -- Accepting responsibility and apologizing to those affected by our mistakes, - and learning from the experience -- Focusing on what is best not just for us as individuals, but for the overall - community - -Examples of unacceptable behavior include: - -- The use of sexualized language or imagery, and sexual attention or advances of - any kind -- Trolling, insulting or derogatory comments, and personal or political attacks -- Public or private harassment -- Publishing others' private information, such as a physical or email address, - without their explicit permission -- Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Enforcement Responsibilities - -Community leaders are responsible for clarifying and enforcing our standards of -acceptable behavior and will take appropriate and fair corrective action in -response to any behavior that they deem inappropriate, threatening, offensive, -or harmful. - -Community leaders have the right and responsibility to remove, edit, or reject -comments, commits, code, wiki edits, issues, and other contributions that are -not aligned to this Code of Conduct, and will communicate reasons for moderation -decisions when appropriate. - -## Scope - -This Code of Conduct applies within all community spaces, and also applies when -an individual is officially representing the community in public spaces. -Examples of representing our community include using an official e-mail address, -posting via an official social media account, or acting as an appointed -representative at an online or offline event. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported to the community leaders responsible for enforcement at -[INSERT CONTACT METHOD]. -All complaints will be reviewed and investigated promptly and fairly. - -All community leaders are obligated to respect the privacy and security of the -reporter of any incident. - -## Enforcement Guidelines - -Community leaders will follow these Community Impact Guidelines in determining -the consequences for any action they deem in violation of this Code of Conduct: - -### 1. Correction - -**Community Impact**: Use of inappropriate language or other behavior deemed -unprofessional or unwelcome in the community. - -**Consequence**: A private, written warning from community leaders, providing -clarity around the nature of the violation and an explanation of why the -behavior was inappropriate. A public apology may be requested. - -### 2. Warning - -**Community Impact**: A violation through a single incident or series of -actions. - -**Consequence**: A warning with consequences for continued behavior. No -interaction with the people involved, including unsolicited interaction with -those enforcing the Code of Conduct, for a specified period of time. This -includes avoiding interactions in community spaces as well as external channels -like social media. Violating these terms may lead to a temporary or permanent -ban. - -### 3. Temporary Ban - -**Community Impact**: A serious violation of community standards, including -sustained inappropriate behavior. - -**Consequence**: A temporary ban from any sort of interaction or public -communication with the community for a specified period of time. No public or -private interaction with the people involved, including unsolicited interaction -with those enforcing the Code of Conduct, is allowed during this period. -Violating these terms may lead to a permanent ban. - -### 4. Permanent Ban - -**Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an -individual, or aggression toward or disparagement of classes of individuals. - -**Consequence**: A permanent ban from any sort of public interaction within the -community. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], -version 2.1, available at -[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. - -Community Impact Guidelines were inspired by -[Mozilla's code of conduct enforcement ladder][mozilla coc]. - -For answers to common questions about this code of conduct, see the FAQ at -[https://www.contributor-covenant.org/faq][faq]. Translations are available at -[https://www.contributor-covenant.org/translations][translations]. - -[homepage]: https://www.contributor-covenant.org -[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html -[mozilla coc]: https://github.com/mozilla/diversity -[faq]: https://www.contributor-covenant.org/faq -[translations]: https://www.contributor-covenant.org/translations +--- +title: Code of Conduct +--- + +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +- Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +- The use of sexualized language or imagery, and sexual attention or advances of + any kind +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email address, + without their explicit permission +- Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +[INSERT CONTACT METHOD]. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][mozilla coc]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][faq]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[mozilla coc]: https://github.com/mozilla/diversity +[faq]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations diff --git a/docs/resource/01-request-adapter/01-alova-mock.md b/docs/resource/01-request-adapter/01-alova-mock.md index 612823839..cb921f5b4 100644 --- a/docs/resource/01-request-adapter/01-alova-mock.md +++ b/docs/resource/01-request-adapter/01-alova-mock.md @@ -1,12 +1,11 @@ --- title: Mock data -sidebar_position: 10 --- import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -This mock plug-in is an alova request adapter. Different from the traditional Proxy form, you can control the scope of use of mock data. You can control the global scope, a group of interface scopes, and even the enabling and use of a certain interface. Disabled, which is very useful in our actual business scenarios. Each iteration will add or modify a set of interfaces. We hope that the previous functions will still follow the developed interfaces, and let the new or modified interfaces Taking the simulation data, at this time, each developer can group the interfaces involved in this iteration into a group, and turn them on or off. +This mock plug-in is an alova request adapter. Different from the traditional Proxy form, you can control the Scope of usage of mock data. You can control the global scope, a group of interface scopes, and even the enabling and use of a certain interface. Disabled, which is very useful in our actual business scenarios. Each iteration will add or modify a set of interfaces. We hope that the previous functions will still follow the developed interfaces, and let the new or modified interfaces Taking the simulation data, at this time, each developer can group the interfaces involved in this iteration into a group, and turn them on or off. ## Features @@ -101,7 +100,7 @@ export default defineMock( Create a mock request adapter when calling `createAlova`, and pass in the mock interface to complete. ```javascript -import GlobalFetch from 'alova/GlobalFetch'; +import adapterFetch from 'alova/fetch'; import { createAlovaMockAdapter } from '@alova/mock'; import mockGroup1 from './mockGroup1'; @@ -111,7 +110,7 @@ const mockAdapter = createAlovaMockAdapter([mockGroup1, /** ... */], { enable: true, // Non-mock request adapter, used to send requests when the mock interface is not matched - httpAdapter: GlobalFetch(), + httpAdapter: adapterFetch(), // mock interface response delay, in milliseconds delay: 1000, @@ -120,7 +119,7 @@ const mockAdapter = createAlovaMockAdapter([mockGroup1, /** ... */], { mockRequestLogger: true, // Simulation interface callback, data is the returned simulation data, you can use it to construct any object you want and return it to alova - // The following is the default callback function, which is suitable for requesting the adapter using GlobalFetch + // The following is the default callback function, which is suitable for requesting the adapter using adapterFetch // If you are using other request adapters, please customize the return data structure suitable for the adapter in the mock interface callback onMockResponse: data => new Response(JSON.stringify(data)) }); @@ -223,7 +222,7 @@ import Augustv1_1 from './August-v1.1'; import Keevenv1_1 from './kevin-v1.1'; const mockAdapter = createAlovaMockAdapter([Augustv1_1, kevinv1_1], { - httpAdapter: GlobalFetch(), + httpAdapter: adapterFetch(), delay: 1000 }); export const alovaInst = createAlova({ @@ -238,9 +237,9 @@ export const alovaInst = createAlova({ The mock data is generally only used in the development environment, and will be switched to the actual interface in the production environment, so this mock code becomes useless in the production environment. At this time, we can exclude this code by judging the environment variables. , you just need to do: ```javascript -const globalFetch = GlobalFetch(); +const alovaFetch = adapterFetch(); const mockAdapter = createAlovaMockAdapter([mockGroup1, /** ... */], { - httpAdapter: globalFetch, + httpAdapter: alovaFetch, delay: 1000, }); @@ -249,7 +248,7 @@ export const alovaInst = createAlova({ // highlight-start // In the production environment controlled by environment variables, the mock-related code will not be packaged in - requestAdapter: process.env.NODE_ENV === 'development' ? mockAdapter : globalFetch, + requestAdapter: process.env.NODE_ENV === 'development' ? mockAdapter : adapterFetch, // highlight-end statesHook: /** ... */ @@ -273,7 +272,7 @@ export default defineMock({ ## Convert mock data -**@alova/mock** By default, the response data is packaged as a Response instance, and the response header is packaged as a Headers instance by default, which is adapted for `GlobalFetch`, but if you use other request adapters, you need to mock the data Convert to the corresponding format. +**@alova/mock** By default, the response data is packaged as a Response instance, and the response header is packaged as a Headers instance by default, which is adapted for `adapterFetch`, but if you use other request adapters, you need to mock the data Convert to the corresponding format. ### Convert response data diff --git a/docs/resource/01-request-adapter/02-alova-adapter-xhr.md b/docs/resource/01-request-adapter/02-alova-adapter-xhr.md index 4959a1b1f..3a280593c 100644 --- a/docs/resource/01-request-adapter/02-alova-adapter-xhr.md +++ b/docs/resource/01-request-adapter/02-alova-adapter-xhr.md @@ -1,337 +1,336 @@ ---- -title: XMLHttpRequest Adapter -sidebar_position: 20 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -## Install - - - - -```bash -npm install @alova/adapter-xhr --save -``` - - - - -```bash -yarn add @alova/adapter-xhr -``` - - - - -## Instructions - -### create alova - -Use **xhrRequestAdapter** as request adapter for alova. - -```javascript -import { createAlova } from 'alova'; -import { xhrRequestAdapter } from '@alova/adapter-xhr'; - -const alovaInst = createAlova({ - //... - requestAdapter: xhrResponseAdapter() - //... -}); -``` - -### Request - -The XMLHttpRequest adapter provides basic configuration parameters, including `responseType`, `withCredentials`, `mimeType`, `auth`, as follows: - - - - -```html - -
Loading...
-
The request data is: {{ data }}
- - - -``` - -
- - -```jsx -const list = () => - alovaInst.Get('/list', { - /** - * Set the response data type - * Can be set to change the response type. Values are: "arraybuffer", "blob", "document", "json" and "text" - * defaults to "json" - */ - responseType: 'text', - - /** - * True when credentials are to be included in cross-origin requests. false when they are excluded from cross-origin requests and when cookies are ignored in their responses. Default is false - */ - withCredentials: true, - - /** - * Set the mimeType of the response data - */ - mimeType: 'text/plain; charset=x-user-defined', - - /** - * auth means use HTTP Basic authentication and provide credentials. - * This will set an `Authorization` header, overriding any existing - * Custom headers for `Authorization` set using `headers`. - * Note that only HTTP Basic authentication can be configured via this parameter. - * For Bearer tokens etc., use the `Authorization` custom header instead. - */ - auth: { - username: 'name1', - password: '123456' - } - }); - -const App = () => { - const { loading, data } = useRequest(list); - - return ( - { loading ?
Loading...
: null } -
The request data is: { JSON.stringify(data) }
- ) -}; -``` - -
- - -```html - - -{#if $loading} -
Loading...
-{/if} -
The request data is: { data }
-``` - -
-
- -### Upload - -Use `FormData` to upload files, and this `FormData` instance will be sent to the server through `xhr.send`. It will be set `Content-Type` automatically, you don't need to custom it with `multipart/form-data`. - -```javascript -const uploadFile = imageFile => { - const formData = new FormData(); - formData.append('file', imageFile); - return alovaInst.Post('/uploadImg', formData, { - // Start upload progress - enableUpload: true - }); -}; - -const { - loading, - data, - uploading, - send: sendUpload -} = useRequest(uploadFile, { - immediate: false -}); - -// Picture selection event callback -const handleImageChoose = ({ target }) => { - sendUpload(target.files[0]); -}; -``` - -### download - -Point the request url to the file address to download, you can also enable the download progress by setting `enableDownload` to true. - -```javascript -const downloadFile = () => - alovaInst.Get('/bigImage.jpg', { - // Start download progress - enableDownload: true, - responseType: 'blob' - }); - -const { loading, data, downloading, send, onSuccess } = useRequest(downloadFile, { - immediate: false -}); -onSuccess(({ data: imageBlob }) => { - // download image - const anchor = document.createElement('a'); - anchor.href = URL.createObjectURL(blob); - anchor.download = 'image.jpg'; - anchor.click(); - URL.revokeObjectURL(anchor.href); -}); -const handleImageDownload = () => { - send(); -}; -``` - -## Mock request adapter compatible - -When developing applications, we may still need to use simulated requests. Only by default, the response data of [Mock Request Adapter (@alova/mock)](/tutorial/request-adapter/alova-mock) is a `Response` instance, which is compatible with the `GlobalFetch` request adapter by default. When using the XMLHttpRequest adapter, we You need to adapt the response data of the mock request adapter to the XMLHttpRequest adapter. In this case, you need to use the `xhrMockResponse` exported in the **@alova/adapter-xhr** package as the response adapter. - -```javascript -import { defineMock, createAlovaMockAdapter } from '@alova/mock'; -import { xhrRequestAdapter, xhrMockResponse } from '@alova/adapter-xhr'; - -const mocks = defineMock({ - //... -}); - -// mock data request adapter -export default createAlovaMockAdapter([mocks], { - // After specifying the request adapter, requests that do not match the simulated interface will use this adapter to send requests - httpAdapter: xhrRequestAdapter(), - - // Use xhrMockResponse to adapt the simulated data to the XMLHttpRequest adapter - onMockResponse: xhrMockResponse -}); - -export const alovaInst = createAlova({ - //... - // Control whether to use the simulated request adapter through environment variables - requestAdapter: process.env.NODE_ENV === 'development' ? mockAdapter : xhrRequestAdapter() -}); -``` - -## Typescript - -The XMLHttpRequest request adapter provides complete type adaptation. - -### method configuration - -When creating a method instance, in addition to the common configuration items in the method, you can also use the configuration items in `AlovaXHRRequestConfig`. - -```typescript -/** - * xhr request configuration parameters - */ -interface AlovaXHRRequestConfig { - /** - * Set the response data type. - * - * Can be set to change the response type. Values are: "arraybuffer", "blob", "document", "json", and "text". - * Setting 1: If the current global object is not a Window object, the setting to "document" is ignored. - * Setup 2: Throw an "InvalidStateError" DOMException if the state is loading or complete. - * Setting 3: Throws an "InvalidAccessError" DOMException if the sync flag is set and the current global object is a Window object. - * @default "json" - */ - responseType?: XMLHttpRequestResponseType; - - /** - * True when credentials are to be included in cross-origin requests. false when they are excluded from cross-origin requests and when cookies are ignored in their responses. The default is false. - * An 'InvalidStateError' DOMException is thrown if the state is not sent or not opened, or if the send() flag is set. - * @default false - */ - withCredentials?: boolean; - - /** - * Set the mimeType of the response data - */ - mimeType?: string; - - /** - * `auth` indicates that HTTP Basic authentication should be used, and credentials are provided. - * This will set an `Authorization` header, overriding any existing - * Custom headers for `Authorization` set using `headers`. - * Note that only HTTP Basic authentication can be configured via this parameter. - * For Bearer tokens etc., use the `Authorization` custom header instead. - */ - auth?: { - username: string; - password: string; - }; -} -``` - -### Response data - -XMLHttpRequest adapter response data is as follows: - -```typescript -interface AlovaXHRResponseHeaders { - [x: string]: any; -} -interface AlovaXHRResponse { - status: number; - statusText: string; - data: T; - headers: AlovaXHRResponseHeaders; -} -``` +--- +title: XMLHttpRequest Adapter +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## Install + + + + +```bash +npm install @alova/adapter-xhr --save +``` + + + + +```bash +yarn add @alova/adapter-xhr +``` + + + + +## Instructions + +### create alova + +Use **xhrRequestAdapter** as request adapter for alova. + +```javascript +import { createAlova } from 'alova'; +import { xhrRequestAdapter } from '@alova/adapter-xhr'; + +const alovaInst = createAlova({ + //... + requestAdapter: xhrResponseAdapter() + //... +}); +``` + +### Request + +The XMLHttpRequest adapter provides basic configuration parameters, including `responseType`, `withCredentials`, `mimeType`, `auth`, as follows: + + + + +```html + +
Loading...
+
The request data is: {{ data }}
+ + + +``` + +
+ + +```jsx +const list = () => + alovaInst.Get('/list', { + /** + * Set the response data type + * Can be set to change the response type. Values are: "arraybuffer", "blob", "document", "json" and "text" + * defaults to "json" + */ + responseType: 'text', + + /** + * True when credentials are to be included in cross-origin requests. false when they are excluded from cross-origin requests and when cookies are ignored in their responses. Default is false + */ + withCredentials: true, + + /** + * Set the mimeType of the response data + */ + mimeType: 'text/plain; charset=x-user-defined', + + /** + * auth means use HTTP Basic authentication and provide credentials. + * This will set an `Authorization` header, overriding any existing + * Custom headers for `Authorization` set using `headers`. + * Note that only HTTP Basic authentication can be configured via this parameter. + * For Bearer tokens etc., use the `Authorization` custom header instead. + */ + auth: { + username: 'name1', + password: '123456' + } + }); + +const App = () => { + const { loading, data } = useRequest(list); + + return ( + { loading ?
Loading...
: null } +
The request data is: { JSON.stringify(data) }
+ ) +}; +``` + +
+ + +```html + + +{#if $loading} +
Loading...
+{/if} +
The request data is: { data }
+``` + +
+
+ +### Upload + +Use `FormData` to upload files, and this `FormData` instance will be sent to the server through `xhr.send`. It will be set `Content-Type` automatically, you don't need to custom it with `multipart/form-data`. + +```javascript +const uploadFile = imageFile => { + const formData = new FormData(); + formData.append('file', imageFile); + return alovaInst.Post('/uploadImg', formData, { + // Start upload progress + enableUpload: true + }); +}; + +const { + loading, + data, + uploading, + send: sendUpload +} = useRequest(uploadFile, { + immediate: false +}); + +// Picture selection event callback +const handleImageChoose = ({ target }) => { + sendUpload(target.files[0]); +}; +``` + +### download + +Point the request url to the file address to download, you can also enable the download progress by setting `enableDownload` to true. + +```javascript +const downloadFile = () => + alovaInst.Get('/bigImage.jpg', { + // Start download progress + enableDownload: true, + responseType: 'blob' + }); + +const { loading, data, downloading, send, onSuccess } = useRequest(downloadFile, { + immediate: false +}); +onSuccess(({ data: imageBlob }) => { + // download image + const anchor = document.createElement('a'); + anchor.href = URL.createObjectURL(blob); + anchor.download = 'image.jpg'; + anchor.click(); + URL.revokeObjectURL(anchor.href); +}); +const handleImageDownload = () => { + send(); +}; +``` + +## Mock request adapter compatible + +When developing applications, we may still need to use simulated requests. Only by default, the response data of [Mock Request Adapter (@alova/mock)](/next/resource/request-adapter/alova-mock) is a `Response` instance, which is compatible with the `alova/fetch` request adapter by default. When using the XMLHttpRequest adapter, we You need to adapt the response data of the mock request adapter to the XMLHttpRequest adapter. In this case, you need to use the `xhrMockResponse` exported in the **@alova/adapter-xhr** package as the response adapter. + +```javascript +import { defineMock, createAlovaMockAdapter } from '@alova/mock'; +import { xhrRequestAdapter, xhrMockResponse } from '@alova/adapter-xhr'; + +const mocks = defineMock({ + //... +}); + +// mock data request adapter +export default createAlovaMockAdapter([mocks], { + // After specifying the request adapter, requests that do not match the simulated interface will use this adapter to send requests + httpAdapter: xhrRequestAdapter(), + + // Use xhrMockResponse to adapt the simulated data to the XMLHttpRequest adapter + onMockResponse: xhrMockResponse +}); + +export const alovaInst = createAlova({ + //... + // Control whether to use the simulated request adapter through environment variables + requestAdapter: process.env.NODE_ENV === 'development' ? mockAdapter : xhrRequestAdapter() +}); +``` + +## Typescript + +The XMLHttpRequest request adapter provides complete type adaptation. + +### method configuration + +When creating a method instance, in addition to the common configuration items in the method, you can also use the configuration items in `AlovaXHRRequestConfig`. + +```typescript +/** + * xhr request configuration parameters + */ +interface AlovaXHRRequestConfig { + /** + * Set the response data type. + * + * Can be set to change the response type. Values are: "arraybuffer", "blob", "document", "json", and "text". + * Setting 1: If the current global object is not a Window object, the setting to "document" is ignored. + * Setup 2: Throw an "InvalidStateError" DOMException if the state is loading or complete. + * Setting 3: Throws an "InvalidAccessError" DOMException if the sync flag is set and the current global object is a Window object. + * @default "json" + */ + responseType?: XMLHttpRequestResponseType; + + /** + * True when credentials are to be included in cross-origin requests. false when they are excluded from cross-origin requests and when cookies are ignored in their responses. The default is false. + * An 'InvalidStateError' DOMException is thrown if the state is not sent or not opened, or if the send() flag is set. + * @default false + */ + withCredentials?: boolean; + + /** + * Set the mimeType of the response data + */ + mimeType?: string; + + /** + * `auth` indicates that HTTP Basic authentication should be used, and credentials are provided. + * This will set an `Authorization` header, overriding any existing + * Custom headers for `Authorization` set using `headers`. + * Note that only HTTP Basic authentication can be configured via this parameter. + * For Bearer tokens etc., use the `Authorization` custom header instead. + */ + auth?: { + username: string; + password: string; + }; +} +``` + +### Response data + +XMLHttpRequest adapter response data is as follows: + +```typescript +interface AlovaXHRResponseHeaders { + [x: string]: any; +} +interface AlovaXHRResponse { + status: number; + statusText: string; + data: T; + headers: AlovaXHRResponseHeaders; +} +``` diff --git a/docs/resource/01-request-adapter/03-alova-adapter-axios.md b/docs/resource/01-request-adapter/03-alova-adapter-axios.md index 93b936a8f..891430935 100644 --- a/docs/resource/01-request-adapter/03-alova-adapter-axios.md +++ b/docs/resource/01-request-adapter/03-alova-adapter-axios.md @@ -1,293 +1,292 @@ ---- -title: axios adapter -sidebar_position: 30 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -## Install - - - - -```bash -npm install @alova/adapter-axios --save -``` - - - - -```bash -yarn add @alova/adapter-axios -``` - - - - -## Instructions - -### create alova - -Use **axiosRequestAdapter** as request adapter for alova. - -```javascript -import { createAlova } from 'alova'; -import { axiosRequestAdapter } from '@alova/adapter-axios'; - -const alovaInst = createAlova({ - //... - requestAdapter: axiosRequestAdapter() - //... -}); -``` - -The adapter will use the default axios instance to make requests internally. If you set some global parameters for axios, you may need to pay attention to the following two points: - -1. Priority is given to using the `baseURL` and `timeout` parameters in the axios instance, so if you set these parameters on the axios instance, you do not need to set them in `createAlova`; -2. The `beforeRequest` hook of the alova instance will be triggered earlier than the `interceptor.request` of the axios instance, and the `responded` hook of the alova will be triggered later than the `interceptor.response` of the axios instance; - -> You can also [use custom axios instance](#use-custom-axios-instance) - -## usage - -### request - -The usage of request is exactly the same as that used in the web environment. Already fully compatible with **axios**, you can specify [all configuration items](https://axios-http.com/docs/req_config) supported by `axios` in _config_ of method instance creation. - - - - -```html - -
Loading...
-
The request data is: {{ data }}
- - - -``` - -
- - -```jsx -const list = () => - alovaInst.Get('/list', { - // The set parameters will be passed to axios - paramsSerializer: params => { - return Qs.stringify(params, {arrayFormat: 'brackets'}) - }, - }); - -const App = () => { - const { loading, data } = useRequest(list); - - return ( - { loading ?
Loading...
: null } -
The request data is: { JSON.stringify(data) }
- ) -}; -``` - -
- - -```html - - -{#if $loading} -
Loading...
-{/if} -
The request data is: { data }
-``` - -
-
- -### Upload - -Use `FormData` to upload files, and this `FormData` instance will be passed to axios, which is consistent with the usage of axios upload files. - -```javascript -const uploadFile = imageFile => { - const formData = new FormData(); - formData.append('file', imageFile); - return alovaInst.Post('/uploadImg', formData, { - // Start upload progress - enableUpload: true - }); -}; - -const { - loading, - data, - uploading, - send: sendUpload -} = useRequest(uploadFile, { - immediate: false -}); - -// Picture selection event callback -const handleImageChoose = ({ target }) => { - sendUpload(target.files[0]); -}; -``` - -### download - -Point the request url to the file address to download, you can also enable the download progress by setting `enableDownload` to true. - -```javascript -const downloadFile = () => - alovaInst.Get('/bigImage.jpg', { - // Start download progress - enableDownload: true, - responseType: 'blob' - }); - -const { loading, data, downloading, send, onSuccess } = useRequest(downloadFile, { - immediate: false -}); -onSuccess(({ data: imageBlob }) => { - // download image - const anchor = document.createElement('a'); - anchor.href = URL.createObjectURL(blob); - anchor.download = 'image.jpg'; - anchor.click(); - URL.revokeObjectURL(anchor.href); -}); -const handleImageDownload = () => { - send(); -}; -``` - -## Use custom axios instance - -By default, this adapter will use the default axios instance for requests, but in some cases you need to use a custom created axios instance. You can do this: - -```javascript -const customAxios = axios.create({ - // ... -}); - -const alovaInst = createAlova({ - // ... - // highlight-start - requestAdapter: axiosRequestAdapter({ - axios: customAxios - }) - // highlight-end -}); -``` - -## Mock request adapter compatible - -When developing applications, we may still need to use simulated requests. Only by default, the response data of [Mock Request Adapter (@alova/mock)](/tutorial/request-adapter/alova-mock) is a `Response` instance, which is compatible with the `GlobalFetch` request adapter by default. When using the axios adapter, we The response data of the mock request adapter needs to be compatible with **AxiosResponse**, and the error instance is **AxiosError**, so you need to use `axiosMockResponse` exported from the **@alova/adapter-axios** package as the response adapter . - -```javascript -import { defineMock, createAlovaMockAdapter } from '@alova/mock'; -import { axiosRequestAdapter, axiosMockResponse } from '@alova/adapter-axios'; - -const mocks = defineMock({ - //... -}); - -// mock data request adapter -export default createAlovaMockAdapter([mocks], { - // After specifying axios request adapter, requests that do not match the simulated interface will use this adapter to send requests - httpAdapter: axiosRequestAdapter(), - - // axiosMockResponse contains onMockResponse and onMockError - // Used to convert mock data to AxiosResponse and AxiosError compatible format - ...axiosMockResponse -}); - -export const alovaInst = createAlova({ - //... - // Control whether to use the simulated request adapter through environment variables - requestAdapter: process.env.NODE_ENV === 'development' ? mockAdapter : axiosRequestAdapter() -}); -``` - -##Typescript - -The axios request adapter provides complete type adaptation. The type of method configuration and response data will exactly match the type of axios. - -### method configuration - -When creating a method instance, in addition to the general configuration items in the method, you can also use the configuration items in `AxiosRequestConfig`, we have removed the items that conflict with the general configuration of the method instance in the type. - -```typescript -/** - * axios request configuration parameters - * Removed the attributes that conflicted with the method - */ -export type AlovaAxiosRequestConfig = Omit< - AxiosRequestConfig, - | 'url' - | 'method' - | 'baseURL' - | 'headers' - | 'params' - | 'data' - | 'timeout' - | 'cancelToken' - | 'signal' - | 'onUploadProgress' - | 'onDownloadProgress' ->; -``` - -### Response data - -The response data type of axios is `AxiosResponse`, when you use the axios adapter, you will also get the response data in the same format. In actual use, we usually need to process response data globally. A simple example is as follows: - -```typescript -const alovaInst = createAlova( - baseURL: 'https://api.alovajs.org', - requestAdapter: axiosRequestAdapter(), - responded(response) { - // response is automatically inferred as AxiosResponse type - return response.data; - } -}); -``` - -### Error - -When axios receives non-20x and 30x response status codes, it will throw an error. In order to contain more information, axios custom-designed the error instance into an `AxiosError` instance instead of a normal Error instance, so when encountering An error will be thrown when there is a server error or a network error, and you can catch it in the global error callback. - -```typescript -const alovaInst = createAlova( - baseURL: 'https://api.alovajs.org', - requestAdapter: axiosRequestAdapter(), - responded: { - onSuccess(response) { - // response is automatically inferred as AxiosResponse type - return response.data; - }, - onError(err: AxiosError) { - // err type is any by default, you can cast it to AxiosError - //... - } - } -}); -``` +--- +title: axios adapter +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## Install + + + + +```bash +npm install @alova/adapter-axios --save +``` + + + + +```bash +yarn add @alova/adapter-axios +``` + + + + +## Instructions + +### create alova + +Use **axiosRequestAdapter** as request adapter for alova. + +```javascript +import { createAlova } from 'alova'; +import { axiosRequestAdapter } from '@alova/adapter-axios'; + +const alovaInst = createAlova({ + //... + requestAdapter: axiosRequestAdapter() + //... +}); +``` + +The adapter will use the default axios instance to make requests internally. If you set some global parameters for axios, you may need to pay attention to the following two points: + +1. Priority is given to using the `baseURL` and `timeout` parameters in the axios instance, so if you set these parameters on the axios instance, you do not need to set them in `createAlova`; +2. The `beforeRequest` hook of the alova instance will be triggered earlier than the `interceptor.request` of the axios instance, and the `responded` hook of the alova will be triggered later than the `interceptor.response` of the axios instance; + +> You can also [use custom axios instance](#use-custom-axios-instance) + +## usage + +### request + +The usage of request is exactly the same as that used in the web environment. Already fully compatible with **axios**, you can specify [all configuration items](https://axios-http.com/docs/req_config) supported by `axios` in _config_ of method instance creation. + + + + +```html + +
Loading...
+
The request data is: {{ data }}
+ + + +``` + +
+ + +```jsx +const list = () => + alovaInst.Get('/list', { + // The set parameters will be passed to axios + paramsSerializer: params => { + return Qs.stringify(params, {arrayFormat: 'brackets'}) + }, + }); + +const App = () => { + const { loading, data } = useRequest(list); + + return ( + { loading ?
Loading...
: null } +
The request data is: { JSON.stringify(data) }
+ ) +}; +``` + +
+ + +```html + + +{#if $loading} +
Loading...
+{/if} +
The request data is: { data }
+``` + +
+
+ +### Upload + +Use `FormData` to upload files, and this `FormData` instance will be passed to axios, which is consistent with the usage of axios upload files. + +```javascript +const uploadFile = imageFile => { + const formData = new FormData(); + formData.append('file', imageFile); + return alovaInst.Post('/uploadImg', formData, { + // Start upload progress + enableUpload: true + }); +}; + +const { + loading, + data, + uploading, + send: sendUpload +} = useRequest(uploadFile, { + immediate: false +}); + +// Picture selection event callback +const handleImageChoose = ({ target }) => { + sendUpload(target.files[0]); +}; +``` + +### download + +Point the request url to the file address to download, you can also enable the download progress by setting `enableDownload` to true. + +```javascript +const downloadFile = () => + alovaInst.Get('/bigImage.jpg', { + // Start download progress + enableDownload: true, + responseType: 'blob' + }); + +const { loading, data, downloading, send, onSuccess } = useRequest(downloadFile, { + immediate: false +}); +onSuccess(({ data: imageBlob }) => { + // download image + const anchor = document.createElement('a'); + anchor.href = URL.createObjectURL(blob); + anchor.download = 'image.jpg'; + anchor.click(); + URL.revokeObjectURL(anchor.href); +}); +const handleImageDownload = () => { + send(); +}; +``` + +## Use custom axios instance + +By default, this adapter will use the default axios instance for requests, but in some cases you need to use a custom created axios instance. You can do this: + +```javascript +const customAxios = axios.create({ + // ... +}); + +const alovaInst = createAlova({ + // ... + // highlight-start + requestAdapter: axiosRequestAdapter({ + axios: customAxios + }) + // highlight-end +}); +``` + +## Mock request adapter compatible + +When developing applications, we may still need to use simulated requests. Only by default, the response data of [Mock Request Adapter (@alova/mock)](/next/resource/request-adapter/alova-mock) is a `Response` instance, which is compatible with the `alova/fetch` request adapter by default. When using the axios adapter, we The response data of the mock request adapter needs to be compatible with **AxiosResponse**, and the error instance is **AxiosError**, so you need to use `axiosMockResponse` exported from the **@alova/adapter-axios** package as the response adapter . + +```javascript +import { defineMock, createAlovaMockAdapter } from '@alova/mock'; +import { axiosRequestAdapter, axiosMockResponse } from '@alova/adapter-axios'; + +const mocks = defineMock({ + //... +}); + +// mock data request adapter +export default createAlovaMockAdapter([mocks], { + // After specifying axios request adapter, requests that do not match the simulated interface will use this adapter to send requests + httpAdapter: axiosRequestAdapter(), + + // axiosMockResponse contains onMockResponse and onMockError + // Used to convert mock data to AxiosResponse and AxiosError compatible format + ...axiosMockResponse +}); + +export const alovaInst = createAlova({ + //... + // Control whether to use the simulated request adapter through environment variables + requestAdapter: process.env.NODE_ENV === 'development' ? mockAdapter : axiosRequestAdapter() +}); +``` + +##Typescript + +The axios request adapter provides complete type adaptation. The type of method configuration and response data will exactly match the type of axios. + +### method configuration + +When creating a method instance, in addition to the general configuration items in the method, you can also use the configuration items in `AxiosRequestConfig`, we have removed the items that conflict with the general configuration of the method instance in the type. + +```typescript +/** + * axios request configuration parameters + * Removed the attributes that conflicted with the method + */ +export type AlovaAxiosRequestConfig = Omit< + AxiosRequestConfig, + | 'url' + | 'method' + | 'baseURL' + | 'headers' + | 'params' + | 'data' + | 'timeout' + | 'cancelToken' + | 'signal' + | 'onUploadProgress' + | 'onDownloadProgress' +>; +``` + +### Response data + +The response data type of axios is `AxiosResponse`, when you use the axios adapter, you will also get the response data in the same format. In actual use, we usually need to process response data globally. A simple example is as follows: + +```typescript +const alovaInst = createAlova( + baseURL: 'https://api.alovajs.org', + requestAdapter: axiosRequestAdapter(), + responded(response) { + // response is automatically inferred as AxiosResponse type + return response.data; + } +}); +``` + +### Error + +When axios receives non-20x and 30x response status codes, it will throw an error. In order to contain more information, axios custom-designed the error instance into an `AxiosError` instance instead of a normal Error instance, so when encountering An error will be thrown when there is a server error or a network error, and you can catch it in the global error callback. + +```typescript +const alovaInst = createAlova( + baseURL: 'https://api.alovajs.org', + requestAdapter: axiosRequestAdapter(), + responded: { + onSuccess(response) { + // response is automatically inferred as AxiosResponse type + return response.data; + }, + onError(err: AxiosError) { + // err type is any by default, you can cast it to AxiosError + //... + } + } +}); +``` diff --git a/docs/resource/01-request-adapter/04-alova-adapter-taro.md b/docs/resource/01-request-adapter/04-alova-adapter-taro.md index 20fec51a7..44edb2d0a 100644 --- a/docs/resource/01-request-adapter/04-alova-adapter-taro.md +++ b/docs/resource/01-request-adapter/04-alova-adapter-taro.md @@ -1,436 +1,443 @@ ---- -title: Taro Adapter -sidebar_position: 40 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -:::info Tips - -This plugin only supports the taro application of react 16.8+, vue3 version. - -::: - -## Install - - - - -```bash -npm install @alova/adapter-taro --save -``` - - - - -```bash -yarn add @alova/adapter-taro -``` - - - - -:::warning React-Native Application - -If you are develop a React-Native app with Taro, please ensure `metro >= 0.76.0` and enable `resolver.unstable_enablePackageExports` in the `metro.config.js`. - -[about unstable_enablePackageExports of metro](https://facebook.github.io/metro/docs/configuration/#unstable_enablepackageexports-experimental) - -::: - -:::warning Dependency precompilation issues - -[Dependency precompilation function](https://docs.taro.zone/blog/2022/05/19/Taro-3.5-beta#2-%E4%BE%9D%E8%B5%96%E9%A2%84%E7%BC%96%E8%AF%91) has been added in Taro v3.5 beta, and is enabled by default in development mode when you are using the `alova` library and `@alova/scene-react(vue)` may cause the error `` [alova]can not call useHooks until set the `statesHook` at alova instance. ``. This is caused by the prebundle feature repeatedly packaging two different `alova` packages. , turning off the prebundle function at this time can solve this problem. - -```js -// config/dev.ts -export default { - // ... - compiler: { - type: 'webpack5', - prebundle: { - // Close prebundle - enable: false - } - } -} satisfies UserConfigExport - -``` - -Thanks to [LBinin’s issue](https://github.com/alovajs/scene/issues/63). - -This problem has been committed to the Taro team [issue](https://github.com/NervJS/taro/issues/15728) and we look forward to solving this issue. - -::: - -## Usage - -### create alova - -Calling **AdapterTaro** will return _Request Adapter_, _Storage Adapter_, and _ReactHook_, so you no longer need to set these three items, and the usage is exactly the same. - - - - -```javascript -import { createAlova } from 'alova'; -import AdapterTaro from '@alova/adapter-taro'; - -const alovaInst = createAlova({ - baseURL: 'https://api.alovajs.org', - ...AdapterTaro() -}); -``` - - - - - -```javascript -import { createAlova } from 'alova'; -import AdapterTaroVue from '@alova/adapter-taro/vue'; - -const alovaInst = createAlova({ - baseURL: 'https://api.alovajs.org', - ...AdapterTaroVue() -}); -``` - - - - -### Request - -The usage method of the request is exactly the same as that used in the web environment. Already fully compatible with `Taro.request`, you can specify [all configuration items](https://taro-docs.jd.com/docs/apis/network/request/) supported by `Taro.request` in the _config_ of method instance creation - - - - -```jsx -const list = () => - alovaInst.Get('/list', { - // The set parameters will be passed to Taro.request - enableHttp2: true - }); - -const App = () => { - const { loading, data } = useRequest(list); - - return ( - { loading ? Loading... : null } - The requested data is: { JSON.stringify(data) } - ) -}; -``` - - - - -```html - - Loading... - The requested data is: {{ data }} - - - -``` - - - - -### Upload - -When `requestType: 'upload'` is set in the _config_ of the method instance, it means to upload the file, the request adapter will call `Taro.uploadFile`, and the uploaded file data is placed in the data of the method instance, you need to specify in the data `name` and `filePath`, these two parameters will be passed to `Taro.uploadFile`, at the same time, you can also specify other parameters in data, and the request adapter will pass them to `formData `in parameters. - -Similarly, it is fully compatible with `Taro.uploadFile`, you can specify [all configuration items](https://taro-docs.jd.com/docs/apis/network/upload/uploadFile) supported by `Taro.uploadFile`, if there are additional parameters to be set, please specify them in _config_ of the method instance. - - - - -```jsx -const uploadFile = (name, filePath, formData) => - alovaInst.Post( - '/uploadImg', - { - name, - filePath, - - // Additional data will be passed into formData of uni.uploadFile - ...formData - }, - { - // Set the request method to upload, and the adapter will call uni.uploadFile - requestType: 'upload', - - // Start upload progress - enableUpload: true - } - ); - -const App = () => { - const { loading, data, uploading, send } = useRequest(uploadFile, { - immediate: false - }); - - const handleImageChoose = () => { - Taro.chooseImage({ - success: chooseImageRes => { - const tempFilePaths = chooseImageRes.tempFilePaths; - send('fileName', tempFilePaths[0], { - extra1: 'a', - extra2: 'b' - }); - } - }); - }; - - return ( - { loading ? Uploading... : null } - Upload progress: { uploading.loaded }/{ uploading.total } - - {/* ... */} - ) -} -``` - - - - -```html - - Uploading... - Upload progress: {{ uploading.loaded }}/{{ uploading.total }} - - - - - -``` - - - - -### download - -When `requestType: 'download'` is set in the _config_ of the method instance, it means to download the file, and the request adapter will call `Taro.downloadFile`. - -Similarly, it is fully compatible with `Taro.downloadFile`, you can specify [all configuration items](https://taro-docs.jd.com/docs/apis/network/download/downloadFile) supported by `Taro.downloadFile`, if there are additional parameters to be set, please specify them in _config_ of the method instance. - - - - -```jsx -const downloadFile = filePath => - alovaInst.Get('/bigImage.jpg', { - // Set the request method to download, and the adapter will call uni.downloadFile - requestType: 'download', - filePath, - - // Start download progress - enableDownload: true - }); - -const App = () => { - const { loading, data, downloading, send } = useRequest(downloadFile, { - immediate: false - }); - const handleImageDownload = () => { - send('file_save_path'); - }; - - return ( - { loading ? Downloading... : null } - Download progress: { downloading.loaded }/{ downloading.total } - - {/* ... */} - ); -} -``` - - - - -```html - - Downloading... - Download progress: {{ downloading.loaded }}/{{ downloading.total }} - - - - - -``` - - - - -## Mock request adapter compatible - -When using Taro to develop applications, we may still need to use mock requests, but by default, the response data of [Mock Request Adapter (@alova/mock)](/tutorial/request-adapter/alova-mock) is a `Response` instance, That is, it is compatible with the `GlobalFetch` request adapter by default. When used in the Taro environment, we need to make the response data of the simulated request adapter compatible with the Taro adapter, so you need to use the **@alova/adapter-taro** package exported `taroMockResponse` as response adapter. - -```javascript -import { defineMock, createAlovaMockAdapter } from '@alova/mock'; -import AdapterTaro, { taroRequestAdapter, taroMockResponse } from '@alova/adapter-taro'; - -const mocks = defineMock({ - //... -}); - -// mock data request adapter -export default createAlovaMockAdapter([mocks], { - // After specifying the taro request adapter, requests that do not match the simulated interface will use this adapter to send requests - httpAdapter: taroRequestAdapter, - - // Simulate the response adapter, after specifying, the response data will be converted to a taro-compatible data format - onMockResponse: taroMockResponse -}); - -export const alovaInst = createAlova({ - baseURL: 'https://api.alovajs.org', - timeout: 5000, - ...AdapterTaro({ - // Control whether to use the simulated request adapter through environment variables - mockRequest: process.env.NODE_ENV === 'development' ? mockAdapter : undefined - }) - //... -}); -``` - -## Typescript - -The taro request adapter provides complete type adaptation, and the type of method configuration and response data will exactly match the type of taro. - -### method configuration - -When creating a method instance, in addition to the general configuration items in the method, you can also use the configuration items in `Taro.request`, `Taro.uploadFile` and `Taro.downloadFile`, we have removed the type and method Items that conflict with the common configuration of the instance. - -```typescript -/** - * Taro.request requests additional parameters - */ -export type TaroRequestConfig = Omit< - Taro.request.Option, - 'url' | 'data' | 'header' | 'method' | 'timeout' | 'success' | 'fail' | 'complete' ->; - -/** - * Taro.uploadFile additional parameter - */ -export type TaroUploadConfig = Omit< - Taro.uploadFile.Option, - 'url' | 'filePath' | 'name' | 'header' | 'formData' | 'timeout' | 'success' | 'fail' | 'complete' ->; - -/** - * Taro.downloadFile additional parameters - */ -export type TaroDownloadConfig = Omit< - Taro.downloadFile.Option, - 'url' | 'header' | 'timeout' | 'success' | 'fail' | 'complete' ->; - -/** - * Merged request configuration parameters - */ -export type TaroConfig = { - /** - * Request type, upload means upload, download means download, not filling means normal request - */ - requestType?: 'upload' | 'download'; -} & TaroRequestConfig & - TaroUploadConfig & - TaroDownloadConfig; -``` - -### Response data - -Because the taro request adapter is compatible with `Taro.request`, `Taro.uploadFile` and `Taro.downloadFile`, but their response value types are slightly different, so the response data type is as follows: - -```typescript -type TaroResponse = - // The response type of Taro.request - | Taro.request.SuccessCallbackResult - - // The response type of Taro.uploadFile - | Taro.uploadFile.SuccessCallbackResult - - // The response type of Taro.downloadFile - | Taro.downloadFile.FileSuccessCallbackResult; -``` - -In actual use, we usually need to process the response data globally. It is recommended to judge the returned data separately. A simple example is as follows: - -```typescript -const alovaInst = createAlova({ - baseURL: 'https://api.alovajs.org', - ...AdapterTaro(), - responded(response) { - const { statusCode, data } = response as Taro.request.SuccessCallbackResult; - if (statusCode >= 400) { - throw new Error('request error'); - } - return data || null; - } -}); -``` +--- +title: Taro Adapter +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info Tips + +This plugin only supports the taro application of react 16.8+, vue3 version. + +::: + +## Install + + + + +```bash +npm install @alova/adapter-taro --save +``` + + + + +```bash +yarn add @alova/adapter-taro +``` + + + + +:::warning React-Native Application + +If you are develop a React-Native app with Taro, please ensure `metro >= 0.76.0` and enable `resolver.unstable_enablePackageExports` in the `metro.config.js`. + +[about unstable_enablePackageExports of metro](https://facebook.github.io/metro/docs/configuration/#unstable_enablepackageexports-experimental) + +::: + +:::warning Dependency precompilation issues + +[Dependency precompilation function](https://docs.taro.zone/blog/2022/05/19/Taro-3.5-beta#2-%E4%BE%9D%E8%B5%96%E9%A2%84%E7%BC%96%E8%AF%91) has been added in Taro v3.5 beta, and is enabled by default in development mode when you are using the `alova` library and `@alova/scene-react(vue)` may cause the error `` [alova]can not call useHooks until set the `statesHook` at alova instance. ``. This is caused by the prebundle feature repeatedly packaging two different `alova` packages. , turning off the prebundle function at this time can solve this problem. + +```js +// config/dev.ts +export default { + // ... + compiler: { + type: 'webpack5', + prebundle: { + // Close prebundle + enable: false + } + } +} satisfies UserConfigExport + +``` + +Thanks to [LBinin’s issue](https://github.com/alovajs/scene/issues/63). + +This problem has been committed to the Taro team [issue](https://github.com/NervJS/taro/issues/15728) and we look forward to solving this issue. + +::: + +## Usage + +### create alova + +Calling **AdapterTaro** will return _Request Adapter_, _Storage Adapter_, and _ReactHook_, so you no longer need to set these three items, and the usage is exactly the same. + + + + +```javascript +import { createAlova } from 'alova'; +import AdapterTaro from '@alova/adapter-taro'; + +const alovaInst = createAlova({ + baseURL: 'https://api.alovajs.org', + ...AdapterTaro() +}); +``` + + + + + +```javascript +import { createAlova } from 'alova'; +import AdapterTaroVue from '@alova/adapter-taro/vue'; + +const alovaInst = createAlova({ + baseURL: 'https://api.alovajs.org', + ...AdapterTaroVue() +}); +``` + + + + +### Request + +The usage method of the request is exactly the same as that used in the web environment. Already fully compatible with `Taro.request`, you can specify [all configuration items](https://taro-docs.jd.com/docs/apis/network/request/) supported by `Taro.request` in the _config_ of method instance creation + + + + +```jsx +const list = () => + alovaInst.Get('/list', { + // The set parameters will be passed to Taro.request + enableHttp2: true + }); + +const App = () => { + const { loading, data } = useRequest(list); + + return ( + { loading ? Loading... : null } + The requested data is: { JSON.stringify(data) } + ) +}; +``` + + + + +```html + + Loading... + The requested data is: {{ data }} + + + +``` + + + + +### Upload + +When `requestType: 'upload'` is set in the _config_ of the method instance, it means to upload the file, the request adapter will call `Taro.uploadFile`, and the uploaded file data is placed in the data of the method instance, you need to specify in the data `name` and `filePath`, these two parameters will be passed to `Taro.uploadFile`, at the same time, you can also specify other parameters in data, and the request adapter will pass them to `formData `in parameters. + +Similarly, it is fully compatible with `Taro.uploadFile`, you can specify [all configuration items](https://taro-docs.jd.com/docs/apis/network/upload/uploadFile) supported by `Taro.uploadFile`, if there are additional parameters to be set, please specify them in _config_ of the method instance. + + + + +```jsx +const uploadFile = (name, filePath, formData) => + alovaInst.Post( + '/uploadImg', + { + name, + filePath, + + // Additional data will be passed into formData of uni.uploadFile + ...formData + }, + { + // Set the request method to upload, and the adapter will call uni.uploadFile + requestType: 'upload', + + // Start upload progress + enableUpload: true + } + ); + +const App = () => { + const { loading, data, uploading, send } = useRequest(uploadFile, { + immediate: false + }); + + const handleImageChoose = () => { + Taro.chooseImage({ + success: chooseImageRes => { + const tempFilePaths = chooseImageRes.tempFilePaths; + send('fileName', tempFilePaths[0], { + extra1: 'a', + extra2: 'b' + }); + } + }); + }; + + return ( + { loading ? Uploading... : null } + Upload progress: { uploading.loaded }/{ uploading.total } + + {/* ... */} + ) +} +``` + + + + +```html + + Uploading... + Upload progress: {{ uploading.loaded }}/{{ uploading.total }} + + + + + +``` + + + + +### download + +When `requestType: 'download'` is set in the _config_ of the method instance, it means to download the file, and the request adapter will call `Taro.downloadFile`. + +Similarly, it is fully compatible with `Taro.downloadFile`, you can specify [all configuration items](https://taro-docs.jd.com/docs/apis/network/download/downloadFile) supported by `Taro.downloadFile`, if there are additional parameters to be set, please specify them in _config_ of the method instance. + + + + +```jsx +const downloadFile = filePath => + alovaInst.Get('/bigImage.jpg', { + // Set the request method to download, and the adapter will call uni.downloadFile + requestType: 'download', + filePath, + + // Start download progress + enableDownload: true + }); + +const App = () => { + const { loading, data, downloading, send } = useRequest(downloadFile, { + immediate: false + }); + const handleImageDownload = () => { + send('file_save_path'); + }; + + return ( + { loading ? Downloading... : null } + Download progress: { downloading.loaded }/{ downloading.total } + + {/* ... */} + ); +} +``` + + + + +```html + + Downloading... + Download progress: {{ downloading.loaded }}/{{ downloading.total }} + + + + + +``` + + + + +## Mock request adapter compatible + +When using Taro to develop applications, we may still need to use mock requests, but by default, the response data of [Mock Request Adapter (@alova/mock)](/next/resource/request-adapter/alova-mock) is a `Response` instance, That is, it is compatible with the `alova/fetch` request adapter by default. When used in the Taro environment, we need to make the response data of the simulated request adapter compatible with the Taro adapter, so you need to use the **@alova/adapter-taro** package exported `taroMockResponse` as response adapter. + +```javascript +import { defineMock, createAlovaMockAdapter } from '@alova/mock'; +import AdapterTaro, { taroRequestAdapter, taroMockResponse } from '@alova/adapter-taro'; + +const mocks = defineMock({ + //... +}); + +// mock data request adapter +export default createAlovaMockAdapter([mocks], { + // After specifying the taro request adapter, requests that do not match the simulated interface will use this adapter to send requests + httpAdapter: taroRequestAdapter, + + // Simulate the response adapter, after specifying, the response data will be converted to a taro-compatible data format + onMockResponse: taroMockResponse +}); + +export const alovaInst = createAlova({ + baseURL: 'https://api.alovajs.org', + timeout: 5000, + ...AdapterTaro({ + // Control whether to use the simulated request adapter through environment variables + mockRequest: process.env.NODE_ENV === 'development' ? mockAdapter : undefined + }) + //... +}); +``` + +## Typescript + +The taro request adapter provides complete type adaptation, and the type of method configuration and response data will exactly match the type of taro. + +### method configuration + +When creating a method instance, in addition to the general configuration items in the method, you can also use the configuration items in `Taro.request`, `Taro.uploadFile` and `Taro.downloadFile`, we have removed the type and method Items that conflict with the common configuration of the instance. + +```typescript +/** + * Taro.request requests additional parameters + */ +export type TaroRequestConfig = Omit< + Taro.request.Option, + 'url' | 'data' | 'header' | 'method' | 'timeout' | 'success' | 'fail' | 'complete' +>; + +/** + * Taro.uploadFile additional parameter + */ +export type TaroUploadConfig = Omit< + Taro.uploadFile.Option, + | 'url' + | 'filePath' + | 'name' + | 'header' + | 'formData' + | 'timeout' + | 'success' + | 'fail' + | 'complete' +>; + +/** + * Taro.downloadFile additional parameters + */ +export type TaroDownloadConfig = Omit< + Taro.downloadFile.Option, + 'url' | 'header' | 'timeout' | 'success' | 'fail' | 'complete' +>; + +/** + * Merged request configuration parameters + */ +export type TaroConfig = { + /** + * Request type, upload means upload, download means download, not filling means normal request + */ + requestType?: 'upload' | 'download'; +} & TaroRequestConfig & + TaroUploadConfig & + TaroDownloadConfig; +``` + +### Response data + +Because the taro request adapter is compatible with `Taro.request`, `Taro.uploadFile` and `Taro.downloadFile`, but their response value types are slightly different, so the response data type is as follows: + +```typescript +type TaroResponse = + // The response type of Taro.request + | Taro.request.SuccessCallbackResult + + // The response type of Taro.uploadFile + | Taro.uploadFile.SuccessCallbackResult + + // The response type of Taro.downloadFile + | Taro.downloadFile.FileSuccessCallbackResult; +``` + +In actual use, we usually need to process the response data globally. It is recommended to judge the returned data separately. A simple example is as follows: + +```typescript +const alovaInst = createAlova({ + baseURL: 'https://api.alovajs.org', + ...AdapterTaro(), + responded(response) { + const { statusCode, data } = response as Taro.request.SuccessCallbackResult; + if (statusCode >= 400) { + throw new Error('request error'); + } + return data || null; + } +}); +``` diff --git a/docs/resource/01-request-adapter/05-alova-adapter-uniapp.md b/docs/resource/01-request-adapter/05-alova-adapter-uniapp.md index 1466a4d15..a23406852 100644 --- a/docs/resource/01-request-adapter/05-alova-adapter-uniapp.md +++ b/docs/resource/01-request-adapter/05-alova-adapter-uniapp.md @@ -1,279 +1,278 @@ ---- -title: Uniapp Adapter -sidebar_position: 50 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -:::info Tips - -This plugin only supports vue3 version of uniapp application. - -::: - -## Install - - - - -```bash -npm install @alova/adapter-uniapp --save -``` - - - - -```bash -yarn add @alova/adapter-uniapp -``` - - - - -## Usage - -### create alova - -Calling **AdapterUniapp** will return _Request Adapter_, _Storage Adapter_, and _VueHook_, so you no longer need to set these three items, and the usage is exactly the same. - -```javascript -import { createAlova } from 'alova'; -import AdapterUniapp from '@alova/adapter-uniapp'; - -const alovaInst = createAlova({ - baseURL: 'https://api.alovajs.org', - ...AdapterUniapp() -}); -``` - -### Request - -The usage method of the request is exactly the same as that used in the web environment. Already fully compatible with `uni.request`, you can specify [all configuration items] supported by `uni.request` in _config_ of method instance creation (https://uniapp.dcloud.net.cn/api/request/ request.html) - -```html - - Loading... - The requested data is: {{ data }} - - - -``` - -When using `useRequest/useWatcher` to send a request immediately, it will be executed asynchronously in the `onLoad` hook, so you can access the options data in `methodHandler` as follows: - -```javascript -import { onLoad } from '@dcloudio/uni-app'; - -let options = {}; -onLoad(opt => { - options = opt; -}); -const { loading, data } = useRequest(() => getDetail(options.id)); -``` - -### Upload - -When `requestType: 'upload'` is set in the _config_ of the method instance, it means to upload the file, the request adapter will call `uni.uploadFile`, and the uploaded file data is placed in the data of the method instance, you need to specify in the data `name`, `filePath or files`, and `file` (if necessary), these 4 parameters will be passed to `uni.uploadFile`, at the same time, you can also specify other parameters besides these 4 parameters in data , the request adapter will pass them into the `formData` parameter. - -Similarly, it is fully compatible with `uni.uploadFile`, you can specify [all configuration items] supported by `uni.uploadFile` in _config_ of method instance creation (https://uniapp.dcloud.net.cn/api /request/network-file.html#uploadfile), if there are additional parameters to be set, please specify them in _config_ of the method instance. - -```html - - Uploading... - Upload progress: {{ uploading.loaded }}/{{ uploading.total }} - - - - - -``` - -### download - -When `requestType: 'download'` is set in the _config_ of the method instance, it means to download the file, and the request adapter will call `uni.downloadFile`. - -Similarly, it is fully compatible with `uni.downloadFile`, you can specify [all configuration items] supported by `uni.downloadFile` in _config_ of method instance creation (https://uniapp.dcloud.net.cn/api /request/network-file.html#downloadfile), if there are additional parameters to be set, please specify them in _config_ of the method instance. - -```html - - Downloading... - Download progress: {{ downloading.loaded }}/{{ downloading.total }} - - - - - -``` - -## Mock request adapter compatible - -When using uniapp to develop applications, we may still need to use simulated requests, but by default, the response data of [mock adapter (@alova/mock)](/tutorial/request-adapter/alova-mock) is a `Response` instance, That is, it is compatible with the `GlobalFetch` request adapter by default. When used in the uniapp environment, we need to make the response data of the simulated request adapter compatible with the uniapp adapter, so you need to use the **@alova/adapter-uniapp** package exported `uniappMockResponse` as response adapter. - -```javascript -import { defineMock, createAlovaMockAdapter } from '@alova/mock'; -import AdapterUniapp, { uniappRequestAdapter, uniappMockResponse } from '@alova/adapter-uniapp'; - -const mocks = defineMock({ - //... -}); - -// mock data request adapter -export default createAlovaMockAdapter([mocks], { - // After specifying the uniapp request adapter, requests that do not match the simulated interface will use this adapter to send requests - httpAdapter: uniappRequestAdapter, - - // Simulate the response adapter, after specifying, the response data will be converted to a uniapp-compatible data format - onMockResponse: uniappMockResponse -}); - -export const alovaInst = createAlova({ - baseURL: 'https://api.alovajs.org', - timeout: 5000, - ...AdapterUniapp({ - // Control whether to use the simulated request adapter through environment variables - mockRequest: process.env.NODE_ENV === 'development' ? mockAdapter : undefined - }) - //... -}); -``` - -## Typescript - -uniapp request adapter provides complete type adaptation, and the type of method configuration and response data will exactly match the type of uniapp. - -### method configuration - -When creating a method instance, in addition to the general configuration items in the method, you can also use the configuration items in `uniapp.request`, `uniapp.uploadFile` and `uniapp.downloadFile`, we have removed and method from the type Items that conflict with the common configuration of the instance. - -```typescript -/** - * uni.request requests additional parameters - */ -export type UniappRequestConfig = Omit< - UniNamespace.RequestOptions, - 'url' | 'data' | 'header' | 'method' | 'timeout' | 'success' | 'fail' | 'complete' ->; - -/** - * uni.uploadFile additional parameters - */ -export type UniappUploadConfig = Omit< - UniNamespace.UploadFileOption, - 'url' | 'name' | 'header' | 'formData' | 'timeout' | 'success' | 'fail' | 'complete' ->; - -/** - * uni.downloadFile additional parameters - */ -export type UniappDownloadConfig = Omit< - UniNamespace.DownloadFileOption, - 'url' | 'header' | 'timeout' | 'success' | 'fail' | 'complete' ->; - -/** - * Merged request configuration parameters - */ -export type UniappConfig = { - /** - * Request type, upload means upload, download means download, not filling means normal request - */ - requestType?: 'upload' | 'download'; -} & UniappRequestConfig & - UniappUploadConfig & - UniappDownloadConfig; -``` - -### Response data - -Because the uniapp request adapter is compatible with `uniapp.request`, `uniapp.uploadFile` and `uniapp.downloadFile`, but their response value types are slightly different, so the response data type is as follows: - -```typescript -type UniappResponse = - // The response type of uni.request - | UniNamespace.RequestSuccessCallbackResult - - // The response type of uni.uploadFile - | UniNamespace.UploadFileSuccessCallbackResult - - // The response type of uni.downloadFile - | UniNamespace.DownloadSuccessData; -``` - -In actual use, we usually need to process the response data globally. It is recommended to judge the returned data separately. A simple example is as follows: - -```typescript -const alovaInst = createAlova( - baseURL: 'https://api.alovajs.org', - ...AdapterUniapp(), - responded(response) { - const { statusCode, data } = response as UniNamespace.RequestSuccessCallbackResult; - if (statusCode >= 400) { - throw new Error('request error'); - } - return data || null; - } -}); -``` +--- +title: Uniapp Adapter +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info Tips + +This plugin only supports vue3 version of uniapp application. + +::: + +## Install + + + + +```bash +npm install @alova/adapter-uniapp --save +``` + + + + +```bash +yarn add @alova/adapter-uniapp +``` + + + + +## Usage + +### create alova + +Calling **AdapterUniapp** will return _Request Adapter_, _Storage Adapter_, and _VueHook_, so you no longer need to set these three items, and the usage is exactly the same. + +```javascript +import { createAlova } from 'alova'; +import AdapterUniapp from '@alova/adapter-uniapp'; + +const alovaInst = createAlova({ + baseURL: 'https://api.alovajs.org', + ...AdapterUniapp() +}); +``` + +### Request + +The usage method of the request is exactly the same as that used in the web environment. Already fully compatible with `uni.request`, you can specify [all configuration items] supported by `uni.request` in _config_ of method instance creation (https://uniapp.dcloud.net.cn/api/request/ request.html) + +```html + + Loading... + The requested data is: {{ data }} + + + +``` + +When using `useRequest/useWatcher` to send a request immediately, it will be executed asynchronously in the `onLoad` hook, so you can access the options data in `methodHandler` as follows: + +```javascript +import { onLoad } from '@dcloudio/uni-app'; + +let options = {}; +onLoad(opt => { + options = opt; +}); +const { loading, data } = useRequest(() => getDetail(options.id)); +``` + +### Upload + +When `requestType: 'upload'` is set in the _config_ of the method instance, it means to upload the file, the request adapter will call `uni.uploadFile`, and the uploaded file data is placed in the data of the method instance, you need to specify in the data `name`, `filePath or files`, and `file` (if necessary), these 4 parameters will be passed to `uni.uploadFile`, at the same time, you can also specify other parameters besides these 4 parameters in data , the request adapter will pass them into the `formData` parameter. + +Similarly, it is fully compatible with `uni.uploadFile`, you can specify [all configuration items] supported by `uni.uploadFile` in _config_ of method instance creation (https://uniapp.dcloud.net.cn/api /request/network-file.html#uploadfile), if there are additional parameters to be set, please specify them in _config_ of the method instance. + +```html + + Uploading... + Upload progress: {{ uploading.loaded }}/{{ uploading.total }} + + + + + +``` + +### download + +When `requestType: 'download'` is set in the _config_ of the method instance, it means to download the file, and the request adapter will call `uni.downloadFile`. + +Similarly, it is fully compatible with `uni.downloadFile`, you can specify [all configuration items] supported by `uni.downloadFile` in _config_ of method instance creation (https://uniapp.dcloud.net.cn/api /request/network-file.html#downloadfile), if there are additional parameters to be set, please specify them in _config_ of the method instance. + +```html + + Downloading... + Download progress: {{ downloading.loaded }}/{{ downloading.total }} + + + + + +``` + +## Mock request adapter compatible + +When using uniapp to develop applications, we may still need to use simulated requests, but by default, the response data of [mock adapter (@alova/mock)](/next/resource/request-adapter/alova-mock) is a `Response` instance, That is, it is compatible with the `alova/fetch` request adapter by default. When used in the uniapp environment, we need to make the response data of the simulated request adapter compatible with the uniapp adapter, so you need to use the **@alova/adapter-uniapp** package exported `uniappMockResponse` as response adapter. + +```javascript +import { defineMock, createAlovaMockAdapter } from '@alova/mock'; +import AdapterUniapp, { uniappRequestAdapter, uniappMockResponse } from '@alova/adapter-uniapp'; + +const mocks = defineMock({ + //... +}); + +// mock data request adapter +export default createAlovaMockAdapter([mocks], { + // After specifying the uniapp request adapter, requests that do not match the simulated interface will use this adapter to send requests + httpAdapter: uniappRequestAdapter, + + // Simulate the response adapter, after specifying, the response data will be converted to a uniapp-compatible data format + onMockResponse: uniappMockResponse +}); + +export const alovaInst = createAlova({ + baseURL: 'https://api.alovajs.org', + timeout: 5000, + ...AdapterUniapp({ + // Control whether to use the simulated request adapter through environment variables + mockRequest: process.env.NODE_ENV === 'development' ? mockAdapter : undefined + }) + //... +}); +``` + +## Typescript + +uniapp request adapter provides complete type adaptation, and the type of method configuration and response data will exactly match the type of uniapp. + +### method configuration + +When creating a method instance, in addition to the general configuration items in the method, you can also use the configuration items in `uniapp.request`, `uniapp.uploadFile` and `uniapp.downloadFile`, we have removed and method from the type Items that conflict with the common configuration of the instance. + +```typescript +/** + * uni.request requests additional parameters + */ +export type UniappRequestConfig = Omit< + UniNamespace.RequestOptions, + 'url' | 'data' | 'header' | 'method' | 'timeout' | 'success' | 'fail' | 'complete' +>; + +/** + * uni.uploadFile additional parameters + */ +export type UniappUploadConfig = Omit< + UniNamespace.UploadFileOption, + 'url' | 'name' | 'header' | 'formData' | 'timeout' | 'success' | 'fail' | 'complete' +>; + +/** + * uni.downloadFile additional parameters + */ +export type UniappDownloadConfig = Omit< + UniNamespace.DownloadFileOption, + 'url' | 'header' | 'timeout' | 'success' | 'fail' | 'complete' +>; + +/** + * Merged request configuration parameters + */ +export type UniappConfig = { + /** + * Request type, upload means upload, download means download, not filling means normal request + */ + requestType?: 'upload' | 'download'; +} & UniappRequestConfig & + UniappUploadConfig & + UniappDownloadConfig; +``` + +### Response data + +Because the uniapp request adapter is compatible with `uniapp.request`, `uniapp.uploadFile` and `uniapp.downloadFile`, but their response value types are slightly different, so the response data type is as follows: + +```typescript +type UniappResponse = + // The response type of uni.request + | UniNamespace.RequestSuccessCallbackResult + + // The response type of uni.uploadFile + | UniNamespace.UploadFileSuccessCallbackResult + + // The response type of uni.downloadFile + | UniNamespace.DownloadSuccessData; +``` + +In actual use, we usually need to process the response data globally. It is recommended to judge the returned data separately. A simple example is as follows: + +```typescript +const alovaInst = createAlova( + baseURL: 'https://api.alovajs.org', + ...AdapterUniapp(), + responded(response) { + const { statusCode, data } = response as UniNamespace.RequestSuccessCallbackResult; + if (statusCode >= 400) { + throw new Error('request error'); + } + return data || null; + } +}); +``` diff --git a/docs/resource/02-storage-adapter/01-psc.md b/docs/resource/02-storage-adapter/01-psc.md index cf051f595..436d01410 100644 --- a/docs/resource/02-storage-adapter/01-psc.md +++ b/docs/resource/02-storage-adapter/01-psc.md @@ -1,6 +1,5 @@ --- title: Process Shared Adapter -sidebar_position: 10 --- Process shared storage adapter allows you to share cache in multi-process environment. @@ -64,16 +63,17 @@ createAlova({ Of course, you can share both l1Cache and l2Cache at the same time! Just use the scope option and create different shared storage adapters. ```javascript -const createScopedPSCAdapter = (scope: string) => createPSCAdapter( - NodeSyncAdapter(stopIPC => { - process.on('SIGTERM', stopIPC); - }), - // This parameter is used to specify the storage adapter. We will introduce this later - undefined, - // highlight-start - { scope } - // highlight-end -); +const createScopedPSCAdapter = (scope: string) => + createPSCAdapter( + NodeSyncAdapter(stopIPC => { + process.on('SIGTERM', stopIPC); + }), + // This parameter is used to specify the storage adapter. We will introduce this later + undefined, + // highlight-start + { scope } + // highlight-end + ); ``` ## Use in Electron @@ -138,25 +138,27 @@ When using multiple Alova instances, there is no need to create multiple `PSCAda ::: - ## Custom Storage Adapter By passing the second parameter to createPSCAdapter, you can specify the storage adapter to use. ```typescript -const pscAdapter = createPSCAdapter(ElectronSyncAdapter( - ipcRenderer, - // highlight-start - // Same as passing to l1Cache in createAlova. Use default implementation if passing undefined. - MyStorageAdapter() - // highlight-end -)); +const pscAdapter = createPSCAdapter( + ElectronSyncAdapter( + ipcRenderer, + // highlight-start + // Same as passing to l1Cache in createAlova. Use default implementation if passing undefined. + MyStorageAdapter() + // highlight-end + ) +); createAlova({ // ... l1Cache: pscAdapter }); ``` + > You can also use [lru-cache](https://www.npmjs.com/package/lru-cache) as a cache adapter. ## Custom SharedAdapter diff --git a/docs/resource/03-framework/01-vue-options.md b/docs/resource/03-framework/01-vue-options.md index 4f2a5ddd9..63033adba 100644 --- a/docs/resource/03-framework/01-vue-options.md +++ b/docs/resource/03-framework/01-vue-options.md @@ -1,6 +1,5 @@ --- title: vue2/3 options -sidebar_position: 10 --- import Tabs from '@theme/Tabs'; @@ -49,14 +48,14 @@ First use `vueOptionHook` to create an alova instance. ```javascript import { createAlova, Method } from 'alova'; -import GlobalFetch from 'alova/GlobalFetch'; +import adapterFetch from 'alova/fetch'; import { VueOptionsHook } from '@alova/vue-options'; // api.js const alovaInst = createAlova({ baseURL: 'http://example.com', statesHook: VueOptionsHook, - requestAdapter: GlobalFetch(), + requestAdapter: adapterFetch(), responded: response => response.json() }); @@ -325,9 +324,9 @@ export const getData = () => alovaInst.Get('/todolist'); **typescript** -To add response data type in typescript, please read [alova documentation typescript chapter](/tutorial/combine-framework/typescript) +To add response data type in typescript, please read [alova documentation typescript chapter](/next/tutorial/advanced/in-depth/typescript) ## limit -1. [Manage extra states](/tutorial/advanced/manage-extra-states) is not supported yet. +1. [Manage extra states](/next/tutorial/client/in-depth/manage-extra-states) is not supported yet. 2. Currently, only alova’s three core useHooks of `useRequest/useWatcher/useFetcher` are supported, as well as the encapsulation based on the core useHook in your own project. [@alova/scene](https://github.com/alovajs/scene) is not supported yet. extension useHook. diff --git a/docs/resource/03-framework/02-solid.md b/docs/resource/03-framework/02-solid.md index 6f7e0e099..eb5fb12f2 100644 --- a/docs/resource/03-framework/02-solid.md +++ b/docs/resource/03-framework/02-solid.md @@ -1,6 +1,5 @@ --- title: solid -sidebar_position: 20 --- Comming soon... diff --git a/docs/resource/03-framework/03-angular.md b/docs/resource/03-framework/03-angular.md index 245469211..dfc05e123 100644 --- a/docs/resource/03-framework/03-angular.md +++ b/docs/resource/03-framework/03-angular.md @@ -1,6 +1,5 @@ --- title: angular -sidebar_position: 30 --- Comming soon... diff --git a/docs/resource/03-framework/04-native-mp.md b/docs/resource/03-framework/04-native-mp.md index a55050983..6d5e8bc1c 100644 --- a/docs/resource/03-framework/04-native-mp.md +++ b/docs/resource/03-framework/04-native-mp.md @@ -1,6 +1,5 @@ --- title: Native mini program(China🇨🇳) -sidebar_position: 40 --- Comming soon... diff --git a/docs/resource/03-framework/05-preact.md b/docs/resource/03-framework/05-preact.md index c70ee8364..a996552a5 100644 --- a/docs/resource/03-framework/05-preact.md +++ b/docs/resource/03-framework/05-preact.md @@ -1,6 +1,5 @@ --- title: preact -sidebar_position: 50 --- Comming soon... diff --git a/docs/resource/03-framework/06-qwik.md b/docs/resource/03-framework/06-qwik.md index 3d2cd2c94..00c0f06e3 100644 --- a/docs/resource/03-framework/06-qwik.md +++ b/docs/resource/03-framework/06-qwik.md @@ -1,6 +1,5 @@ --- title: qwik -sidebar_position: 60 --- Comming soon... diff --git a/docs/resource/03-framework/07-lit.md b/docs/resource/03-framework/07-lit.md index 8d3500f34..f3b5a8b92 100644 --- a/docs/resource/03-framework/07-lit.md +++ b/docs/resource/03-framework/07-lit.md @@ -1,6 +1,5 @@ --- title: lit -sidebar_position: 70 --- Comming soon... diff --git a/docs/resource/03-framework/08-stencil.md b/docs/resource/03-framework/08-stencil.md index e651aacfd..cb9b09671 100644 --- a/docs/resource/03-framework/08-stencil.md +++ b/docs/resource/03-framework/08-stencil.md @@ -1,6 +1,5 @@ --- title: stencil -sidebar_position: 80 --- Comming soon... diff --git a/docs/tutorial/01-example/01-init-page.md b/docs/tutorial/01-example/01-init-page.md deleted file mode 100644 index d38ee50b1..000000000 --- a/docs/tutorial/01-example/01-init-page.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -title: (vue)page initialization request -sidebar_position: 10 ---- - -import InitPage from '@site/example-links/InitPage'; - -> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started); - - - -:::info example description - -This example mainly shows the simplicity of the request, it will return stateful request status, data, etc. which can be used directly in the view and managed directly by alova - -::: diff --git a/docs/tutorial/01-example/02-submit-form.md b/docs/tutorial/01-example/02-submit-form.md deleted file mode 100644 index b5f3bcce0..000000000 --- a/docs/tutorial/01-example/02-submit-form.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -title: (vue)form submission -sidebar_position: 20 ---- - -import SubmitForm from '@site/example-links/SubmitForm'; - -> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started); - - - -:::info example description - -This example mainly shows the basic data submission encoding method - -::: diff --git a/docs/tutorial/01-example/03-condition-search.md b/docs/tutorial/01-example/03-condition-search.md deleted file mode 100644 index e7a58e069..000000000 --- a/docs/tutorial/01-example/03-condition-search.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -title: (vue)Conditional Search -sidebar_position: 30 ---- - -import ConditionSearch from '@site/example-links/ConditionSearch'; - -> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started); - - - -:::info example description - -This example mainly shows the simplicity of data search. There is no need for the developer to maintain the state of the request, data, etc., and there is no need to manually trigger the request to send. Just bind the state of the search conditions. - -::: diff --git a/docs/tutorial/01-example/04-memory-cache.md b/docs/tutorial/01-example/04-memory-cache.md deleted file mode 100644 index 6768af625..000000000 --- a/docs/tutorial/01-example/04-memory-cache.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -title: (vue)Response Cache - Memory Mode -sidebar_position: 40 ---- - -import MemoryCache from '@site/example-links/MemoryCache'; - -> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started); - - - -:::info example description - -The memory cache mode stores the response data in memory, and the cache is invalid when the page is refreshed. - -_Operation guide:_ - -1. Click on the following student list item, the requesting student details will be sent, and the modal box will display the Loading status; -2. Click the mask to close the pop-up box and reopen it. At this time, the cache will be hit and the student details will be displayed immediately, and the request record will no longer be printed in Request Records; - -::: diff --git a/docs/tutorial/01-example/05-storage-placeholder.md b/docs/tutorial/01-example/05-storage-placeholder.md deleted file mode 100644 index 9fd88b854..000000000 --- a/docs/tutorial/01-example/05-storage-placeholder.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -title: (vue)Response Cache - Cache Placeholder Mode -sidebar_position: 50 ---- - -import StoragePlaceholder from '@site/example-links/StoragePlaceholder'; - -> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started); - - - -:::info example description - -The cache placeholder mode is to persist the response data. It will be updated to the data state as placeholder data immediately after refreshing the page. At the same time, a request is sent. Developers can use placeholder data to replace the Loading state before responding. - -_Operation guide:_ - -1. Click `Reload page` to refresh the page, you no longer see the Loading status, but the old data is rendered and replaced with new data when the request is responded; -2. Click `Invalidate the data of placeholder` to invalidate the cached data, then you will see the Loading status again; - -::: diff --git a/docs/tutorial/01-example/06-storage-restore.md b/docs/tutorial/01-example/06-storage-restore.md deleted file mode 100644 index bfa105451..000000000 --- a/docs/tutorial/01-example/06-storage-restore.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -title: (vue)Response Caching - Restore Mode -sidebar_position: 60 ---- - -import StorageRestore from '@site/example-links/StorageRestore'; - -> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started); - - - -:::info example description - -The cache restore mode is to persist the response data. When the request hits the cache, the persistent cached data will be returned, and no more requests will be issued. It is generally used for some data that needs server management but does not change for a certain period of time. The following is an example of the restore mode of holiday information. - -_Operation guide:_ - -1. Click `Reload page` to refresh the page, you will no longer see the Loading status, but will use the cached data and render it to the page immediately, and no longer send requests; -2. Click `Invalidate the data of placeholder` to invalidate the cached data, then you will see the Loading status again; - -::: diff --git a/docs/tutorial/01-example/07-update-state.md b/docs/tutorial/01-example/07-update-state.md deleted file mode 100644 index 00f6ed794..000000000 --- a/docs/tutorial/01-example/07-update-state.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -title: (vue)Update state across components/pages -sidebar_position: 70 ---- - -import UpdateState from '@site/example-links/UpdateState'; - -> The example uses vue3 as an example, but you can also use alova in react and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details; - - - -:::info example description - -When you need to update state across components or pages, you are often limited by the component hierarchy. Here is a way to break this limitation. - -_Operation guidance:_ - -In the edit box or edit page, after adding or editing list data, the list data is updated across component levels. - -::: diff --git a/docs/tutorial/01-example/08-prefetch.md b/docs/tutorial/01-example/08-prefetch.md deleted file mode 100644 index ab2690dc1..000000000 --- a/docs/tutorial/01-example/08-prefetch.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -title: (vue)Data Prefetch -sidebar_position: 80 ---- - -import Prefetch from '@site/example-links/Prefetch'; - -> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started); - - - -:::info example description - -Data pre-fetching is a way to predict user operation behavior to achieve a strategy of displaying content to users faster. - -_Operation guide:_ - -1. Move the mouse to any list item and stay there for 0.2 seconds, a request for detailed data will be sent on the bottom panel; -2. Click to open this list item, you can see the detailed data immediately; - -::: diff --git a/docs/tutorial/01-example/09-load-more.md b/docs/tutorial/01-example/09-load-more.md deleted file mode 100644 index ebe27569f..000000000 --- a/docs/tutorial/01-example/09-load-more.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: (vue)Load more -sidebar_position: 90 ---- - -import LoadMore from '@site/example-links/LoadMore'; - -> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started); - - - -:::info example description - -Using pagination strategy, achieve a more high-performance and easy-to-use pagination function, automatic management of paging-related status, preloading of previous and previous pages, and automatic maintenance of data addition/editing/replacement/ Removed, and request-level stabilization. - -_Operation guide:_ - -1. After the initialization is completed, the next page of data will be preloaded, and there is no need to wait for scrolling down the page; -2. Adding, deleting, and modifying list items does not need to reset the list, it will be automatically processed into the same effect as the re-request; - -[usePagination documentation](/tutorial/strategy/usePagination) - -::: diff --git a/docs/tutorial/01-example/10-paginated-list.md b/docs/tutorial/01-example/10-paginated-list.md deleted file mode 100644 index a1e55d20d..000000000 --- a/docs/tutorial/01-example/10-paginated-list.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: (vue)Paginated list -sidebar_position: 100 ---- - -import Pagination from '@site/example-links/Pagination'; - -> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started); - - - -:::info example description - -Using pagination strategy, achieve a more high-performance and easy-to-use pagination function, automatic management of paging-related status, preloading of previous and previous pages, and automatic maintenance of data addition/editing/replacement/ Removed, and request-level stabilization. - -_Operation guide:_ - -1. After the initialization is completed, the next page of data will be preloaded, and there is no need to wait when turning to the next page; -2. Adding, deleting, and modifying list items does not need to reset the list, it will be automatically processed into the same effect as the re-request; - -[usePagination documentation](/tutorial/strategy/usePagination) - -::: diff --git a/docs/tutorial/01-example/11-controlled-cache-by-indexeddb.md b/docs/tutorial/01-example/11-controlled-cache-by-indexeddb.md deleted file mode 100644 index b5b5eaf4f..000000000 --- a/docs/tutorial/01-example/11-controlled-cache-by-indexeddb.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: (vue)Manage Cache with IndexedDB -sidebar_position: 110 ---- - -import ControlledCacheByIndexedDB from '@site/example-links/ControlledCacheByIndexedDB'; - -> The example uses vue3 as an example, but you can also use alova in react and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details; - - - -:::info example description - -Using controlled cache allows developers to customize and manage the cache. Under the large file cache, it can cooperate with IndexedDB to manage the local cache. - -_Operation guidance:_ - -1. Select one of the pictures, the picture will request the network to load first, and the picture data will be saved in the local IndexedDB; -2. Refresh the page and select the same picture again, the picture will get data from IndexedDB instead of initiating a network request; - -[Controlled cache document](/tutorial/cache/controlled-cache) - -::: diff --git a/docs/tutorial/01-example/12-silent-submit-setting.md b/docs/tutorial/01-example/12-silent-submit-setting.md deleted file mode 100644 index aa49e9dc2..000000000 --- a/docs/tutorial/01-example/12-silent-submit-setting.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: (svelte)Silent Submit - Settings Page -sidebar_position: 120 ---- - -import SettingSilentSvelte from '@site/example-links/SettingSilentSvelte'; - -> The example uses svelte as an example, but you can also use alova in react and vue, please read the [Getting Started Guide](/tutorial/getting-started) for details; - - - -:::info example description - -Use the silent submission strategy, submit and respond immediately, greatly reducing the impact of network fluctuations, so that your application is still very smooth even when the network is unstable or even disconnected. - -_Operation guidance:_ - -1. Operate the setting item, it will generate feedback immediately without waiting for the server to respond; -2. Switch the request mode and network status to experience the difference between them; - -[Silent submit strategy document](/tutorial/strategy/sensorless-data-interaction) - -::: diff --git a/docs/tutorial/01-example/13-silent-submit-simple-list.md b/docs/tutorial/01-example/13-silent-submit-simple-list.md deleted file mode 100644 index 2b4ba3046..000000000 --- a/docs/tutorial/01-example/13-silent-submit-simple-list.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: (vue)Silent submit - Simple List -sidebar_position: 130 ---- - -import SimpleListSilentVue from '@site/example-links/SimpleListSilentVue'; - -> The example uses vue3 as an example, but you can also use alova in react and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details; - - - -:::info example description - -A simple list implemented using the silent submission strategy, which responds immediately after submission, greatly reduces the impact of network fluctuations, allowing your application to remain very smooth even when the network is unstable or even disconnected. - -_Operation guidance:_ - -1. Add, edit, and delete list items, it will generate feedback immediately without waiting for the server to respond; -2. Switch the request mode and network status to experience the difference between them; - -[Silent submit strategy document](/tutorial/strategy/sensorless-data-interaction) - -::: diff --git a/docs/tutorial/01-example/14-silent-submit-notes.md b/docs/tutorial/01-example/14-silent-submit-notes.md deleted file mode 100644 index d4ae8b3b8..000000000 --- a/docs/tutorial/01-example/14-silent-submit-notes.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: (react)Silent submit - Notebook -sidebar_position: 140 ---- - -import NoteSilentReact from '@site/example-links/NoteSilentReact'; - -> The example uses react as an example, but you can also use alova in vue3 and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details; - - - -:::info example description - -A simple list implemented using the silent submission strategy, which responds immediately after submission, greatly reduces the impact of network fluctuations, allowing your application to remain very smooth even when the network is unstable or even disconnected. - -_Operation guidance:_ - -1. Add, edit, and delete notes, it will generate feedback immediately without waiting for the server to respond; -2. Switch the request mode and network status to experience the difference between them; - -[Silent submit strategy document](/tutorial/strategy/sensorless-data-interaction) - -::: diff --git a/docs/tutorial/01-example/15-form-hook.md b/docs/tutorial/01-example/15-form-hook.md deleted file mode 100644 index 8dbccd923..000000000 --- a/docs/tutorial/01-example/15-form-hook.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -title: (react)Form submit strategy -sidebar_position: 150 ---- - -import FormHook from '@site/example-links/FormHook'; - -> The example uses react as an example, but you can also use alova in vue3 and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details; - - - -:::info example description - -This example provides four parts: form data persistence, form editing, multi-module form, and form filtering data, which can be tried separately. - -[Form submit strategy document](/tutorial/strategy/useForm) - -::: diff --git a/docs/tutorial/01-example/16-captcha-send.md b/docs/tutorial/01-example/16-captcha-send.md deleted file mode 100644 index 1eca34a3d..000000000 --- a/docs/tutorial/01-example/16-captcha-send.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -title: (react)Send captcha -sidebar_position: 160 ---- - -import CaptchaSend from '@site/example-links/CaptchaSend'; - -> The example uses react as an example, but you can also use alova in vue3 and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details; - - - -:::info example description - -This example demonstrates the convenient implementation of the captcha sending function. - -[Captcha sending strategy document](/tutorial/strategy/useCaptcha) - -::: diff --git a/docs/tutorial/01-example/17-retriable-hook.md b/docs/tutorial/01-example/17-retriable-hook.md deleted file mode 100644 index 8febc2043..000000000 --- a/docs/tutorial/01-example/17-retriable-hook.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -title: (react)Request Retry/Poll Request -sidebar_position: 170 ---- - -import RetriableHook from '@site/example-links/RetriableHook'; - -> The example uses react as an example, but you can also use alova in vue3 and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details; - - - -:::info example description - -This example provides four parts: form data persistence, form editing, multi-module form, and form filtering data, which can be tried separately. - -[Request Retry Strategy Documentation](/tutorial/strategy/useRetriableRequest) - -::: diff --git a/docs/tutorial/01-example/18.action-delegation-middleware.md b/docs/tutorial/01-example/18.action-delegation-middleware.md deleted file mode 100644 index 2590d7c38..000000000 --- a/docs/tutorial/01-example/18.action-delegation-middleware.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -title: (react)Trigger requests across components -sidebar_position: 180 ---- - -import ActionDelegationMiddleware from '@site/example-links/ActionDelegationMiddleware'; - -> The example uses react as an example, but you can also use alova in vue3 and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details; - - - -:::info example description - -This example mainly uses the `actionDelegationMiddleware` middleware to demonstrate the refresh of server-side data across any level of components without using global state management. - -[Actions delegation middleware document](/tutorial/strategy/actionDelegationMiddleware) - -::: diff --git a/docs/tutorial/01-example/19-serial-request.md b/docs/tutorial/01-example/19-serial-request.md deleted file mode 100644 index 34fb885df..000000000 --- a/docs/tutorial/01-example/19-serial-request.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -title: (vue)serial request hook -sidebar_position: 190 ---- - -import SerialRequest from '@site/example-links/SerialRequest'; - -> The example uses vue3 as an example, but you can also use alova in react and svelte, please read [Getting Started Guide](/tutorial/getting-started) for details; - - - -:::info example description - -This example mainly demonstrates a more elegant serial request through `useSerialRequest`. - -[useSerialRequest](/tutorial/strategy/useSerialRequest) -[useSerialWatcher](/tutorial/strategy/useSerialWatcher) - -::: diff --git a/docs/tutorial/01-example/_category_.json b/docs/tutorial/01-example/_category_.json deleted file mode 100644 index 52fa495e9..000000000 --- a/docs/tutorial/01-example/_category_.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "label": "Examples", - "link": { - "type": "generated-index", - "description": "Here are rich examples to show how alova behaves in different request scenarios.The dependencies will be installed before the example runs, so wait for a while!" - } -} diff --git a/docs/tutorial/02-getting-started/README.md b/docs/tutorial/02-getting-started/01-introduce.md similarity index 93% rename from docs/tutorial/02-getting-started/README.md rename to docs/tutorial/02-getting-started/01-introduce.md index d28fedb28..a9a3dbebb 100644 --- a/docs/tutorial/02-getting-started/README.md +++ b/docs/tutorial/02-getting-started/01-introduce.md @@ -1,11 +1,13 @@ --- -title: What is alova +title: Introduce --- import Link from '@docusaurus/Link'; import NavCard from '@site/src/components/NavCard'; import SupportList from '@site/src/components/SupportList'; +## What is alova? + Alova is an creative next-generation request tool that takes front-end and back-end collaboration and API consumption as its starting point, simplifies API consumption from 7 steps to only 1, saving you most of the work in requesting and making network requests very simple. Let's see how Alova can help you simplify your work. ![](/img/overview_flow_en.png) @@ -34,7 +36,7 @@ const { loading, error, data, page, pageSize, total } = usePagination((page, siz ); ``` -alova provides 10+ request strategy modules based on the [RSM](/tutorial/others/RSM) specification, which are implemented in the form of useHook. +alova provides 10+ request strategy modules based on the [RSM](/about/RSM) specification, which are implemented in the form of useHook. ### Alova editor extension @@ -42,7 +44,7 @@ Using the alova extension in vscode can help you automatically generate request This extension also optimizes the API consumption process and gives you a different API usage experience. In the past, you needed to query the API document first and constantly switch between the API document and the editor to write request codes. With the alova plug-in, you no longer need to leave the editor and can directly use the API in the editor while checking. -> For a detailed introduction to the alova plug-in, please refer to [Integrated Editor Extension](/tutorial/getting-started/extension-integration). +> For a detailed introduction to the alova plug-in, please refer to [Integrated Editor Extension](/next/tutorial/getting-started/extension-integration). ## Is there any difference? @@ -60,7 +62,7 @@ In addition, let's take a look at the specific features: :::info comparison -You can also check [Comparison with other request libraries](/tutorial/others/comparison) to learn more about the differences of Alova. +You can also check [Comparison with other request libraries](/about/comparison) to learn more about the differences of Alova. ::: @@ -107,7 +109,7 @@ target: '__blank' ## Welcome to contribute -Before contributing, please be sure to read the [Contribution Guide](/contributing/overview) in detail to ensure your effective contribution. +Before contributing, please be sure to read the [Contribution Guide](/next/contributing/overview) in detail to ensure your effective contribution. ## Start diff --git a/docs/tutorial/02-getting-started/02-quick-start.md b/docs/tutorial/02-getting-started/02-quick-start.md index ec4855f93..8bce9680d 100644 --- a/docs/tutorial/02-getting-started/02-quick-start.md +++ b/docs/tutorial/02-getting-started/02-quick-start.md @@ -1,6 +1,5 @@ --- title: Quick Start -sidebar_position: 20 --- import Tabs from '@theme/Tabs'; @@ -12,7 +11,7 @@ import quickStartPOST from '!!raw-loader!@site/codesandbox/01-getting-started/02 :::tip Example Tips -If you haven't learned about alova yet, it is recommended that you read [alova overview](/tutorial/getting-started) first. +If you haven't learned about alova yet, it is recommended that you read [introduce alova](/next/tutorial/getting-started/introduce) first. ::: @@ -49,8 +48,6 @@ bun add alova -> You can also [use alova through CDN](/tutorial/others/use-in-static) - ## Create an alova instance In alova, you need to initiate a request through an alova instance. Let's create one first. When creating an alova instance, you need to specify a request adapter. Here we recommend using the `alova/fetch` request adapter, which is a wrapper based on the `fetch API` and is very concise. @@ -79,7 +76,7 @@ requestAdapter: fetchAdapter(); }); ``` -> When using fetchAdapter in nodejs, the nodejs version requires `v17.5`, or you can use [axios request adapter](/tutorial/request-adapter/alova-adapter-axios/). +> When using fetchAdapter in nodejs, the nodejs version requires `v17.5`, or you can use [axios request adapter](/next/resource/request-adapter/alova-adapter-axios/). diff --git a/docs/tutorial/02-getting-started/03-method.md b/docs/tutorial/02-getting-started/03-basic/03-method.md similarity index 92% rename from docs/tutorial/02-getting-started/03-method.md rename to docs/tutorial/02-getting-started/03-basic/03-method.md index c3d054c89..aeec4f13b 100644 --- a/docs/tutorial/02-getting-started/03-method.md +++ b/docs/tutorial/02-getting-started/03-basic/03-method.md @@ -1,318 +1,317 @@ ---- -title: Method Instance -sidebar_position: 30 ---- - -In the previous chapter, we tried to send a request and get the response data. In fact, `alovaInstance.Get(...)` is not a function that initiates a request, but creates a method instance, which is a PromiseLike instance. You can send a request through the `then, catch, finally` method or `await`, just like a Promise object. - -```javascript -const userMethodInstance = alovaInstance.Get('/api/user'); - -userMethodInstance.then(response => { - // ... -}); - -userMethodInstance.catch(error => { - // ... -}); - -userMethodInstance.finally(() => { - // ... -}); - -try { - await userMethodInstance; -} catch (error) { - // ... -} finally { - // ... -} -``` - -Simple way to write: - -```javascript -const response = await alovaInstance.Get('/api/user'); -``` - -Each method instance describes the type of each request, request url, request header, request parameters, etc. In addition, you can also define request behavior on the method instance to control how the method handles the request. - -## Request type - -alova provides 7 request types: GET, POST, PUT, DELETE, HEAD, OPTIONS, and PATCH. - -| Instance creation function | Parameters | -| -------------------------- | --------------------------------------------- | -| GET | `alovaInstance.Get(url[, config])` | -| POST | `alovaInstance.Post(url[, data[, config]])` | -| PUT | `alovaInstance.Put(url[, data[, config]])` | -| DELETE | `alovaInstance.Delete(url[, data[, config]])` | -| HEAD | `alovaInstance.Head(url[, config])` | -| OPTIONS | `alovaInstance.Options(url[, config])` | -| PATCH | `alovaInstance.Patch(url[, data[, config]])` | - -Parameter description: - -- `url` is the request path; -- `data` is the request body data; -- `config` is the request configuration object, which includes the request header, params parameters, request behavior parameters and other configurations; - -You can also create your own custom method instance, which is useful when dynamically specifying the request type. - -```javascript -import { Method } from 'alova'; - -const method = new Method('GET', alovaInstance, '/api/users', { - params: { - ID: 1 - } -}); -``` - -Next, let's take a look at how to define request parameters, which should be familiar to you. - -## Request parameters - -### URL parameters - -Use params to pass in URL parameters, and the params parameters will be concatenated after the url in the form of ?. - -```javascript -alovaInstance.Get('/todo/list', { - params: { - userId: 1 - } -}); -``` - -Of course, you can also concatenate directly after the url, and the effect is the same. - -```javascript -alovaInstance.Get('/todo/list?userId=1'); -``` - -### Request body - -When sending **POST, PUT, DELETE, PATCH requests**, data can be sent through the request body. At this time, the second parameter passed in is the request body. It is worth noting that the POST request can also pass in the params parameter. - -```javascript -alovaInstance.Post( - '/todo', - // The second parameter is the request body - { - title: 'test todo', - time: '12:00' - }, - // The third parameter is the configuration - { - params: { - userId: 1 - } - } -); -``` - -### Request header - -Specify the request header through headers. - -```javascript -alovaInstance.Get('/user', { - headers: { - 'Content-Type': 'application/json;charset=UTF-8' - } -}); -``` - -### Parameters supported by other request adapters - -In addition to request headers, params parameters, etc., it also supports configuring parameters supported by the corresponding request adapter. When using `alova/fetch` as the request adapter of alova, you can configure any parameters supported by `fetch API` on the method instance, and these parameters will be passed to the `fetch` function when requesting. - -```javascript -alovaInstance.Get('/todo/list', { - // ... - // highlight-start - credentials: 'same-origin', - referrerPolicy: 'no-referrer', - mode: 'cors' - // highlight-end -}); -``` - -When the above method instance sends a request through `fetch`, it will request with the following parameters. - -```javascript -fetch('/todo/list', { - // ... - // highlight-start - credentials: 'same-origin', - referrerPolicy: 'no-referrer', - mode: 'cors' - // highlight-end -}); -``` - -> In addition to passing Object, the request body can also pass request body parameters supported by the request adapter, such as `alova/fetch` supports passing `string | FormData | Blob | ArrayBuffer | URLSearchParams | ReadableStream` parameters. - -If you use other request adapters, you can also pass the parameters they support. - -## Request behavior - -In [RSM](/tutorial/others/RSM), request behavior is used to describe how the request will be processed. - -### Timeout - -Set the request timeout. - -```javascript -// Request timeout at request level -alovaInstance.Get('/todo/list', { - // ... - // highlight-start - timeout: 10000 - // highlight-end -}); -``` - -### Request sharing - -We always encounter this situation. When a request is sent but not responded, the same request is sent again, which causes request waste or repeated submission problems, such as the following three scenarios: - -1. A component will obtain initialization data when it is created. When a page renders multiple components at the same time, multiple identical requests will be sent at the same time; - -2. The submit button is not disabled, and the user clicks the submit button multiple times; - -3. When the preload page is entered before the preload is completed, multiple identical requests will be sent; - -4. Prevent repeated requests in react's StrictMode; - -Shared requests are used to solve these problems. It can not only improve the fluency of the application, but also reduce the pressure on the server. - -```mermaid -flowchart LR -classDef response fill:#a8bcff -R1[request 1] --> S1[send request] --> W1[wait for response]:::response --> RE1[receive data 1] -R2[same request as request 1] --> W1[wait for response]:::response --> RE2[receive data 1] -``` - -Request sharing is enabled by default. If you want to turn off sharing requests on a specific request, you can do this: - -```javascript -alovaInst.Get('/todo', { - // ... - // highlight-start - shareRequest: false - // highlight-end -}); -``` - -:::warning How to identify the same request - -The request method, request url, request header, url parameter, and request body of the method instance are used as unique identifiers. Identical identifiers indicate the same request, rather than comparing the reference address of the method instance. - -::: - -### Transform response data - -Sometimes we need to uniformly transform response data. We can set the `transformData` function for the method instance to transform the response data into the required structure. - -```javascript -alovaInstance.Get('/todo/list', { - // The function accepts the response data and response header data, and requires the transformed data to be returned. - transformData(rawData, headers) { - return rawData.list.map(item => { - return { - ...item, - statusText: item.done ? 'Completed' : 'In progress' - }; - }); - } -}); -``` - -### Response cache - -Response cache allows you to better utilize server-side data multiple times without sending a request to obtain data every time. The GET request will set a memory cache time of 5 minutes by default. If you don't need it, you can turn off the cache for the current request in the following way. - -```ts -alovaInstance.Get('/todo/list', { - // Set to 0 or null to turn off the default response cache - cacheFor: 0 -}); -``` - -For details, please refer to [Response Cache](/tutorial/cache/mode) - -## Interrupt request - -Call the `abort` of the method instance to interrupt the request. - -```javascript -const userMethod = alovaInstance.Get('/api/user'); -userMethod.then(res => { - // ... -}); - -const handleCancel = () => { - // highlight-start - userMethod.abort(); - // highlight-end -}; -``` - -## Listen for upload and download progress - -By binding the upload progress event through the `onUpload` of the method instance, and the download progress event through the `onDownload`, it will return the unbinding function. - -```javascript -const uploadMethod = alovaInstance.Post('/todo/uploadfile', formData); -const offUploadEvent = uploadMethod.onUpload(event => { - console.log('File size:', event.total); - console.log('Uploaded:', event.loaded); -}); - -uploadMethod.then(res => { - // ... -}); - -// Unbind upload callback -const handleOffEvent = () => { - offUploadEvent(); -}; -``` - -```javascript -const downloadMethod = alovaInstance.Get('/todo/downloadfile'); -const offDownloadEvent = downloadMethod.onDownload(event => { - console.log('File size:', event.total); - console.log('Downloaded:', event.loaded); -}); - -downloadMethod.then(res => { - // ... -}); - -// Unbind download callback -const handleOffEvent = () => { - offDownloadEvent(); -}; -``` - -:::warning Use `alova/fetch` adapter to pay attention to - -Due to the limitation of fetch api, the one provided by alova `alova/fetch` adapter does not support upload progress. If you need upload progress, please use [XMLHttpRequest adapter](/tutorial/request-adapter/alova-adapter-xhr) or [axios adapter](/tutorial/request-adapter/alova-adapter-axios). - -You can also write your own request adapter, see [Writing a request adapter](/tutorial/custom/custom-http-adapter) for details. - -::: - -**Upload/download progress type** - -```typescript -type Progress = { - /** Total amount of data uploaded or downloaded */ - total: number; - /** Completed data */ - loaded: number; -}; -``` +--- +title: Method Instance +--- + +In the previous chapter, we tried to send a request and get the response data. In fact, `alovaInstance.Get(...)` is not a function that initiates a request, but creates a method instance, which is a PromiseLike instance. You can send a request through the `then, catch, finally` method or `await`, just like a Promise object. + +```javascript +const userMethodInstance = alovaInstance.Get('/api/user'); + +userMethodInstance.then(response => { + // ... +}); + +userMethodInstance.catch(error => { + // ... +}); + +userMethodInstance.finally(() => { + // ... +}); + +try { + await userMethodInstance; +} catch (error) { + // ... +} finally { + // ... +} +``` + +Simple way to write: + +```javascript +const response = await alovaInstance.Get('/api/user'); +``` + +Each method instance describes the type of each request, request url, request header, request parameters, etc. In addition, you can also define request behavior on the method instance to control how the method handles the request. + +## Request type + +alova provides 7 request types: GET, POST, PUT, DELETE, HEAD, OPTIONS, and PATCH. + +| Instance creation function | Parameters | +| -------------------------- | --------------------------------------------- | +| GET | `alovaInstance.Get(url[, config])` | +| POST | `alovaInstance.Post(url[, data[, config]])` | +| PUT | `alovaInstance.Put(url[, data[, config]])` | +| DELETE | `alovaInstance.Delete(url[, data[, config]])` | +| HEAD | `alovaInstance.Head(url[, config])` | +| OPTIONS | `alovaInstance.Options(url[, config])` | +| PATCH | `alovaInstance.Patch(url[, data[, config]])` | + +Parameter description: + +- `url` is the request path; +- `data` is the request body data; +- `config` is the request configuration object, which includes the request header, params parameters, request behavior parameters and other configurations; + +You can also create your own custom method instance, which is useful when dynamically specifying the request type. + +```javascript +import { Method } from 'alova'; + +const method = new Method('GET', alovaInstance, '/api/users', { + params: { + ID: 1 + } +}); +``` + +Next, let's take a look at how to define request parameters, which should be familiar to you. + +## Request parameters + +### URL parameters + +Use params to pass in URL parameters, and the params parameters will be concatenated after the url in the form of ?. + +```javascript +alovaInstance.Get('/todo/list', { + params: { + userId: 1 + } +}); +``` + +Of course, you can also concatenate directly after the url, and the effect is the same. + +```javascript +alovaInstance.Get('/todo/list?userId=1'); +``` + +### Request body + +When sending **POST, PUT, DELETE, PATCH requests**, data can be sent through the request body. At this time, the second parameter passed in is the request body. It is worth noting that the POST request can also pass in the params parameter. + +```javascript +alovaInstance.Post( + '/todo', + // The second parameter is the request body + { + title: 'test todo', + time: '12:00' + }, + // The third parameter is the configuration + { + params: { + userId: 1 + } + } +); +``` + +### Request header + +Specify the request header through headers. + +```javascript +alovaInstance.Get('/user', { + headers: { + 'Content-Type': 'application/json;charset=UTF-8' + } +}); +``` + +### Parameters supported by other request adapters + +In addition to request headers, params parameters, etc., it also supports configuring parameters supported by the corresponding request adapter. When using `alova/fetch` as the request adapter of alova, you can configure any parameters supported by `fetch API` on the method instance, and these parameters will be passed to the `fetch` function when requesting. + +```javascript +alovaInstance.Get('/todo/list', { + // ... + // highlight-start + credentials: 'same-origin', + referrerPolicy: 'no-referrer', + mode: 'cors' + // highlight-end +}); +``` + +When the above method instance sends a request through `fetch`, it will request with the following parameters. + +```javascript +fetch('/todo/list', { + // ... + // highlight-start + credentials: 'same-origin', + referrerPolicy: 'no-referrer', + mode: 'cors' + // highlight-end +}); +``` + +> In addition to passing Object, the request body can also pass request body parameters supported by the request adapter, such as `alova/fetch` supports passing `string | FormData | Blob | ArrayBuffer | URLSearchParams | ReadableStream` parameters. + +If you use other request adapters, you can also pass the parameters they support. + +## Request behavior + +In [RSM](/about/RSM), request behavior is used to describe how the request will be processed. + +### Timeout + +Set the request timeout. + +```javascript +// Request timeout at request level +alovaInstance.Get('/todo/list', { + // ... + // highlight-start + timeout: 10000 + // highlight-end +}); +``` + +### Request sharing + +We always encounter this situation. When a request is sent but not responded, the same request is sent again, which causes request waste or repeated submission problems, such as the following three scenarios: + +1. A component will obtain initialization data when it is created. When a page renders multiple components at the same time, multiple identical requests will be sent at the same time; + +2. The submit button is not disabled, and the user clicks the submit button multiple times; + +3. When the preload page is entered before the preload is completed, multiple identical requests will be sent; + +4. Prevent repeated requests in react's StrictMode; + +Shared requests are used to solve these problems. It can not only improve the fluency of the application, but also reduce the pressure on the server. + +```mermaid +flowchart LR +classDef response fill:#a8bcff +R1[request 1] --> S1[send request] --> W1[wait for response]:::response --> RE1[receive data 1] +R2[same request as request 1] --> W1[wait for response]:::response --> RE2[receive data 1] +``` + +Request sharing is enabled by default. If you want to turn off sharing requests on a specific request, you can do this: + +```javascript +alovaInst.Get('/todo', { + // ... + // highlight-start + shareRequest: false + // highlight-end +}); +``` + +:::warning How to identify the same request + +The request method, request url, request header, url parameter, and request body of the method instance are used as unique identifiers. Identical identifiers indicate the same request, rather than comparing the reference address of the method instance. + +::: + +### Transform response data + +Sometimes we need to uniformly transform response data. We can set the `transformData` function for the method instance to transform the response data into the required structure. + +```javascript +alovaInstance.Get('/todo/list', { + // The function accepts the response data and response header data, and requires the transformed data to be returned. + transformData(rawData, headers) { + return rawData.list.map(item => { + return { + ...item, + statusText: item.done ? 'Completed' : 'In progress' + }; + }); + } +}); +``` + +### Response cache + +Response cache allows you to better utilize server-side data multiple times without sending a request to obtain data every time. The GET request will set a memory cache time of 5 minutes by default. If you don't need it, you can turn off the cache for the current request in the following way. + +```ts +alovaInstance.Get('/todo/list', { + // Set to 0 or null to turn off the default response cache + cacheFor: 0 +}); +``` + +For details, please refer to [Response Cache](/next/tutorial/cache/mode) + +## Interrupt request + +Call the `abort` of the method instance to interrupt the request. + +```javascript +const userMethod = alovaInstance.Get('/api/user'); +userMethod.then(res => { + // ... +}); + +const handleCancel = () => { + // highlight-start + userMethod.abort(); + // highlight-end +}; +``` + +## Listen for upload and download progress + +By binding the upload progress event through the `onUpload` of the method instance, and the download progress event through the `onDownload`, it will return the unbinding function. + +```javascript +const uploadMethod = alovaInstance.Post('/todo/uploadfile', formData); +const offUploadEvent = uploadMethod.onUpload(event => { + console.log('File size:', event.total); + console.log('Uploaded:', event.loaded); +}); + +uploadMethod.then(res => { + // ... +}); + +// Unbind upload callback +const handleOffEvent = () => { + offUploadEvent(); +}; +``` + +```javascript +const downloadMethod = alovaInstance.Get('/todo/downloadfile'); +const offDownloadEvent = downloadMethod.onDownload(event => { + console.log('File size:', event.total); + console.log('Downloaded:', event.loaded); +}); + +downloadMethod.then(res => { + // ... +}); + +// Unbind download callback +const handleOffEvent = () => { + offDownloadEvent(); +}; +``` + +:::warning Use `alova/fetch` adapter to pay attention to + +Due to the limitation of fetch api, the one provided by alova `alova/fetch` adapter does not support upload progress. If you need upload progress, please use [XMLHttpRequest adapter](/next/resource/request-adapter/alova-adapter-xhr) or [axios adapter](/next/resource/request-adapter/alova-adapter-axios). + +You can also write your own request adapter, see [Writing a request adapter](/next/tutorial/advanced/custom/http-adapter) for details. + +::: + +**Upload/download progress type** + +```typescript +type Progress = { + /** Total amount of data uploaded or downloaded */ + total: number; + /** Completed data */ + loaded: number; +}; +``` diff --git a/docs/tutorial/02-getting-started/04-alova.md b/docs/tutorial/02-getting-started/03-basic/04-alova.md similarity index 78% rename from docs/tutorial/02-getting-started/04-alova.md rename to docs/tutorial/02-getting-started/03-basic/04-alova.md index 6495b6ed4..93259774b 100644 --- a/docs/tutorial/02-getting-started/04-alova.md +++ b/docs/tutorial/02-getting-started/03-basic/04-alova.md @@ -1,79 +1,78 @@ ---- -title: Alova Instance -sidebar_position: 40 ---- - -Alova instances can not only create method instances of different types, but also set global parameters. The created method instances will inherit the parameters of this alova instance. When the parameters of the alova instance are set to the same parameters as the method instance, such as `timeout`, `shareRequest`, etc., the parameters of the method instance will be used first. - -Next we look at the global parameters of alova. - -## baseURL - -After setting the baseURL, you no longer need to add the same url prefix for every request. - -```javascript -const alovaInstance = createAlova({ - baseURL: 'https://api.alovajs.dev' - // ... -}); -``` - -At this point, you only need to specify pathname when creating a method instance. - -```javascript -alovaInstance.Get('/todo/list'); -``` - -## Global timeout - -The following is to set the global request timeout. - -```javascript -// Set request timeout globally -const alovaInstance = createAlova({ - // ... - // highlight-start - // Request timeout, unit is milliseconds, default is 0, which means it will never timeout - timeout: 50000 - // highlight-end -}); -``` - -## Global sharing request - -Set sharing requests globally. - -```javascript -const alovaInstance = createAlova({ - // ... - // highlight-start - shareRequest: false - // highlight-end -}); -``` - -## Request adapter - -In the previous chapter we have configured the `alova/fetch` request adapter, which will be used to send requests initiated by this alova instance. In fact, we also provide various request adapters for different JS environments. - -- [Mock request adapter](/tutorial/request-adapter/alova-mock) -- [XMLHttpRequest Adapter](/tutorial/request-adapter/alova-adapter-xhr) -- [axios adapter](/tutorial/request-adapter/alova-adapter-axios) -- [uniapp adapter](/tutorial/request-adapter/alova-adapter-uniapp) -- [taro adapter](/tutorial/request-adapter/alova-adapter-taro) - -## Global response cache - -You can also set the response cache globally: - -```ts -const alovaInstance = createAlova({ - // ... - cacheFor: { - GET: 0, // Close all GET caches - POST: 60 * 60 * 1000 // Set all POST caches for 1 hour - } -}); -``` - -For more details, please refer to [Response Cache](/tutorial/cache/mode) +--- +title: Alova Instance +--- + +Alova instances can not only create method instances of different types, but also set global parameters. The created method instances will inherit the parameters of this alova instance. When the parameters of the alova instance are set to the same parameters as the method instance, such as `timeout`, `shareRequest`, etc., the parameters of the method instance will be used first. + +Next we look at the global parameters of alova. + +## baseURL + +After setting the baseURL, you no longer need to add the same url prefix for every request. + +```javascript +const alovaInstance = createAlova({ + baseURL: 'https://api.alovajs.dev' + // ... +}); +``` + +At this point, you only need to specify pathname when creating a method instance. + +```javascript +alovaInstance.Get('/todo/list'); +``` + +## Global timeout + +The following is to set the global request timeout. + +```javascript +// Set request timeout globally +const alovaInstance = createAlova({ + // ... + // highlight-start + // Request timeout, unit is milliseconds, default is 0, which means it will never timeout + timeout: 50000 + // highlight-end +}); +``` + +## Global sharing request + +Set sharing requests globally. + +```javascript +const alovaInstance = createAlova({ + // ... + // highlight-start + shareRequest: false + // highlight-end +}); +``` + +## Request adapter + +In the previous chapter we have configured the `alova/fetch` request adapter, which will be used to send requests initiated by this alova instance. In fact, we also provide various request adapters for different JS environments. + +- [Mock request adapter](/next/resource/request-adapter/alova-mock) +- [XMLHttpRequest Adapter](/next/resource/request-adapter/alova-adapter-xhr) +- [axios adapter](/next/resource/request-adapter/alova-adapter-axios) +- [uniapp adapter](/next/resource/request-adapter/alova-adapter-uniapp) +- [taro adapter](/next/resource/request-adapter/alova-adapter-taro) + +## Global response cache + +You can also set the response cache globally: + +```ts +const alovaInstance = createAlova({ + // ... + cacheFor: { + GET: 0, // Close all GET caches + POST: 60 * 60 * 1000 // Set all POST caches for 1 hour + } +}); +``` + +For more details, please refer to [Response Cache](/next/tutorial/cache/mode) diff --git a/docs/tutorial/02-getting-started/05-global-interceptor.md b/docs/tutorial/02-getting-started/03-basic/05-global-interceptor.md similarity index 98% rename from docs/tutorial/02-getting-started/05-global-interceptor.md rename to docs/tutorial/02-getting-started/03-basic/05-global-interceptor.md index bbf902e2f..78a5b2343 100644 --- a/docs/tutorial/02-getting-started/05-global-interceptor.md +++ b/docs/tutorial/02-getting-started/03-basic/05-global-interceptor.md @@ -1,6 +1,5 @@ --- title: Global Interceptor -sidebar_position: 50 --- import Tabs from '@theme/Tabs'; @@ -71,7 +70,7 @@ const alovaInstance = createAlova({ responded: { // highlight-start // Interceptor for successful request - // When using the GlobalFetch request adapter, the first parameter receives the Response object + // When using the alova/fetch request adapter, the first parameter receives the Response object // The second parameter is the method instance of the current request. You can use it to synchronize the configuration information before and after the request. onSuccess: async (response, method) => { if (response.status >= 400) { diff --git a/docs/tutorial/02-getting-started/06-method-metadata.md b/docs/tutorial/02-getting-started/03-basic/06-method-metadata.md similarity index 96% rename from docs/tutorial/02-getting-started/06-method-metadata.md rename to docs/tutorial/02-getting-started/03-basic/06-method-metadata.md index 27d9074ab..75f14ca0e 100644 --- a/docs/tutorial/02-getting-started/06-method-metadata.md +++ b/docs/tutorial/02-getting-started/03-basic/06-method-metadata.md @@ -1,151 +1,150 @@ ---- -title: Method Metadata -sidebar_position: 60 ---- - -Method instances run through the entire request life cycle of alova, and there will be a large number of different method instances in the project. Sometimes we need to add additional information to specific method instances to facilitate their identification or additional information transfer. Wait, at this point, we need to use method metadata. - -## Use metadata to identify identities - -### Use identity before request - -For example, most of the interfaces in your project need to be accompanied by `token` for each request, but there are still some interfaces that do not require verification. You may handle them uniformly in the global `beforeRequest` function. - -```javascript -const nonvalidateRequiredApi = [ - '/api/url1', - '/api/url2', - '/api/url3' - // ... -]; - -createAlova({ - beforeRequest(method) { - if (!nonvalidateRequiredApi.includes(method.url)) { - method.config.headers.token = '...'; - } - } -}); -``` - -This will cause the following two problems: - -1. Information is not aggregated with method instances, making maintainability even worse; -2. Coding is more troublesome; - -To solve these two problems, we will use metadata to identify a specific method instance when it is created. - -**Step 1: Define metadata when creating method instance** - -```javascript -const loginAPI = (username, password) => { - const methodInstance = alovaInst.Post('/login', { - username, - password - }); - methodInstance.meta = { - ignoreToken: true - }; - return methodInstance; -}; -``` - -And you can also directly define metadata in config - -```javascript -const loginAPI = (username, password) => { - return alovaInst.Post( - '/login', - { - username, - password - }, - { - meta: { - ignoreToken: true - } - } - ); -}; -``` - -**Step 2: Use metadata as the basis for judgment in `beforeRequest`** - -```javascript -createAlova({ - // ... - beforeRequest(method) { - if (!method.meta?.ignoreToken) { - method.config.headers.token = '...'; - } - } -}); -``` - -### Use the identity after the response - -This method can also be used in global `responded`. For example, in most cases, the request api will return json data, but there may be a file download interface, which will return a binary data stream. In this case Below, you can use different metadata in `responded` to handle different responses separately. - -**Step one: When creating a method instance, you also need to assign a metadata** - -```javascript -const downloadAPI = filePath => { - const methodInstance = alovaInst.Post('/download_file', { - filePath - }); - methodInstance.meta = { - isDownload: true - }; - return methodInstance; -}; -``` - -**Step 2: Use metadata in `responded` as a basis for judgment** - -```javascript -createAlova({ - // ... - responded: - onSuccess: (response, method) => method.meta?.isDownload ? response.blob() : response.json() - onError: (error, method) => { - //Metadata of method instances can also be accessed when responding to errors - } - } -}); -``` - -## Use metadata to pass information - -In some cases, if you want to add additional information to different method instances for use elsewhere, you can also use metadata to save it. Take uniformly generating different method instance IDs as an example. - -```javascript -createAlova({ - beforeRequest(method) { - if (!method.meta.generateId) { - method.meta.uid = generateUUID(); - } - }, - - responded: { - onSuccess(response, method) { - // Access the meta data generated by the current method when the request is successful. - const currentMethodUID = method.meta.uid; - }, - onError(error, method) { - //Access the meta data generated by the current method when the request fails. - const currentMethodUID = method.meta.uid; - } - } -}); -``` - -## Tips for non-typescript projects - -In a non-typescript environment, you can use any attribute as an information carrier, not limited to the `meta` attribute. - -```javascript -methodInstance.showResponseMsg = true; -methodInstance.others = 'abc'; -``` - -Only in the typescript environment, any attribute name will report that the attribute "$0" does not exist. ts(2339)`, so in the type we specify the `meta` attribute as the information carrier. +--- +title: Method Metadata +--- + +Method instances run through the entire request life cycle of alova, and there will be a large number of different method instances in the project. Sometimes we need to add additional information to specific method instances to facilitate their identification or additional information transfer. Wait, at this point, we need to use method metadata. + +## Use metadata to identify identities + +### Use identity before request + +For example, most of the interfaces in your project need to be accompanied by `token` for each request, but there are still some interfaces that do not require verification. You may handle them uniformly in the global `beforeRequest` function. + +```javascript +const nonvalidateRequiredApi = [ + '/api/url1', + '/api/url2', + '/api/url3' + // ... +]; + +createAlova({ + beforeRequest(method) { + if (!nonvalidateRequiredApi.includes(method.url)) { + method.config.headers.token = '...'; + } + } +}); +``` + +This will cause the following two problems: + +1. Information is not aggregated with method instances, making maintainability even worse; +2. Coding is more troublesome; + +To solve these two problems, we will use metadata to identify a specific method instance when it is created. + +**Step 1: Define metadata when creating method instance** + +```javascript +const loginAPI = (username, password) => { + const methodInstance = alovaInst.Post('/login', { + username, + password + }); + methodInstance.meta = { + ignoreToken: true + }; + return methodInstance; +}; +``` + +And you can also directly define metadata in config + +```javascript +const loginAPI = (username, password) => { + return alovaInst.Post( + '/login', + { + username, + password + }, + { + meta: { + ignoreToken: true + } + } + ); +}; +``` + +**Step 2: Use metadata as the basis for judgment in `beforeRequest`** + +```javascript +createAlova({ + // ... + beforeRequest(method) { + if (!method.meta?.ignoreToken) { + method.config.headers.token = '...'; + } + } +}); +``` + +### Use the identity after the response + +This method can also be used in global `responded`. For example, in most cases, the request api will return json data, but there may be a file download interface, which will return a binary data stream. In this case Below, you can use different metadata in `responded` to handle different responses separately. + +**Step one: When creating a method instance, you also need to assign a metadata** + +```javascript +const downloadAPI = filePath => { + const methodInstance = alovaInst.Post('/download_file', { + filePath + }); + methodInstance.meta = { + isDownload: true + }; + return methodInstance; +}; +``` + +**Step 2: Use metadata in `responded` as a basis for judgment** + +```javascript +createAlova({ + // ... + responded: + onSuccess: (response, method) => method.meta?.isDownload ? response.blob() : response.json() + onError: (error, method) => { + //Metadata of method instances can also be accessed when responding to errors + } + } +}); +``` + +## Use metadata to pass information + +In some cases, if you want to add additional information to different method instances for use elsewhere, you can also use metadata to save it. Take uniformly generating different method instance IDs as an example. + +```javascript +createAlova({ + beforeRequest(method) { + if (!method.meta.generateId) { + method.meta.uid = generateUUID(); + } + }, + + responded: { + onSuccess(response, method) { + // Access the meta data generated by the current method when the request is successful. + const currentMethodUID = method.meta.uid; + }, + onError(error, method) { + //Access the meta data generated by the current method when the request fails. + const currentMethodUID = method.meta.uid; + } + } +}); +``` + +## Tips for non-typescript projects + +In a non-typescript environment, you can use any attribute as an information carrier, not limited to the `meta` attribute. + +```javascript +methodInstance.showResponseMsg = true; +methodInstance.others = 'abc'; +``` + +Only in the typescript environment, any attribute name will report that the attribute "$0" does not exist. ts(2339)`, so in the type we specify the `meta` attribute as the information carrier. diff --git a/docs/tutorial/02-getting-started/07-combine-framework.md b/docs/tutorial/02-getting-started/03-basic/07-combine-framework.md similarity index 95% rename from docs/tutorial/02-getting-started/07-combine-framework.md rename to docs/tutorial/02-getting-started/03-basic/07-combine-framework.md index 51d25ac6a..2f5ab96b8 100644 --- a/docs/tutorial/02-getting-started/07-combine-framework.md +++ b/docs/tutorial/02-getting-started/03-basic/07-combine-framework.md @@ -1,6 +1,5 @@ --- title: Combine UI framework -sidebar_position: 70 --- import Tabs from '@theme/Tabs'; @@ -94,7 +93,7 @@ useRequest means sending a request. By default, a request will be sent when call -[When to use useRequest and when to send a request via `await alovaInstance.Get`](/tutorial/best-practice/skills). +[When to use useRequest and when to send a request via `await alovaInstance.Get`](/next/tutorial/project/best-practice/skills). :::warning useHook usage specification @@ -192,7 +191,7 @@ When no error is thrown, the next node receives the return value of the previous ### Transform response data -In [method detailed explanation](/tutorial/getting-started/method), we have already learned about `transformData`, which is also very useful when used in useHook. It allows useHook's data to receive the transformed data without transform again. +In [method detailed explanation](/next/tutorial/getting-started/basic/method), we have already learned about `transformData`, which is also very useful when used in useHook. It allows useHook's data to receive the transformed data without transform again. ```javascript const todoListGetter = alovaInstance.Get('/todo/list', { @@ -281,4 +280,4 @@ The above is the basic use of our most commonly used `useRequest`. Other commonl 3. useAutoRequest: automatically request according to rules such as timed polling, browser focus, network reconnection, etc. 4. ... -For complete usage or other client request strategies, please move to [Client Strategy](/xxx) to view all client request strategies provided by alova. +For complete usage or other client request strategies, please move to [Client Strategy](/next/tutorial/client/strategy) to view all client request strategies provided by alova. diff --git a/docs/tutorial/02-getting-started/08-server.md b/docs/tutorial/02-getting-started/03-basic/08-server.md similarity index 90% rename from docs/tutorial/02-getting-started/08-server.md rename to docs/tutorial/02-getting-started/03-basic/08-server.md index fe8947d9f..f38fa6a59 100644 --- a/docs/tutorial/02-getting-started/08-server.md +++ b/docs/tutorial/02-getting-started/03-basic/08-server.md @@ -1,9 +1,8 @@ --- title: Server Usage -sidebar_position: 80 --- -As mentioned in the previous [Quick Start](/tutorial/getting-started/quick-start), alova can be used in server-side environments such as `nodejs/deno/bun`. You can use all functions except client request strategies on the server. In addition, alova also provides good support for server-side environments. +As mentioned in the previous [Quick Start](/next/tutorial/getting-started/quick-start), alova can be used in server-side environments such as `nodejs/deno/bun`. You can use all functions except client request strategies on the server. In addition, alova also provides good support for server-side environments. ```js import { createAlova } from 'alova'; @@ -68,7 +67,7 @@ const result = await sendCaptcha( ); ``` -> For more `Server hooks`, please visit [Server Strategy](/xxx). +> For more `Server hooks`, please visit [Server Strategy](/next/tutorial/server/strategy). ## Multi-level Cache @@ -147,4 +146,4 @@ const alovaInstance = createAlova({ }); ``` -> For a deeper understanding of response caching, please visit [In-depth Response Cache](/xxx). +> For a deeper understanding of response caching, please visit [Cache Details](/next/tutorial/cache). diff --git a/docs/tutorial/02-getting-started/03-basic/_category_.json b/docs/tutorial/02-getting-started/03-basic/_category_.json new file mode 100644 index 000000000..c000b83bf --- /dev/null +++ b/docs/tutorial/02-getting-started/03-basic/_category_.json @@ -0,0 +1,3 @@ +{ + "label": "Basic" +} diff --git a/docs/tutorial/02-getting-started/09-extension-integration.md b/docs/tutorial/02-getting-started/09-extension-integration.md index 65da6b1fd..b091b02f2 100644 --- a/docs/tutorial/02-getting-started/09-extension-integration.md +++ b/docs/tutorial/02-getting-started/09-extension-integration.md @@ -1,6 +1,5 @@ --- title: Extension integration -sidebar_position: 90 --- Integrating Alova's editor extension can make it more powerful. diff --git a/docs/tutorial/02-getting-started/10-congratulations.md b/docs/tutorial/02-getting-started/10-congratulations.md index b9523c239..7f92b6c90 100644 --- a/docs/tutorial/02-getting-started/10-congratulations.md +++ b/docs/tutorial/02-getting-started/10-congratulations.md @@ -1,6 +1,5 @@ --- title: Conclusion -sidebar_position: 100 --- import NavCard from '@site/src/components/NavCard'; @@ -11,16 +10,16 @@ import NavCard from '@site/src/components/NavCard'; { title: 'Client Strategy', desc: 'Select the strategy module you need to quickly implement complex requests', -link: '/category/request-adapter' +link: '/next/tutorial/client/strategy' }, { title: 'Server Strategy', desc: 'Select the strategy module you need to quickly implement complex requests', -link: '/category/request-adapter' +link: '/next/tutorial/server/strategy' }, { title: 'Best Practices', desc: 'Use Alova skills summarized in practice in projects', -link: '/category/best-practice' +link: '/next/tutorial/project/best-practice' } ]}> diff --git a/docs/tutorial/02-getting-started/_category_.json b/docs/tutorial/02-getting-started/_category_.json index 6a7cbc87e..ac034c06d 100644 --- a/docs/tutorial/02-getting-started/_category_.json +++ b/docs/tutorial/02-getting-started/_category_.json @@ -1,3 +1,5 @@ -{ - "label": "Getting Started" -} +{ + "label": "Getting Started", + "collapsed": false, + "collapsible": false +} diff --git a/docs/tutorial/03-client/01-use-request.md b/docs/tutorial/03-client/01-strategy/01-use-request.md similarity index 93% rename from docs/tutorial/03-client/01-use-request.md rename to docs/tutorial/03-client/01-strategy/01-use-request.md index d90b8fceb..8761bd35f 100644 --- a/docs/tutorial/03-client/01-use-request.md +++ b/docs/tutorial/03-client/01-strategy/01-use-request.md @@ -1,6 +1,5 @@ --- title: Auto Manage States -sidebar_position: 10 --- import Tabs from '@theme/Tabs'; @@ -16,7 +15,7 @@ useRequest indicates the sending of a request. By default, a request will be sen ## Usage -Its basic usage has been introduced in detail in [Combining UI Framework](/tutorial/getting-started/combine-framework). +Its basic usage has been introduced in detail in [Basic - Combining UI Framework](/next/tutorial/getting-started/basic/combine-framework). ### Set initial data @@ -155,7 +154,7 @@ send(1); #### Received in force function -force is used to specify whether to penetrate the response cache. The content about response cache will be explained in the [cache mode](/tutorial/cache/mode) later. +force is used to specify whether to penetrate the response cache. The content about response cache will be explained in the [cache mode](/next/tutorial/cache/mode) later. ```javascript const { send } = useRequest(alovaInstance.Get('/todo'), { @@ -202,7 +201,7 @@ abort(); ### Additional managed states -Set additional managed states, which can be updated across components. For details, please refer to [Additional managed states](/tutorial/managed-state). +Set additional managed states, which can be updated across components. For details, please refer to [Additional managed states](/next/tutorial/client/in-depth/manage-extra-states). ### Middleware @@ -220,8 +219,8 @@ useRequest(todoListGetter, { }); ``` -For details, please refer to [In-depth Client-Middleware](/tutorial/middleware). +For details, please refer to [In-depth Client-Middleware](/next/tutorial/client/in-depth/middleware). ## API -Please refer to [API-useRequest](/api/core-hooks#userequest). +Please refer to [API-useRequest](/next/api/core-hooks#userequest). diff --git a/docs/tutorial/03-client/02-use-watcher.md b/docs/tutorial/03-client/01-strategy/02-use-watcher.md similarity index 96% rename from docs/tutorial/03-client/02-use-watcher.md rename to docs/tutorial/03-client/01-strategy/02-use-watcher.md index 1f49972f1..235f78d26 100644 --- a/docs/tutorial/03-client/02-use-watcher.md +++ b/docs/tutorial/03-client/01-strategy/02-use-watcher.md @@ -1,6 +1,5 @@ --- title: Watching Request -sidebar_position: 20 --- import Tabs from '@theme/Tabs'; @@ -44,7 +43,7 @@ Next, let's take the search for todo items as an example and try to change the o :::tip Usage Tips -useWatcher supports all the features of useRequest. For details, please see [useRequest](/tutorial/client/use-request). The following are the unique uses of useWatcher. +useWatcher supports all the features of useRequest. For details, please see [useRequest](/next/tutorial/client/strategy/use-request). The following are the unique uses of useWatcher. ::: @@ -145,4 +144,4 @@ useWatcher( ## API -Please refer to [API-useWatcher](/api/core-hooks#usewatcher). +Please refer to [API-useWatcher](/next/api/core-hooks#usewatcher). diff --git a/docs/tutorial/03-client/03-use-fetcher.md b/docs/tutorial/03-client/01-strategy/03-use-fetcher.md similarity index 96% rename from docs/tutorial/03-client/03-use-fetcher.md rename to docs/tutorial/03-client/01-strategy/03-use-fetcher.md index 7eccb58ed..201febcde 100644 --- a/docs/tutorial/03-client/03-use-fetcher.md +++ b/docs/tutorial/03-client/01-strategy/03-use-fetcher.md @@ -1,6 +1,5 @@ --- title: Fetch Data -sidebar_position: 30 --- import Tabs from '@theme/Tabs'; @@ -183,7 +182,7 @@ The above example is set `updateState` to false when calling `useFetcher`. This ## Update views across modules/components -Next, we will modify a todo data and re-fetch the latest todo list data to update the view. We may not know which page the todo list is currently on. In this case, when using the `fetch` function, we can use [Method snapshots matcher](/tutorial/advanced/method-matcher) to dynamically fetch the data of the current page. +Next, we will modify a todo data and re-fetch the latest todo list data to update the view. We may not know which page the todo list is currently on. In this case, when using the `fetch` function, we can use [Method snapshots matcher](/next/tutorial/client/in-depth/method-matcher) to dynamically fetch the data of the current page. > The Method snapshots matcher is used to find method instances that meet the conditions among the requested method instances. @@ -232,8 +231,8 @@ useFetcher only updates the cache after the request is completed, and if this Me ::: -> For more methods of using `Method` instance matcher, see [Method instance matcher](/tutorial/advanced/method-matcher). +> For more methods of using `Method` instance matcher, see [Method instance matcher](/next/tutorial/client/in-depth/method-matcher). ## Force sending request -Same as `useRequest` and `useWatcher`, please read [Force Request](/tutorial/cache/force-request) for more information. +Same as `useRequest` and `useWatcher`, please read [Force Request](/next/tutorial/cache/force-request) for more information. diff --git a/docs/tutorial/03-client/04-use-pagination.md b/docs/tutorial/03-client/01-strategy/04-use-pagination.md similarity index 96% rename from docs/tutorial/03-client/04-use-pagination.md rename to docs/tutorial/03-client/01-strategy/04-use-pagination.md index 380797923..96200e558 100644 --- a/docs/tutorial/03-client/04-use-pagination.md +++ b/docs/tutorial/03-client/01-strategy/04-use-pagination.md @@ -1,6 +1,5 @@ --- title: Pagination request strategy -sidebar_position: 40 --- import Tabs from '@theme/Tabs'; @@ -16,11 +15,11 @@ use hook A hook designed for paging scenarios, which can help you automatically manage paging data, preload data, reduce unnecessary data refresh, improve fluency by 300%, and reduce coding difficulty by 50%\*\*. You can use it in the two paging scenarios of pull-down loading and page number flipping. This hook provides a wealth of features to help your application create better performance and more convenient paging functions. -## Example + ## Features @@ -483,7 +482,7 @@ const App = () => { -Same as `useWatcher`, you can also implement request debounce by specifying `debounce`, for details, please refer to [useWatcher's debounce parameter setting](/api/core-hooks#usewatcher). +Same as `useWatcher`, you can also implement request debounce by specifying `debounce`, for details, please refer to [useWatcher's debounce parameter setting](/next/api/core-hooks#usewatcher). ```javascript usePagination((page, pageSize) => queryStudents(page, pageSize, studentName, clsName), { @@ -494,7 +493,7 @@ usePagination((page, pageSize) => queryStudents(page, pageSize, studentName, cls }); ``` -It should be noted that `debounce` is achieved by request debounce in [**useWatcher**](/api/core-hooks#usewatcher). **At the end of the monitoring state, there are two hidden monitoring states of page and pageSize, which can also be set by debounce. ** +It should be noted that `debounce` is achieved by request debounce in [**useWatcher**](/next/api/core-hooks#usewatcher). **At the end of the monitoring state, there are two hidden monitoring states of page and pageSize, which can also be set by debounce. ** For example, when `watchingStates` is set to `[studentName, clsName]`, `[studentName, clsName, page, pageSize]` will be monitored internally, so if you need to set anti-shake for page and pageSize, you can specify ` [0, 0, 500, 500]`. @@ -623,7 +622,7 @@ In append mode, you can specify the parameter of `refresh` as a list item. When ### Manually update list data -Use the `update` function to update responsive data, which is similar to [useRequest's update](/tutorial/combine-framework/use-request), the only difference is that when calling `update` to update `data`, the list data is updated, while non-response data. This is useful when manually clearing list data without reissuing the request. +Use the `update` function to update responsive data, which is similar to [useRequest's update](/next/tutorial/client/strategy/use-request), the only difference is that when calling `update` to update `data`, the list data is updated, while non-response data. This is useful when manually clearing list data without reissuing the request. ```typescript // case list data @@ -647,7 +646,7 @@ declare function reload(): void; ### Hook configuration -Inherit all configurations of [**useWatcher**](/api/core-hooks#usewatcher). +Inherit all configurations of [**useWatcher**](/next/api/core-hooks#usewatcher). | Name | Description | Type | Default | Version | | ------------------- | --------------------------------------------------------------------- | ------------------------- | -------------------------- | ------- | @@ -663,7 +662,7 @@ Inherit all configurations of [**useWatcher**](/api/core-hooks#usewatcher). ### Responsive data -Inherit all responsive data from [**useWatcher**](/api/core-hooks#usewatcher). +Inherit all responsive data from [**useWatcher**](/next/api/core-hooks#usewatcher). | Name | Description | Type | Version | | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ------- | @@ -677,7 +676,7 @@ Inherit all responsive data from [**useWatcher**](/api/core-hooks#usewatcher). ### Action function -Inherit all action functions of [**useWatcher**](/api/core-hooks#usewatcher). +Inherit all action functions of [**useWatcher**](/next/api/core-hooks#usewatcher). | name | description | function parameters | return value | version | | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | ------- | @@ -690,7 +689,7 @@ Inherit all action functions of [**useWatcher**](/api/core-hooks#usewatcher). ### Event -Inherit all events from [**useWatcher**](/api/core-hooks#usewatcher). +Inherit all events from [**useWatcher**](/next/api/core-hooks#usewatcher). | Name | Description | Callback Parameters | Version | | --------------- | ---------------------------------------------- | ------------------------------------ | ------- | diff --git a/docs/tutorial/03-client/05-use-form.md b/docs/tutorial/03-client/01-strategy/05-use-form.md similarity index 96% rename from docs/tutorial/03-client/05-use-form.md rename to docs/tutorial/03-client/01-strategy/05-use-form.md index 5baa7eb2f..030c0ef23 100644 --- a/docs/tutorial/03-client/05-use-form.md +++ b/docs/tutorial/03-client/01-strategy/05-use-form.md @@ -1,6 +1,5 @@ --- title: Form submiting strategy -sidebar_position: 50 --- import Tabs from '@theme/Tabs'; @@ -16,9 +15,9 @@ use hook A hook designed for form submission. Through this hook, you can easily implement form drafts and multi-page (multi-step) forms. In addition, it also provides common functions such as form reset. -## Example + ## Features @@ -414,7 +413,7 @@ const returnStates = useForm(submitData, { In this way, no matter which component is rendered first, the form with the id of testForm can be initialized, and the subsequent components will first use the initialized form data when encountering the id of testForm, and will not initialize again. This way you can initialize form data inside any component. -> More detailed multi-step forms can also be experienced and viewed in [Form Submission Demo](/tutorial/example/form-hook). + ### Conditional filter @@ -431,7 +430,7 @@ const { send: searchData } = useForm(queryCity, { :::warning Conditional Restrictions -In conditional filtering scenarios, `useForm` is more suitable for non-paginated list conditional queries. If you need to perform conditional queries in paginated lists, it is recommended to use [Pagination Request Strategy (usePagination)](/tutorial/strategy/usePagination). +In conditional filtering scenarios, `useForm` is more suitable for non-paginated list conditional queries. If you need to perform conditional queries in paginated lists, it is recommended to use [Pagination Request Strategy (usePagination)](/next/tutorial/client/strategy/use-pagination). ::: @@ -439,7 +438,7 @@ In conditional filtering scenarios, `useForm` is more suitable for non-paginated ### Hook configuration -Inherit all configurations from [**useRequest**](/api/core-hooks#userequest). +Inherit all configurations from [**useRequest**](/next/api/core-hooks#userequest). | Name | Description | Type | Default | Version | | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- | ------- | ------- | @@ -450,7 +449,7 @@ Inherit all configurations from [**useRequest**](/api/core-hooks#userequest). ### Responsive data -Inherit all responsive data from [**useRequest**](/api/core-hooks#userequest). +Inherit all responsive data from [**useRequest**](/next/api/core-hooks#userequest). | Name | Description | Type | Version | | ---- | ----------------------------------- | ---- | ------- | @@ -472,7 +471,7 @@ Inherit all responsive data from [**useRequest**](/api/core-hooks#userequest). ### Action function -Inherit all action functions of [**useRequest**](/api/core-hooks#userequest). +Inherit all action functions of [**useRequest**](/next/api/core-hooks#userequest). | name | description | function parameters | return value | version | | ---------- | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------ | ------------ | ------- | @@ -481,7 +480,7 @@ Inherit all action functions of [**useRequest**](/api/core-hooks#userequest). ### Event -Inherit all events from [**useRequest**](/api/core-hooks#userequest). +Inherit all events from [**useRequest**](/next/api/core-hooks#userequest). | Name | Description | Callback Parameters | Version | | --------- | ----------------------------------------------- | ------------------- | ------- | diff --git a/docs/tutorial/03-client/06-token-authentication.md b/docs/tutorial/03-client/01-strategy/06-token-authentication.md similarity index 97% rename from docs/tutorial/03-client/06-token-authentication.md rename to docs/tutorial/03-client/01-strategy/06-token-authentication.md index bc692b559..041f13429 100644 --- a/docs/tutorial/03-client/06-token-authentication.md +++ b/docs/tutorial/03-client/01-strategy/06-token-authentication.md @@ -1,6 +1,5 @@ --- title: Token authentication interceptor -sidebar_position: 60 --- import Tabs from '@theme/Tabs'; @@ -10,8 +9,6 @@ import TabItem from '@theme/TabItem'; Interceptor -Version requirements: v1.3.0+ - ::: > Before using extension hooks, make sure you are familiar with the basic use of alova. @@ -114,7 +111,7 @@ const alovaInstance = createAlova({ :::warning -When you use the `GlobalFetch` adapter, you may encounter the problem `TypeError: Failed to execute 'json' on 'Response': body stream already read`. This is because the `body stream` of `Response` can only be accessed once, you can `response.clone().json()` to solve it. +When you use the `alova/fetch` adapter, you may encounter the problem `TypeError: Failed to execute 'json' on 'Response': body stream already read`. This is because the `body stream` of `Response` can only be accessed once, you can `response.clone().json()` to solve it. ::: @@ -154,7 +151,7 @@ createClientTokenAuthentication({ ::: -> For more information about metadata, go to [method metadata](/tutorial/getting-started/method-metadata). +> For more information about metadata, go to [method metadata](/next/tutorial/getting-started/basic/method-metadata). ```javascript export const refreshToken = () => { @@ -172,7 +169,7 @@ The same as refreshing the token silently on the client, just specify whether th ### Processed in request success interceptor -When using `GlobalFetch`, as long as the server returns the response data, the response success interceptor will be triggered. At this time, we need to handle the token refresh in the response success interceptor. +When using `alova/fetch`, as long as the server returns the response data, the response success interceptor will be triggered. At this time, we need to handle the token refresh in the response success interceptor. ```javascript createServerTokenAuthentication({ @@ -237,7 +234,7 @@ createServerTokenAuthentication({ ::: -> For more information about metadata, please go to [method metadata](/tutorial/getting-started/method-metadata). +> For more information about metadata, please go to [method metadata](/next/tutorial/getting-started/basic/method-metadata). ```javascript export const refreshToken = () => { @@ -471,7 +468,7 @@ export const logout = () => { ## Typescript -By default, `createClientServerTokenAuthentication` and `createServerTokenAuthentication` are adapted to the `GlobalFetch` request adapter, you can only specify the type of `StatesHook`, as follows: +By default, `createClientServerTokenAuthentication` and `createServerTokenAuthentication` are adapted to the `alova/fetch` request adapter, you can only specify the type of `StatesHook`, as follows: ```typescript // highlight-start @@ -494,7 +491,7 @@ const alovaInstance = createAlova({ }); ``` -If you are not using the `GlobalFetch` request adapter, You also need to specify the type of request adapter, which is also simple. +If you are not using the `alova/fetch` request adapter, You also need to specify the type of request adapter, which is also simple. The following is an example of the axios request adapter. Specify the request adapter type in `createClientTokenAuthentication`. diff --git a/docs/tutorial/03-client/07-use-auto-request.md b/docs/tutorial/03-client/01-strategy/07-use-auto-request.md similarity index 92% rename from docs/tutorial/03-client/07-use-auto-request.md rename to docs/tutorial/03-client/01-strategy/07-use-auto-request.md index 5db16896b..2a3941c65 100644 --- a/docs/tutorial/03-client/07-use-auto-request.md +++ b/docs/tutorial/03-client/01-strategy/07-use-auto-request.md @@ -1,6 +1,5 @@ --- title: Automatically refetch data -sidebar_position: 70 --- import Tabs from '@theme/Tabs'; @@ -32,9 +31,9 @@ import { useAutoRequest } from 'alova/client'; const { loading, data, error } = useAutoRequest(() => method()); ``` -The return value of `useAutoRequest` is the same as [useRequest](/api/core-hooks#userequest). +The return value of `useAutoRequest` is the same as [useRequest](/next/api/core-hooks#userequest). -In addition to supporting all configuration parameters of [useRequest](/api/core-hooks#userequest), it also supports automatically fetched configuration parameters. You can turn on or off some events through the following configuration, or modify request throttling events. +In addition to supporting all configuration parameters of [useRequest](/next/api/core-hooks#userequest), it also supports automatically fetched configuration parameters. You can turn on or off some events through the following configuration, or modify request throttling events. ```javascript const { loading, data, error, onSuccess, onError, onComplete } = useAutoRequest( @@ -77,7 +76,7 @@ const { loading, data, error, onSuccess, onError, onComplete } = useAutoRequest( :::warning caching advice -It is recommended to turn off the cache of the corresponding request when using `useAutoRequest`, because when the cache is set, the cache will be hit when the automatic request is triggered and the newest data cannot be obtained. Please read [Cache Mode](/tutorial/cache/mode) for details. +It is recommended to turn off the cache of the corresponding request when using `useAutoRequest`, because when the cache is set, the cache will be hit when the automatic request is triggered and the newest data cannot be obtained. Please read [Cache Mode](/next/tutorial/cache/mode) for details. ::: diff --git a/docs/tutorial/03-client/08-action-delegation-middleware.md b/docs/tutorial/03-client/01-strategy/08-action-delegation-middleware.md similarity index 52% rename from docs/tutorial/03-client/08-action-delegation-middleware.md rename to docs/tutorial/03-client/01-strategy/08-action-delegation-middleware.md index 294f7163f..02e1df984 100644 --- a/docs/tutorial/03-client/08-action-delegation-middleware.md +++ b/docs/tutorial/03-client/01-strategy/08-action-delegation-middleware.md @@ -1,6 +1,5 @@ --- title: Cross components to trigger request -sidebar_position: 80 --- import Tabs from '@theme/Tabs'; @@ -18,9 +17,9 @@ In the past, if you want to trigger a request in another component in one compon For example, after updating the menu data in a component, you can re-trigger the re-request of the side menu bar to refresh the data. When the list data is manipulated, the list update is triggered. -## Example + ## Features @@ -139,11 +138,11 @@ Although the action functions delegated by most hooks are the same as the action ### useRequest -| name | description | function parameters | return value | version | -| ------ | ------------------------------------------------------- | ------------------- | ------------ | ------- | -| send | Same as [useRequset](/api/core-hooks#userequest).send | | | - | -| abort | Same as [useRequset](/api/core-hooks#userequest).abort | | | - | -| update | Same as [useRequset](/api/core-hooks#userequest).update | | | - | +| name | description | function parameters | return value | version | +| ------ | ------------------------------------------------------------ | ------------------- | ------------ | ------- | +| send | Same as [useRequset](/next/api/core-hooks#userequest).send | | | - | +| abort | Same as [useRequset](/next/api/core-hooks#userequest).abort | | | - | +| update | Same as [useRequset](/next/api/core-hooks#userequest).update | | | - | ### useWatcher @@ -151,23 +150,23 @@ Same as [useRequest delegate list](#userequest). ### useFetcher -| name | description | function parameters | return value | version | -| ------ | ------------------------------------------------------- | ------------------- | ------------ | ------- | -| fetch | Same as [useFetcher](/api/core-hooks#usefetcher).fetch | | | - | -| abort | Same as [useFetcher](/api/core-hooks#usefetcher).abort | | | - | -| update | Same as [useFetcher](/api/core-hooks#usefetcher).update | | | - | +| name | description | function parameters | return value | version | +| ------ | ------------------------------------------------------------ | ------------------- | ------------ | ------- | +| fetch | Same as [useFetcher](/next/api/core-hooks#usefetcher).fetch | | | - | +| abort | Same as [useFetcher](/next/api/core-hooks#usefetcher).abort | | | - | +| update | Same as [useFetcher](/next/api/core-hooks#usefetcher).update | | | - | ### usePagination -| name | description | function parameters | return value | version | -| -------- | -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | ------------------------------------------ | ------- | -| refresh | For details, see [usePagination action function](/tutorial/strategy/usePagination#api) | | | - | -| insert | For details, see [usePagination action function](/tutorial/strategy/usePagination#api) | | | - | -| remove | For details, see [usePagination action function](/tutorial/strategy/usePagination#api) | | | - | -| replace | For details, see [usePagination action function](/tutorial/strategy/usePagination#api) | | | - | -| reload | For details, see [usePagination action function](/tutorial/strategy/usePagination#api) | | | - | -| update | For details, see [usePagination action function](/tutorial/strategy/usePagination#api) | | | - | -| getState | Get paging related data by name | stateKey: 'page' \| 'pageSize' \| 'data' \| 'pageCount' \| 'total' \| 'isLastPage' | Corresponding to the value of the statekey | - | +| name | description | function parameters | return value | version | +| -------- | --------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | ------------------------------------------ | ------- | +| refresh | For details, see [usePagination action function](/next/tutorial/client/strategy/use-pagination#api) | | | - | +| insert | For details, see [usePagination action function](/next/tutorial/client/strategy/use-pagination#api) | | | - | +| remove | For details, see [usePagination action function](/next/tutorial/client/strategy/use-pagination#api) | | | - | +| replace | For details, see [usePagination action function](/next/tutorial/client/strategy/use-pagination#api) | | | - | +| reload | For details, see [usePagination action function](/next/tutorial/client/strategy/use-pagination#api) | | | - | +| update | For details, see [usePagination action function](/next/tutorial/client/strategy/use-pagination#api) | | | - | +| getState | Get paging related data by name | stateKey: 'page' \| 'pageSize' \| 'data' \| 'pageCount' \| 'total' \| 'isLastPage' | Corresponding to the value of the statekey | - | ### useSQRequest @@ -175,13 +174,13 @@ Same as [useRequest delegate list](#userequest). ### useForm -| name | description | function parameters | return value | version | -| ---------- | -------------------------------------------------------------------------- | ------------------- | ------------ | ------- | -| updateForm | For details, see [useForm action function](/tutorial/strategy/useForm#api) | | | - | -| reset | For details, see [useForm action function](/tutorial/strategy/useForm#api) | | | - | -| send | Same as [useRequset](/api/core-hooks#userequest).send | | | - | -| abort | Same as [useRequset](/api/core-hooks#userequest).abort | | | - | -| update | Same as [useRequset](/api/core-hooks#userequest).update | | | - | +| name | description | function parameters | return value | version | +| ---------- | --------------------------------------------------------------------------------------- | ------------------- | ------------ | ------- | +| updateForm | For details, see [useForm action function](/next/tutorial/client/strategy/use-form#api) | | | - | +| reset | For details, see [useForm action function](/next/tutorial/client/strategy/use-form#api) | | | - | +| send | Same as [useRequset](/next/api/core-hooks#userequest).send | | | - | +| abort | Same as [useRequset](/next/api/core-hooks#userequest).abort | | | - | +| update | Same as [useRequset](/next/api/core-hooks#userequest).update | | | - | ### useCaptcha @@ -189,12 +188,12 @@ Same as [useRequest delegate list](#userequest). ### useRetriableRequest -| name | description | function parameters | return value | version | -| ------ | ------------------------------------------------------------------------------------------------- | ------------------- | ------------ | ------- | -| stop | See [useRetriableRequest action function](/tutorial/strategy/useRetriableRequest#api) for details | | | - | -| send | Same as [useRequset](/api/core-hooks#userequest).send | | | - | -| abort | Same as [useRequset](/api/core-hooks#userequest).abort | | | - | -| update | Same as [useRequset](/api/core-hooks#userequest).update | | | - | +| name | description | function parameters | return value | version | +| ------ | --------------------------------------------------------------------------------------------------------------- | ------------------- | ------------ | ------- | +| stop | See [useRetriableRequest action function](/next/tutorial/client/strategy/use-retriable-request#api) for details | | | - | +| send | Same as [useRequset](/next/api/core-hooks#userequest).send | | | - | +| abort | Same as [useRequset](/next/api/core-hooks#userequest).abort | | | - | +| update | Same as [useRequset](/next/api/core-hooks#userequest).update | | | - | ### useSerialRequest diff --git a/docs/tutorial/03-client/09-sensorless-data-interaction/02-virtual-data.md b/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/02-virtual-data.md similarity index 99% rename from docs/tutorial/03-client/09-sensorless-data-interaction/02-virtual-data.md rename to docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/02-virtual-data.md index 6c2663db4..5c1458be4 100644 --- a/docs/tutorial/03-client/09-sensorless-data-interaction/02-virtual-data.md +++ b/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/02-virtual-data.md @@ -1,6 +1,5 @@ --- title: Virtual data -sidebar_position: 20 --- In fact, virtual data is a reference object with a unique id, and its tracking mechanism is realized by first generating a mapping between virtual data id and response data, and then finding and replacing it with the actual value through virtual data id. diff --git a/docs/tutorial/03-client/09-sensorless-data-interaction/03-start-silent-factory.md b/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/03-start-silent-factory.md similarity index 99% rename from docs/tutorial/03-client/09-sensorless-data-interaction/03-start-silent-factory.md rename to docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/03-start-silent-factory.md index 0a140768c..d738c1967 100644 --- a/docs/tutorial/03-client/09-sensorless-data-interaction/03-start-silent-factory.md +++ b/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/03-start-silent-factory.md @@ -1,6 +1,5 @@ --- title: Boot silent factory -sidebar_position: 30 --- import Tabs from '@theme/Tabs'; diff --git a/docs/tutorial/03-client/09-sensorless-data-interaction/04-conservative-request.md b/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/04-conservative-request.md similarity index 95% rename from docs/tutorial/03-client/09-sensorless-data-interaction/04-conservative-request.md rename to docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/04-conservative-request.md index 4842ff79e..fef087138 100644 --- a/docs/tutorial/03-client/09-sensorless-data-interaction/04-conservative-request.md +++ b/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/04-conservative-request.md @@ -1,6 +1,5 @@ --- title: Step 1 - Implement features with conservative requests -sidebar_position: 40 --- import Tabs from '@theme/Tabs'; @@ -8,7 +7,7 @@ import TabItem from '@theme/TabItem'; Take Todo management as an example to realize the creation, editing, deletion and other functions of Todo in the non-sense interaction mode, and the key code related to the request will be provided in the following chapters. -> The [simple list page example](/tutorial/example/silent-submit-simple-list) here contains the complete code, you can enter the experience. + **useSQRequest** will be used to replace **useRequest** provided by alova, and then the most common conservative request mode will be implemented first, and then the process will be done step by step Interactive mode compatibility. diff --git a/docs/tutorial/03-client/09-sensorless-data-interaction/05-modify-response.md b/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/05-modify-response.md similarity index 98% rename from docs/tutorial/03-client/09-sensorless-data-interaction/05-modify-response.md rename to docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/05-modify-response.md index eddef1b85..083d03cb9 100644 --- a/docs/tutorial/03-client/09-sensorless-data-interaction/05-modify-response.md +++ b/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/05-modify-response.md @@ -1,6 +1,5 @@ --- title: Step 2 - Adjust Response Handling -sidebar_position: 50 --- import Tabs from '@theme/Tabs'; @@ -111,7 +110,7 @@ onSuccess(({ data, silentMethod }) => { // highlight-end ``` -> updateStateEffect is used in the same way as [updateState](/tutorial/advanced/update-across-components) +> updateStateEffect is used in the same way as [updateState](/next/tutorial/client/in-depth/update-across-components) diff --git a/docs/tutorial/03-client/09-sensorless-data-interaction/06-request-retry.md b/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/06-request-retry.md similarity index 96% rename from docs/tutorial/03-client/09-sensorless-data-interaction/06-request-retry.md rename to docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/06-request-retry.md index 1b8ef082b..b9685ed3a 100644 --- a/docs/tutorial/03-client/09-sensorless-data-interaction/06-request-retry.md +++ b/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/06-request-retry.md @@ -1,144 +1,143 @@ ---- -title: Step 3 - Set Request Retry -sidebar_position: 60 ---- - -When a request enters the silent queue, you can set request retry parameters for it to ensure its request success rate. This is valid when the behavior mode is set to **queue** and **silent**. The difference is, The request under the behavior of **silent** is persistent by default, and the request will continue to be sent even if it is refreshed before the request succeeds, while the request under the behavior of **queue** will not be persisted and will be cleared after refreshing. - -## Maximum number of retries - -Set the maximum number of retries, no retries by default. - -```javascript -useSQRequest(createOrEditTodo, { - //... - // highlight-start - // The number of retries is 3 times - maxRetryTimes: 3 - // highlight-end -}); -``` - -## Request delay time - -By default, each retry interval is 1000ms, and we can customize the delay time of each retry in the avoidance strategy. - -```javascript -useSQRequest(createOrEditTodo, { - //... - maxRetryTimes: 3, - // highlight-start - // Requests are delayed by 2000ms each time - backoff: { - delay: 2000 - } - // highlight-end -}); -``` - -If you need to increase the delay time according to the rules, you can set a growth factor for it. - -```javascript -useSQRequest(createOrEditTodo, { - //... - maxRetryTimes: 3, - backoff: { - delay: 2000, - // highlight-start - // When multiplier is set to 2, the first retry delay is 2 seconds, the second is 4 seconds, and the third is 6 seconds - multiplier: 2 - // highlight-end - } -}); -``` - -not enough? You can even add a random jitter value to each delay to make it look less regular - -```javascript -useSQRequest(createOrEditTodo, { - //... - maxRetryTimes: 3, - backoff: { - delay: 2000, - multiplier: 2, - // highlight-start - /** - * The initial jitter percentage value of the delay request, the range is 0-1 - * When only startQuiver is set, endQuiver defaults to 1 - * For example set to 0.5, it will add 50% to 100% random time on the current delay time - * If endQuiver has a value, the delay time will be increased by a random value in the range of startQuiver and endQuiver - */ - startQuiver: 0.5, - - /** - * The jitter end percentage value of the delayed request, the range is 0-1 - * When only endQuiver is set, startQuiver defaults to 0 - * For example set to 0.8, it will add a random time from 0% to 80% on the current delay time - * If startQuiver has a value, the delay time will increase the random value in the range of startQuiver and endQuiver - */ - endQuiver: 0.8; - // highlight-end - } -}); -``` - -## set retry rules - -By default, as long as the request fails, it will be retried. The request failure is divided into the following situations: - -1. The request is wrong, and the error is not caught by the global `onError` hook; -2. The request was successful, but an error was thrown in the global `onSuccess` hook; - -But in reality, not all requests need to be retried. For example, when a server error occurs or the network is disconnected, it should not be retried. In this case, it is necessary to set a retry judgment rule. When a request fails, an instance of `Error` is usually obtained. We can set a regular expression to match `error.message` or `error.name`, and if the match passes, no retry will be made. - -```javascript -useSQRequest(createOrEditTodo, { - //... - // highlight-start - // When the thrown error name is 500, or the wrong message matches network error, do not retry - retryError: { - name: /^500$/, - message: /network error/i - } - // highlight-end -}); -``` - -You can also set one of the matching rules. When only setting the matching rules for `message`, it can be directly abbreviated as a regular expression. - -```javascript -// Only set the name that matches the error -useSQRequest(createOrEditTodo, { - //... - retryError: { - name: /^500$/ - } -}); - -// Only set the message that matches the error -useSQRequest(createOrEditTodo, { - //... - retryError: /network error/i -}); -``` - -In order not to pollute the error message, usually we will put the error code returned by the server in `error.name`, of course, you can also splice it into `error.message`, the error handling example of Response is as follows: - -```javascript -const alovaInst = createAlova({ - //... - responded: { - onSuccess(response) { - // Error thrown on 500 error - if (response.status === 500) { - const error = new Error(response.statusText); - error.name = response.status; - throw error; - } - return response.json(); - } - } -}); -``` - -In the next step, the saved operation records will be used to perform data compensation on the list data to achieve the latest state. +--- +title: Step 3 - Set Request Retry +--- + +When a request enters the silent queue, you can set request retry parameters for it to ensure its request success rate. This is valid when the behavior mode is set to **queue** and **silent**. The difference is, The request under the behavior of **silent** is persistent by default, and the request will continue to be sent even if it is refreshed before the request succeeds, while the request under the behavior of **queue** will not be persisted and will be cleared after refreshing. + +## Maximum number of retries + +Set the maximum number of retries, no retries by default. + +```javascript +useSQRequest(createOrEditTodo, { + //... + // highlight-start + // The number of retries is 3 times + maxRetryTimes: 3 + // highlight-end +}); +``` + +## Request delay time + +By default, each retry interval is 1000ms, and we can customize the delay time of each retry in the avoidance strategy. + +```javascript +useSQRequest(createOrEditTodo, { + //... + maxRetryTimes: 3, + // highlight-start + // Requests are delayed by 2000ms each time + backoff: { + delay: 2000 + } + // highlight-end +}); +``` + +If you need to increase the delay time according to the rules, you can set a growth factor for it. + +```javascript +useSQRequest(createOrEditTodo, { + //... + maxRetryTimes: 3, + backoff: { + delay: 2000, + // highlight-start + // When multiplier is set to 2, the first retry delay is 2 seconds, the second is 4 seconds, and the third is 6 seconds + multiplier: 2 + // highlight-end + } +}); +``` + +not enough? You can even add a random jitter value to each delay to make it look less regular + +```javascript +useSQRequest(createOrEditTodo, { + //... + maxRetryTimes: 3, + backoff: { + delay: 2000, + multiplier: 2, + // highlight-start + /** + * The initial jitter percentage value of the delay request, the range is 0-1 + * When only startQuiver is set, endQuiver defaults to 1 + * For example set to 0.5, it will add 50% to 100% random time on the current delay time + * If endQuiver has a value, the delay time will be increased by a random value in the range of startQuiver and endQuiver + */ + startQuiver: 0.5, + + /** + * The jitter end percentage value of the delayed request, the range is 0-1 + * When only endQuiver is set, startQuiver defaults to 0 + * For example set to 0.8, it will add a random time from 0% to 80% on the current delay time + * If startQuiver has a value, the delay time will increase the random value in the range of startQuiver and endQuiver + */ + endQuiver: 0.8; + // highlight-end + } +}); +``` + +## set retry rules + +By default, as long as the request fails, it will be retried. The request failure is divided into the following situations: + +1. The request is wrong, and the error is not caught by the global `onError` hook; +2. The request was successful, but an error was thrown in the global `onSuccess` hook; + +But in reality, not all requests need to be retried. For example, when a server error occurs or the network is disconnected, it should not be retried. In this case, it is necessary to set a retry judgment rule. When a request fails, an instance of `Error` is usually obtained. We can set a regular expression to match `error.message` or `error.name`, and if the match passes, no retry will be made. + +```javascript +useSQRequest(createOrEditTodo, { + //... + // highlight-start + // When the thrown error name is 500, or the wrong message matches network error, do not retry + retryError: { + name: /^500$/, + message: /network error/i + } + // highlight-end +}); +``` + +You can also set one of the matching rules. When only setting the matching rules for `message`, it can be directly abbreviated as a regular expression. + +```javascript +// Only set the name that matches the error +useSQRequest(createOrEditTodo, { + //... + retryError: { + name: /^500$/ + } +}); + +// Only set the message that matches the error +useSQRequest(createOrEditTodo, { + //... + retryError: /network error/i +}); +``` + +In order not to pollute the error message, usually we will put the error code returned by the server in `error.name`, of course, you can also splice it into `error.message`, the error handling example of Response is as follows: + +```javascript +const alovaInst = createAlova({ + //... + responded: { + onSuccess(response) { + // Error thrown on 500 error + if (response.status === 500) { + const error = new Error(response.statusText); + error.name = response.status; + throw error; + } + return response.json(); + } + } +}); +``` + +In the next step, the saved operation records will be used to perform data compensation on the list data to achieve the latest state. diff --git a/docs/tutorial/03-client/09-sensorless-data-interaction/07-data-compensation.md b/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/07-data-compensation.md similarity index 99% rename from docs/tutorial/03-client/09-sensorless-data-interaction/07-data-compensation.md rename to docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/07-data-compensation.md index 429b9f0ad..b35c45799 100644 --- a/docs/tutorial/03-client/09-sensorless-data-interaction/07-data-compensation.md +++ b/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/07-data-compensation.md @@ -1,6 +1,5 @@ --- title: Step 4 - Data Compensation -sidebar_position: 70 --- The user may perform some data operations in a disconnected environment. At this time, the silent queue will be full of unsubmitted requests. When the network is restored, due to the limitation of the timing mechanism, it will take a little time to complete these requests. The list loaded at this time The data does not include unsubmitted requests, which can cause some confusion for users: diff --git a/docs/tutorial/03-client/09-sensorless-data-interaction/08-edit-item.md b/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/08-edit-item.md similarity index 97% rename from docs/tutorial/03-client/09-sensorless-data-interaction/08-edit-item.md rename to docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/08-edit-item.md index 6dfcf86af..1f3c3286f 100644 --- a/docs/tutorial/03-client/09-sensorless-data-interaction/08-edit-item.md +++ b/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/08-edit-item.md @@ -1,6 +1,5 @@ --- title: Step 5 - Edit Data -sidebar_position: 80 --- > What should I do when the user needs to edit data when the network is disconnected? @@ -18,7 +17,7 @@ And here we will focus on the case of **2-2**. In the previous chapters, we know that when the newly created data item has not been successfully submitted, the virtual data will be used as the placeholder for the id. Usually, we also get the data item through the id. At this time, we are in `useSQRequeset` Virtual data interception is implemented on the above, if a request is accompanied by virtual data information, it will be intercepted before sending and the data can be specified to replace the response data, and the request will be abandoned. -Remember the **silentMethod.reviewData** saved in [Step 2 - Adjust Response Handling](/tutorial/strategy/sensorless-data-interaction/modify-response)? +Remember the **silentMethod.reviewData** saved in [Step 2 - Adjust Response Handling](/next/tutorial/client/strategy/sensorless-data-interaction/modify-response)? ```javascript onSuccess(({ silentMethod }) => { diff --git a/docs/tutorial/03-client/09-sensorless-data-interaction/09-what-more.md b/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/09-what-more.md similarity index 95% rename from docs/tutorial/03-client/09-sensorless-data-interaction/09-what-more.md rename to docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/09-what-more.md index 71d85cf3b..cf14e0282 100644 --- a/docs/tutorial/03-client/09-sensorless-data-interaction/09-what-more.md +++ b/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/09-what-more.md @@ -1,6 +1,5 @@ --- title: What more? -sidebar_position: 90 --- ## Description of the role of virtual data @@ -104,7 +103,7 @@ const defaultSerializers = { }; ``` -[Read Date serializer source code](https://github.com/alovajs/scene/blob/main/src/hooks/silent/serializer/date.ts) +[Read Date serializer source code](https://github.com/alovajs/alova/blob/main/packages/client/src/util/serializer/date.ts) ## Manipulate the silent queue @@ -140,11 +139,11 @@ const handleClick = () => { ### Find silentMethod -In the previous [data compensation](/tutorial/strategy/sensorless-data-interaction/data-compensation), we used [filterSilentMethods](/tutorial/strategy/sensorless-data-interaction/data-compensation#filtersilentmethods) to find the silentMethod of the specified queue instance, it will return all matching silentMethod instances, here are two more ways to find the queue: +In the previous [data compensation](/next/tutorial/client/strategy/sensorless-data-interaction/data-compensation), we used [filterSilentMethods](/next/tutorial/client/strategy/sensorless-data-interaction/data-compensation#filtersilentmethods) to find the silentMethod of the specified queue instance, it will return all matching silentMethod instances, here are two more ways to find the queue: #### Find a silentMethod instance -Use `getSilentMethod` to query the first matching silentMethod instance, the usage is the same as [filterSilentMethods](/tutorial/strategy/sensorless-data-interaction/data-compensation#filtersilentmethods). +Use `getSilentMethod` to query the first matching silentMethod instance, the usage is the same as [filterSilentMethods](/next/tutorial/client/strategy/sensorless-data-interaction/data-compensation#filtersilentmethods). ```typescript function filterSilentMethods( diff --git a/docs/tutorial/03-client/09-sensorless-data-interaction/README.md b/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/README.md similarity index 90% rename from docs/tutorial/03-client/09-sensorless-data-interaction/README.md rename to docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/README.md index 5b703c10d..f04e2c73a 100644 --- a/docs/tutorial/03-client/09-sensorless-data-interaction/README.md +++ b/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/README.md @@ -1,146 +1,146 @@ ---- -title: Sensorless data interaction - Overview ---- - -Non-inductive data interaction means that when users interact with the application, relevant content can be displayed immediately without waiting, or the operation result can be displayed without waiting when submitting information, just like interacting with local data, thereby greatly improving the smoothness of the application It allows users to not feel the lag caused by data transmission. - -This is not new. The concept of optimistic update existed before 2015. It refers to displaying the submission results to the interface before the server responds. It is based on the assumption that most submissions are successful. The opposite is a conservative update, that is, the server will display a wait state before responding until the request is completed. In terms of handling failures, the current optimistic update solution is usually handled through fallback, such as the following sample code: - -```javascript -const list = []; -const data = {}; -addTodo(data).catch(() => { - list = list.filter(item => item !== data); -}); -list.push(data); -``` - -This can cause the following problems: - -1. Rollback will increase the user's understanding and operation costs; -2. Request timing issues; -3. If subsequent requests depend on this submission, this failure will make subsequent requests meaningless; -4. Possible lost requests; - -After several months of program design and continuous iteration, alova has taken a big step in this area. In our program, the above problems have been solved, which can ensure the success of the request more stably. Although there are still technical limitations, But it has been applied in many scenarios. In our technical solution, the problems caused by network fluctuations can be reduced to a higher extent. Your application is still available in high-latency networks or even disconnected, and the latest data can still be maintained after refreshing the page. - -## Application scenarios - -Although non-inductive data interaction cannot be used on a large scale, it is very suitable in certain scenarios. During the exploration, we found at least including but not limited to the following scenarios for your reference. - -### Editor application - -Note-taking applications such as Evernote and canvas editing applications such as MasterGO need to meet the following requirements: - -1. When entering the note or drawing list, the list data will be pulled in full, and the local cache data will be used next time; -2. Real-time synchronization to the server during the editing process, and the synchronization process occurs in the background, which will not affect the normal use of users; -3. You can continue to use it even when the network is poor or disconnected; - -:::info example - -We provide a [note application example](/tutorial/example/silent-submit-notes), you can enter the experience. - -::: - -### Setup module - -The setting module composed of commonly used switches and selectors needs to realize the requirement that the user operation is synchronized to the server in real time, and the submission status is no longer displayed, but the latest status after the operation is directly displayed. - -:::info example - -We provide a [setting page example](/tutorial/example/silent-submit-setting), you can enter the experience. - -::: - -### Simple list management - -The data we fill in when creating a list item is enough for the display of the list page, which is called a simple list. For example, a student list page displays the three data of the student's name, gender, and grade. These three data are required when creating a student fill in. In a simple list the following requirements will be fulfilled: - -1. Immediately display the latest status on the list page when adding, editing and deleting list items, no need to display it after the submission is completed, and it is not limited by network fluctuations; -2. When the page is refreshed, the list page is always kept up to date; - -:::info example - -We provide a [simple list page example](/tutorial/example/silent-submit-simple-list), you can enter the experience. - -::: - -### Complex list management - -A complex list means that the data filled in when creating a list item is not enough for display on the list page, and additional data needs to be generated according to the calculation of the server. For example, a Todo list page needs to list specific executions in addition to displaying basic information. date, but only the execution date range and related rules are specified on the creation page, so the execution date is calculated and generated by the server based on the date range and rules. - -The following requirements will be fulfilled in a complex list: - -1. Immediately display the latest status on the list page when adding, editing and deleting list items, and update the data calculated by the server to this list item after the server responds; -2. When the page is refreshed, the list page is always kept up to date; - -:::info example - -Stay tuned for complex list examples... - -::: - -### Free Mode - -In the above scenarios, you may want to judge whether to use the non-inductive interaction strategy or the most common conservative request strategy based on a condition. The requirements are as follows: - -1. When the network status is good, or paying users will use the non-sensing interaction strategy, but when the network fluctuates greatly, or free users cannot enjoy the non-sensing interaction strategy; -2. Strategies can be switched freely; - -:::info example - -In the above examples, you can experience the free switching strategy - -::: - -## Not recommended application scenarios - -### Information sharing class - -The submitted information needs to be synchronized to others, such as order information. This type of information has high real-time requirements, and we should ensure that the submission is successful. - -### Complex data interaction class - -Complex data interaction refers to the mixed editing and filtering of data, such as adding, editing, deleting and filtering a certain list. In this case, Alova cannot currently support it well. In subsequent versions Will try to solve this puzzle too. - -## Technical solutions - -In the technical solution of non-inductive data interaction, alova has implemented data pre-fetching and silent submission respectively. Next, let's understand these two technical solutions. - -:::info - -Please make sure you have mastered the following chapters before reading - -- [Basic Learning](/tutorial/getting-started) - -::: - -### Data pre-fetching - -In html, you may have seen such a tag ``, which tells the browser to preload the style file when it is idle, and put it in the cache In , when you really need to use it, you can take it out of the cache. Alova also uses a similar scheme to pre-fetch the required data through [useFetcher](/tutorial/advanced/use-fetcher), and it will be stored locally. in cache. You can predict the content that the user needs to read under any circumstances, and then pre-fetch the corresponding content. For example, the content of the next page can be pre-loaded in the process page, or the user stays on a button 200ms, we can pre-fetch the data needed for the next interface, which is similar to **Next.js** page preloading. - -We provide a [preloaded example](/tutorial/example/prefetch), you can enter the experience. - -### Silent submit - -Silent submission is a mechanism of submitting and responding. In the scheme, the completion of submission will be guaranteed, so it can be regarded as a safer optimistic update scheme. Silent submission mainly uses **Silent Queue** to persist request information and ensure request timing issues. **Virtual data** is used as a placeholder for server response data, which is replaced with actual response data when the request is completed. , through these two technologies, localized data creation is realized, and operations such as editing and deleting of newly created data are realized, even if the created data has not yet been submitted successfully on the server side. In order to keep development costs to a minimum, this is done automatically in alova. - -### Quiet Queue - -Silent queues are used to ensure the timing of requests. We can create queues arbitrarily, and all requests entering the queue will be stored in the queue in the form of **SilentMethod** instances. Each **SilentMethod** not only contains request information, but also Contains related configurations for silent submission, such as _unique id_, _error retry parameters_, etc. The requests in the queue will only initiate the next request after the previous response, thus ensuring the timing of the requests in the queue. You can put dependent requests in the same queue, which also ensures data consistency. - -![Silent queue](https://user-images.githubusercontent.com/29848971/220057005-dd467392-4a43-45a7-90dc-999dd1d95531.png) - -In the scheme, three behavior modes of `queue`, `silent`, and `static` are provided respectively, which are used to distinguish what kind of behavior a request needs to perform. - -- queue: The request will enter the silent queue, but it will not be persisted. It will wait for the previous request to complete before sending the request. The response callback will be triggered after the response. It is generally used for data acquisition that depends on the previous request; -- silent: The request will enter the silent queue and be persisted, and then trigger the response callback immediately. In this behavior mode, onSuccess will receive virtual data, and onError will never be triggered. Use this pattern; -- static: the request will not enter the silent queue, nor will it be persisted, it will issue the request immediately, and this mode can be used when silent submission is disabled; - -### virtual data - -In the submit-to-response mechanism, virtual data plays an important role. It means that before the server actually responds, it is used as a placeholder for the response data, and through the tracing mechanism, even if the virtual data is distributed in various locations of the application, Can be automatically replaced with the actual response data after the response. At the same time, it also plays an important role in the silent queue. It can identify the dependencies of requests in the queue, and replace the dependent data with actual data after the dependencies respond. For example, when creating a piece of data, it will return the id of this data. When the service When the terminal has not responded, the user performs a delete operation, and the id needs to be used as the delete identifier. At this time, the delete request will depend on the creation request. Before creating a request response, the virtual data will be used as an id placeholder as a parameter for deletion, and the virtual data id will be replaced after creating a request response, so that the deletion request can be completed. - -![virtual data](https://user-images.githubusercontent.com/29848971/220005505-d30b7ae2-ddd0-4080-81a4-65c9be2cb0bd.png) - -Next, we will learn more about the characteristics of virtual data. +--- +title: Sensorless data interaction - Overview +--- + +Non-inductive data interaction means that when users interact with the application, relevant content can be displayed immediately without waiting, or the operation result can be displayed without waiting when submitting information, just like interacting with local data, thereby greatly improving the smoothness of the application It allows users to not feel the lag caused by data transmission. + +This is not new. The concept of optimistic update existed before 2015. It refers to displaying the submission results to the interface before the server responds. It is based on the assumption that most submissions are successful. The opposite is a conservative update, that is, the server will display a wait state before responding until the request is completed. In terms of handling failures, the current optimistic update solution is usually handled through fallback, such as the following sample code: + +```javascript +const list = []; +const data = {}; +addTodo(data).catch(() => { + list = list.filter(item => item !== data); +}); +list.push(data); +``` + +This can cause the following problems: + +1. Rollback will increase the user's understanding and operation costs; +2. Request timing issues; +3. If subsequent requests depend on this submission, this failure will make subsequent requests meaningless; +4. Possible lost requests; + +After several months of program design and continuous iteration, alova has taken a big step in this area. In our program, the above problems have been solved, which can ensure the success of the request more stably. Although there are still technical limitations, But it has been applied in many scenarios. In our technical solution, the problems caused by network fluctuations can be reduced to a higher extent. Your application is still available in high-latency networks or even disconnected, and the latest data can still be maintained after refreshing the page. + +## Application scenarios + +Although non-inductive data interaction cannot be used on a large scale, it is very suitable in certain scenarios. During the exploration, we found at least including but not limited to the following scenarios for your reference. + +### Editor application + +Note-taking applications such as Evernote and canvas editing applications such as MasterGO need to meet the following requirements: + +1. When entering the note or drawing list, the list data will be pulled in full, and the local cache data will be used next time; +2. Real-time synchronization to the server during the editing process, and the synchronization process occurs in the background, which will not affect the normal use of users; +3. You can continue to use it even when the network is poor or disconnected; + + + +### Setup module + +The setting module composed of commonly used switches and selectors needs to realize the requirement that the user operation is synchronized to the server in real time, and the submission status is no longer displayed, but the latest status after the operation is directly displayed. + + + +### Simple list management + +The data we fill in when creating a list item is enough for the display of the list page, which is called a simple list. For example, a student list page displays the three data of the student's name, gender, and grade. These three data are required when creating a student fill in. In a simple list the following requirements will be fulfilled: + +1. Immediately display the latest status on the list page when adding, editing and deleting list items, no need to display it after the submission is completed, and it is not limited by network fluctuations; +2. When the page is refreshed, the list page is always kept up to date; + + + +### Complex list management + +A complex list means that the data filled in when creating a list item is not enough for display on the list page, and additional data needs to be generated according to the calculation of the server. For example, a Todo list page needs to list specific executions in addition to displaying basic information. date, but only the execution date range and related rules are specified on the creation page, so the execution date is calculated and generated by the server based on the date range and rules. + +The following requirements will be fulfilled in a complex list: + +1. Immediately display the latest status on the list page when adding, editing and deleting list items, and update the data calculated by the server to this list item after the server responds; +2. When the page is refreshed, the list page is always kept up to date; + +:::info example + +Stay tuned for complex list examples... + +::: + +### Free Mode + +In the above scenarios, you may want to judge whether to use the non-inductive interaction strategy or the most common conservative request strategy based on a condition. The requirements are as follows: + +1. When the network status is good, or paying users will use the non-sensing interaction strategy, but when the network fluctuates greatly, or free users cannot enjoy the non-sensing interaction strategy; +2. Strategies can be switched freely; + +:::info example + +In the above examples, you can experience the free switching strategy + +::: + +## Not recommended application scenarios + +### Information sharing class + +The submitted information needs to be synchronized to others, such as order information. This type of information has high real-time requirements, and we should ensure that the submission is successful. + +### Complex data interaction class + +Complex data interaction refers to the mixed editing and filtering of data, such as adding, editing, deleting and filtering a certain list. In this case, Alova cannot currently support it well. In subsequent versions Will try to solve this puzzle too. + +## Technical solutions + +In the technical solution of non-inductive data interaction, alova has implemented data pre-fetching and silent submission respectively. Next, let's understand these two technical solutions. + +:::info + +Please make sure you have mastered the following chapters before reading + +- [Basic Learning](/next/tutorial/getting-started/quick-start) + +::: + +### Data pre-fetching + +In html, you may have seen such a tag ``, which tells the browser to preload the style file when it is idle, and put it in the cache In , when you really need to use it, you can take it out of the cache. Alova also uses a similar scheme to pre-fetch the required data through [useFetcher](/next/tutorial/client/strategy/use-fetcher), and it will be stored locally. in cache. You can predict the content that the user needs to read under any circumstances, and then pre-fetch the corresponding content. For example, the content of the next page can be pre-loaded in the process page, or the user stays on a button 200ms, we can pre-fetch the data needed for the next interface, which is similar to **Next.js** page preloading. + + + +### Silent submit + +Silent submission is a mechanism of submitting and responding. In the scheme, the completion of submission will be guaranteed, so it can be regarded as a safer optimistic update scheme. Silent submission mainly uses **Silent Queue** to persist request information and ensure request timing issues. **Virtual data** is used as a placeholder for server response data, which is replaced with actual response data when the request is completed. , through these two technologies, localized data creation is realized, and operations such as editing and deleting of newly created data are realized, even if the created data has not yet been submitted successfully on the server side. In order to keep development costs to a minimum, this is done automatically in alova. + +### Quiet Queue + +Silent queues are used to ensure the timing of requests. We can create queues arbitrarily, and all requests entering the queue will be stored in the queue in the form of **SilentMethod** instances. Each **SilentMethod** not only contains request information, but also Contains related configurations for silent submission, such as _unique id_, _error retry parameters_, etc. The requests in the queue will only initiate the next request after the previous response, thus ensuring the timing of the requests in the queue. You can put dependent requests in the same queue, which also ensures data consistency. + +![Silent queue](https://user-images.githubusercontent.com/29848971/220057005-dd467392-4a43-45a7-90dc-999dd1d95531.png) + +In the scheme, three behavior modes of `queue`, `silent`, and `static` are provided respectively, which are used to distinguish what kind of behavior a request needs to perform. + +- queue: The request will enter the silent queue, but it will not be persisted. It will wait for the previous request to complete before sending the request. The response callback will be triggered after the response. It is generally used for data acquisition that depends on the previous request; +- silent: The request will enter the silent queue and be persisted, and then trigger the response callback immediately. In this behavior mode, onSuccess will receive virtual data, and onError will never be triggered. Use this pattern; +- static: the request will not enter the silent queue, nor will it be persisted, it will issue the request immediately, and this mode can be used when silent submission is disabled; + +### virtual data + +In the submit-to-response mechanism, virtual data plays an important role. It means that before the server actually responds, it is used as a placeholder for the response data, and through the tracing mechanism, even if the virtual data is distributed in various locations of the application, Can be automatically replaced with the actual response data after the response. At the same time, it also plays an important role in the silent queue. It can identify the dependencies of requests in the queue, and replace the dependent data with actual data after the dependencies respond. For example, when creating a piece of data, it will return the id of this data. When the service When the terminal has not responded, the user performs a delete operation, and the id needs to be used as the delete identifier. At this time, the delete request will depend on the creation request. Before creating a request response, the virtual data will be used as an id placeholder as a parameter for deletion, and the virtual data id will be replaced after creating a request response, so that the deletion request can be completed. + +![virtual data](https://user-images.githubusercontent.com/29848971/220005505-d30b7ae2-ddd0-4080-81a4-65c9be2cb0bd.png) + +Next, we will learn more about the characteristics of virtual data. diff --git a/docs/tutorial/03-client/09-sensorless-data-interaction/_category_.json b/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/_category_.json similarity index 93% rename from docs/tutorial/03-client/09-sensorless-data-interaction/_category_.json rename to docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/_category_.json index 89d03fd9e..42291c7e5 100644 --- a/docs/tutorial/03-client/09-sensorless-data-interaction/_category_.json +++ b/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/_category_.json @@ -1,3 +1,3 @@ -{ - "label": "Sensorless data interaction" -} +{ + "label": "Sensorless data interaction" +} diff --git a/docs/tutorial/03-client/10-use-captcha.md b/docs/tutorial/03-client/01-strategy/10-use-captcha.md similarity index 90% rename from docs/tutorial/03-client/10-use-captcha.md rename to docs/tutorial/03-client/01-strategy/10-use-captcha.md index b12225afc..1d9530706 100644 --- a/docs/tutorial/03-client/10-use-captcha.md +++ b/docs/tutorial/03-client/01-strategy/10-use-captcha.md @@ -1,6 +1,5 @@ --- title: send captcha -sidebar_position: 100 --- import Tabs from '@theme/Tabs'; @@ -16,9 +15,9 @@ use hook The verification code sending hook saves you the trouble of developing the verification code sending function. -## Example + ## Features @@ -153,7 +152,7 @@ useCaptcha(() => apiSendCaptcha(mobile.value), { ### Hook configuration -Inherit all configurations of [**useRequest**](/api/core-hooks#userequest) except `immediate`, `immediate` in `useCaptcha` has been hard-coded to false. +Inherit all configurations of [**useRequest**](/next/api/core-hooks#userequest) except `immediate`, `immediate` in `useCaptcha` has been hard-coded to false. | Name | Description | Type | Default | Version | | ---------------- | ---------------------------------------------------------------------------------------------------------- | ------ | ------- | ------- | @@ -161,7 +160,7 @@ Inherit all configurations of [**useRequest**](/api/core-hooks#userequest) excep ### Responsive data -Inherit all responsive data from [**useRequest**](/api/core-hooks#userequest). +Inherit all responsive data from [**useRequest**](/next/api/core-hooks#userequest). | Name | Description | Type | Version | | --------- | ----------------------------------------------------------------------------------------------------------- | ------ | ------- | @@ -169,7 +168,7 @@ Inherit all responsive data from [**useRequest**](/api/core-hooks#userequest). ### Action function -Inherit all action functions of [**useRequest**](/api/core-hooks#userequest). +Inherit all action functions of [**useRequest**](/next/api/core-hooks#userequest). | name | description | function parameters | return value | version | | ---- | ---------------------------------------------------------------------------- | ------------------------------- | ------------------- | ------- | @@ -177,4 +176,4 @@ Inherit all action functions of [**useRequest**](/api/core-hooks#userequest). ### Event -Inherit all events from [**useRequest**](/api/core-hooks#userequest). +Inherit all events from [**useRequest**](/next/api/core-hooks#userequest). diff --git a/docs/tutorial/03-client/11-use-serial-request.md b/docs/tutorial/03-client/01-strategy/11-use-serial-request.md similarity index 80% rename from docs/tutorial/03-client/11-use-serial-request.md rename to docs/tutorial/03-client/01-strategy/11-use-serial-request.md index baedb257c..e2875532b 100644 --- a/docs/tutorial/03-client/11-use-serial-request.md +++ b/docs/tutorial/03-client/01-strategy/11-use-serial-request.md @@ -1,6 +1,5 @@ --- title: useRequest with serial -sidebar_position: 110 --- import Tabs from '@theme/Tabs'; @@ -14,7 +13,7 @@ use hook > Before using extension hooks, make sure you are familiar with the basic usage of alova. -This use hook is more concise and easy to use than [serial request in best practice](/tutorial/best-practice/skills), with unified loading status, error, and callback functions. +This use hook is more concise and easy to use than [serial request in best practice](/next/tutorial/project/best-practice/serial-request), with unified loading status, error, and callback functions. ## Features @@ -22,9 +21,9 @@ This use hook is more concise and easy to use than [serial request in best pract - Unified request status and callback function; - send function can trigger serial execution of multiple requests; -## Example + ## Usage @@ -92,16 +91,16 @@ When any of the serial requests is wrong, `onError` will be triggered, and its ` ### Hook configuration -Inherit all configurations from [**useRequest**](/api/core-hooks#userequest). +Inherit all configurations from [**useRequest**](/next/api/core-hooks#userequest). ### Responsive data -Inherit all responsive data from [**useRequest**](/api/core-hooks#userequest). +Inherit all responsive data from [**useRequest**](/next/api/core-hooks#userequest). ### Action function -Inherit all action functions of [**useRequest**](/api/core-hooks#userequest). +Inherit all action functions of [**useRequest**](/next/api/core-hooks#userequest). ### Event -Inherit all events from [**useRequest**](/api/core-hooks#userequest). +Inherit all events from [**useRequest**](/next/api/core-hooks#userequest). diff --git a/docs/tutorial/03-client/12-use-serial-watcher.md b/docs/tutorial/03-client/01-strategy/12-use-serial-watcher.md similarity index 80% rename from docs/tutorial/03-client/12-use-serial-watcher.md rename to docs/tutorial/03-client/01-strategy/12-use-serial-watcher.md index 267af9261..bf82a9dbb 100644 --- a/docs/tutorial/03-client/12-use-serial-watcher.md +++ b/docs/tutorial/03-client/01-strategy/12-use-serial-watcher.md @@ -1,6 +1,5 @@ --- title: useWatcher with serial -sidebar_position: 120 --- import Tabs from '@theme/Tabs'; @@ -14,7 +13,7 @@ use hook > Before using extension hooks, make sure you are familiar with the basic usage of alova. -Status update triggers a set of serial requests, which is more concise and easy to use than [serial request in best practice](/tutorial/best-practice/skills), with unified loading status, error, and callback functions. +Status update triggers a set of serial requests, which is more concise and easy to use than [serial request in best practice](/next/tutorial/project/best-practice/serial-request), with unified loading status, error, and callback functions. ## Features @@ -22,9 +21,9 @@ Status update triggers a set of serial requests, which is more concise and easy - Unified request status and callback function; - Status update triggers serial execution of multiple requests; -## Example + ## Usage @@ -93,16 +92,16 @@ When any of the serial requests is wrong, `onError` will be triggered, and its ` ### Hook configuration -Inherit all configurations of [**useWatcher**](/api/core-hooks#usewatcher). +Inherit all configurations of [**useWatcher**](/next/api/core-hooks#usewatcher). ### Responsive data -Inherit all responsive data from [**useWatcher**](/api/core-hooks#usewatcher). +Inherit all responsive data from [**useWatcher**](/next/api/core-hooks#usewatcher). ### Action function -Inherit all action functions of [**useWatcher**](/api/core-hooks#usewatcher). +Inherit all action functions of [**useWatcher**](/next/api/core-hooks#usewatcher). ### Event -Inherit all events from [**useWatcher**](/api/core-hooks#usewatcher). +Inherit all events from [**useWatcher**](/next/api/core-hooks#usewatcher). diff --git a/docs/tutorial/03-client/13-use-retriable-request.md b/docs/tutorial/03-client/01-strategy/13-use-retriable-request.md similarity index 96% rename from docs/tutorial/03-client/13-use-retriable-request.md rename to docs/tutorial/03-client/01-strategy/13-use-retriable-request.md index 62e27e02f..83e34b04a 100644 --- a/docs/tutorial/03-client/13-use-retriable-request.md +++ b/docs/tutorial/03-client/01-strategy/13-use-retriable-request.md @@ -1,6 +1,5 @@ --- title: retriable request -sidebar_position: 130 --- import Tabs from '@theme/Tabs'; @@ -16,9 +15,9 @@ use hook A use hook that can automatically retry a request failure, you can use it for important requests. -## Example + ## Features @@ -177,7 +176,7 @@ const handleStop = () => { ### Hook configuration -Inherit all configurations from [**useRequest**](/api/core-hooks#userequest). +Inherit all configurations from [**useRequest**](/next/api/core-hooks#userequest). | Name | Description | Type | Default | Version | | ------- | -------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- | ----------------------------------------- | ------- | --- | @@ -195,11 +194,11 @@ Inherit all configurations from [**useRequest**](/api/core-hooks#userequest). ### Responsive data -Inherit all responsive data from [**useRequest**](/api/core-hooks#userequest). +Inherit all responsive data from [**useRequest**](/next/api/core-hooks#userequest). ### Action function -Inherit all action functions of [**useRequest**](/api/core-hooks#userequest). +Inherit all action functions of [**useRequest**](/next/api/core-hooks#userequest). | name | description | function parameters | return value | version | | ---- | ------------------------------------------------------------------------------------------------------------------ | ------------------- | ------------ | ------- | @@ -207,7 +206,7 @@ Inherit all action functions of [**useRequest**](/api/core-hooks#userequest). ### Event -Inherit all events from [**useRequest**](/api/core-hooks#userequest). +Inherit all events from [**useRequest**](/next/api/core-hooks#userequest). | Name | Description | Callback Parameters | Version | | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | ------- | diff --git a/docs/tutorial/03-client/14-use-sse.md b/docs/tutorial/03-client/01-strategy/14-use-sse.md similarity index 97% rename from docs/tutorial/03-client/14-use-sse.md rename to docs/tutorial/03-client/01-strategy/14-use-sse.md index 96259cfa8..2d79edac4 100644 --- a/docs/tutorial/03-client/14-use-sse.md +++ b/docs/tutorial/03-client/01-strategy/14-use-sse.md @@ -1,6 +1,5 @@ --- title: request by server-send events -sidebar_position: 140 --- import Tabs from '@theme/Tabs'; @@ -159,7 +158,7 @@ on('event-name', ({ data }) => { ### Global Response Interception -By default, the response data is captured by [the global response interceptors](/tutorial/combine-framework/response). If this is not the desired behavior, you can manually disable it. +By default, the response data is captured by [the global response interceptors](/next/tutorial/getting-started/basic/global-interceptor). If this is not the desired behavior, you can manually disable it. ```typescript const { data, readyState, onMessage, on } = useSSE(method, { diff --git a/docs/tutorial/03-client/15-use-breakpoint-uploader.md b/docs/tutorial/03-client/01-strategy/15-use-breakpoint-uploader.md similarity index 69% rename from docs/tutorial/03-client/15-use-breakpoint-uploader.md rename to docs/tutorial/03-client/01-strategy/15-use-breakpoint-uploader.md index 494bd2a56..9a7887fa9 100644 --- a/docs/tutorial/03-client/15-use-breakpoint-uploader.md +++ b/docs/tutorial/03-client/01-strategy/15-use-breakpoint-uploader.md @@ -1,6 +1,5 @@ --- title: Breakpoint upload -sidebar_position: 150 --- coming soon... diff --git a/docs/tutorial/03-client/16-use-uploader.md b/docs/tutorial/03-client/01-strategy/16-use-uploader.md similarity index 72% rename from docs/tutorial/03-client/16-use-uploader.md rename to docs/tutorial/03-client/01-strategy/16-use-uploader.md index 382005157..b1a36b1ba 100644 --- a/docs/tutorial/03-client/16-use-uploader.md +++ b/docs/tutorial/03-client/01-strategy/16-use-uploader.md @@ -1,6 +1,5 @@ --- title: Universal upload strategy -sidebar_position: 160 --- coming soon... diff --git a/docs/tutorial/03-client/17-rate-limit-middleware.md b/docs/tutorial/03-client/01-strategy/17-rate-limit-middleware.md similarity index 89% rename from docs/tutorial/03-client/17-rate-limit-middleware.md rename to docs/tutorial/03-client/01-strategy/17-rate-limit-middleware.md index 7f6348b34..e816220f7 100644 --- a/docs/tutorial/03-client/17-rate-limit-middleware.md +++ b/docs/tutorial/03-client/01-strategy/17-rate-limit-middleware.md @@ -1,6 +1,5 @@ --- title: Request rate limit -sidebar_position: 170 --- Set the number of requests that should be executed immediately for each interval, and other requests will be automatically delayed. diff --git a/docs/tutorial/03-client/README.md b/docs/tutorial/03-client/01-strategy/README.md similarity index 80% rename from docs/tutorial/03-client/README.md rename to docs/tutorial/03-client/01-strategy/README.md index 84f84a380..579ac8890 100644 --- a/docs/tutorial/03-client/README.md +++ b/docs/tutorial/03-client/01-strategy/README.md @@ -8,7 +8,7 @@ Like using a component library, just learn it when you need a request strategy! All client use hooks have the following in common: -1. They all rely on statesHook, please [set statesHook](/tutorial/getting-started/combine-framework) before using. +1. They all rely on statesHook, please [set statesHook](/next/tutorial/getting-started/basic/combine-framework) before using. 2. Their return values ​​all contain the `update` function, which is used to actively update the exported state value. diff --git a/docs/tutorial/03-client/01-strategy/_category_.json b/docs/tutorial/03-client/01-strategy/_category_.json new file mode 100644 index 000000000..15c3c6fa5 --- /dev/null +++ b/docs/tutorial/03-client/01-strategy/_category_.json @@ -0,0 +1,3 @@ +{ + "label": "Request Strategy" +} diff --git a/docs/tutorial/06-advanced/01-update-across-components.md b/docs/tutorial/03-client/02-in-depth/01-update-across-components.md similarity index 85% rename from docs/tutorial/06-advanced/01-update-across-components.md rename to docs/tutorial/03-client/02-in-depth/01-update-across-components.md index a176d5f2c..793502e5e 100644 --- a/docs/tutorial/06-advanced/01-update-across-components.md +++ b/docs/tutorial/03-client/02-in-depth/01-update-across-components.md @@ -1,6 +1,5 @@ --- title: Update states across components -sidebar_position: 20 --- :::info usage scope @@ -13,7 +12,7 @@ There is a scenario where when the user clicks on an item in the todo list, ente At this time, you can use `updateState` to update the existing responsive state under any module/page. It can find and modify the responsive state in other modules. -[Here is an example of `updateState`](/tutorial/example/update-state) + ## Use method instance to find response states @@ -58,7 +57,7 @@ onSuccess(() => { ## Dynamically update response states -Maybe sometimes you are not sure that you need to update the response states under the method, but you know how to find the cached data that needs to be invalidated. We can use [Method instance matcher](/tutorial/advanced/method-matcher) to dynamically Find the corresponding method instance. The following example shows adding a piece of data to the list corresponding to the method instance named todoList. +Maybe sometimes you are not sure that you need to update the response states under the method, but you know how to find the cached data that needs to be invalidated. We can use [Method instance matcher](/next/tutorial/client/in-depth/method-matcher) to dynamically Find the corresponding method instance. The following example shows adding a piece of data to the list corresponding to the method instance named todoList. ```javascript updateState('todoList', todoListRaw => { @@ -70,7 +69,7 @@ updateState('todoList', todoListRaw => { }); ``` -The [Method instance matcher](/tutorial/advanced/method-matcher) will be introduced in detail in subsequent chapters. +The [Method instance matcher](/next/tutorial/client/in-depth/method-matcher) will be introduced in detail in subsequent chapters. ## Listen for matching events @@ -98,7 +97,7 @@ By default, `updateState` will look for the response state created by alova's us This problem often occurs when updating states across pages. What we tend to overlook is that by default, the previous page has been destroyed when the page jumps. Therefore, if you want to update states across pages, here are two suggestions: 1. Persist the page components to ensure that the updated states can still be found; -2. Use [setCache](/tutorial/cache/set-and-query) instead of `updateState`. The principle is that when the request for the previous page exists in the cache, update its cache to ensure that when the page is created again, the The request can hit the updated cache to achieve the same effect. +2. Use [setCache](/next/tutorial/cache/set-and-query) instead of `updateState`. The principle is that when the request for the previous page exists in the cache, update its cache to ensure that when the page is created again, the The request can hit the updated cache to achieve the same effect. ::: diff --git a/docs/tutorial/06-advanced/02-method-matcher.md b/docs/tutorial/03-client/02-in-depth/02-method-matcher.md similarity index 90% rename from docs/tutorial/06-advanced/02-method-matcher.md rename to docs/tutorial/03-client/02-in-depth/02-method-matcher.md index ca275afa0..ad9415367 100644 --- a/docs/tutorial/06-advanced/02-method-matcher.md +++ b/docs/tutorial/03-client/02-in-depth/02-method-matcher.md @@ -1,6 +1,5 @@ --- title: Method Matcher -sidebar_position: 30 --- :::info Usage scope @@ -13,11 +12,11 @@ The method snapshot matcher is a method that dynamically searches for method ins It is generally used with the following 5 functions that need to use method instances. -1. [setCache](/tutorial/cache/set-and-query) -2. [queryCache](/tutorial/cache/set-and-query) -3. [invalidateCache](/tutorial/cache/manually-invalidate) -4. [updateState](/tutorial/advanced/update-across-components) -5. [useFetcher.fetch](/tutorial/client/use-fetcher) +1. [setCache](/next/tutorial/cache/set-and-query) +2. [queryCache](/next/tutorial/cache/set-and-query) +3. [invalidateCache](/next/tutorial/cache/manually-invalidate) +4. [updateState](/next/tutorial/client/in-depth/update-across-components) +5. [useFetcher.fetch](/next/tutorial/client/strategy/use-fetcher) ## Matching rules diff --git a/docs/tutorial/06-advanced/04-middleware.md b/docs/tutorial/03-client/02-in-depth/03-middleware.md similarity index 99% rename from docs/tutorial/06-advanced/04-middleware.md rename to docs/tutorial/03-client/02-in-depth/03-middleware.md index 8924181cd..3418ca85c 100644 --- a/docs/tutorial/06-advanced/04-middleware.md +++ b/docs/tutorial/03-client/02-in-depth/03-middleware.md @@ -1,6 +1,5 @@ --- title: Request Middleware -sidebar_position: 40 --- import Tabs from '@theme/Tabs'; @@ -210,7 +209,7 @@ async function middleware(context, next) { } ``` -For detailed usage of state proxy, please refer to [State Proxy](/docs/guide/state-proxy). +For detailed usage of state proxy, please refer to [State Proxy](/next/tutorial/advanced/custom/client-strategy). ## Interrupt or repeat request diff --git a/docs/tutorial/06-advanced/08-manage-extra-states.md b/docs/tutorial/03-client/02-in-depth/04-manage-extra-states.md similarity index 91% rename from docs/tutorial/06-advanced/08-manage-extra-states.md rename to docs/tutorial/03-client/02-in-depth/04-manage-extra-states.md index c71e217e3..e332c012f 100644 --- a/docs/tutorial/06-advanced/08-manage-extra-states.md +++ b/docs/tutorial/03-client/02-in-depth/04-manage-extra-states.md @@ -1,9 +1,8 @@ --- title: Manage Extra states -sidebar_position: 80 --- -:::info Scope of use +:::info Scope of usage Client useHook @@ -13,7 +12,7 @@ import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -In the previous [Update responsive state across pages/modules](/tutorial/advanced/update-across-components) chapter, we introduced how to update responsive states across pages or modules through `updateState`, but it can only update states created by useHooks. What should we do if we need to update custom states across components? Let's continue! +In the previous [Update responsive state across pages/modules](/next/tutorial/client/in-depth/update-across-components) chapter, we introduced how to update responsive states across pages or modules through `updateState`, but it can only update states created by useHooks. What should we do if we need to update custom states across components? Let's continue! ## Update a single state diff --git a/docs/tutorial/03-client/02-in-depth/_category_.json b/docs/tutorial/03-client/02-in-depth/_category_.json new file mode 100644 index 000000000..5b186720e --- /dev/null +++ b/docs/tutorial/03-client/02-in-depth/_category_.json @@ -0,0 +1,3 @@ +{ + "label": "In-Depth" +} diff --git a/docs/tutorial/03-client/_category_.json b/docs/tutorial/03-client/_category_.json index 2df63af18..32faa7806 100644 --- a/docs/tutorial/03-client/_category_.json +++ b/docs/tutorial/03-client/_category_.json @@ -1,3 +1,5 @@ { - "label": "Client Strategy" + "label": "Client", + "collapsed": false, + "collapsible": false } diff --git a/docs/tutorial/04-server/01-retry.md b/docs/tutorial/04-server/01-strategy/01-retry.md similarity index 97% rename from docs/tutorial/04-server/01-retry.md rename to docs/tutorial/04-server/01-strategy/01-retry.md index 4aa9d8528..593f7c7b0 100644 --- a/docs/tutorial/04-server/01-retry.md +++ b/docs/tutorial/04-server/01-strategy/01-retry.md @@ -1,6 +1,5 @@ --- title: Request retry strategy -sidebar_position: 10 --- :::info type @@ -124,6 +123,6 @@ endQuiver: 0.8; }); ``` -## API + diff --git a/docs/tutorial/04-server/02-send-captcha.md b/docs/tutorial/04-server/01-strategy/02-send-captcha.md similarity index 90% rename from docs/tutorial/04-server/02-send-captcha.md rename to docs/tutorial/04-server/01-strategy/02-send-captcha.md index e25cf25d6..09eef7eab 100644 --- a/docs/tutorial/04-server/02-send-captcha.md +++ b/docs/tutorial/04-server/01-strategy/02-send-captcha.md @@ -1,6 +1,5 @@ --- title: Send Captcha -sidebar_position: 20 --- Send the captcha via SMS or email, and record the countdown time according to the key. If you send the captcha again within the countdown, an error will be reported. diff --git a/docs/tutorial/04-server/03-rate-limit-middleware.md b/docs/tutorial/04-server/01-strategy/03-rate-limit-middleware.md similarity index 89% rename from docs/tutorial/04-server/03-rate-limit-middleware.md rename to docs/tutorial/04-server/01-strategy/03-rate-limit-middleware.md index 50eaf516c..c9aab92cc 100644 --- a/docs/tutorial/04-server/03-rate-limit-middleware.md +++ b/docs/tutorial/04-server/01-strategy/03-rate-limit-middleware.md @@ -1,6 +1,5 @@ --- title: Request Rate Limit -sidebar_position: 30 --- Set the number of requests that should be executed immediately per interval, other requests will be automatically delayed. diff --git a/docs/tutorial/04-server/README.md b/docs/tutorial/04-server/01-strategy/README.md similarity index 100% rename from docs/tutorial/04-server/README.md rename to docs/tutorial/04-server/01-strategy/README.md diff --git a/docs/tutorial/04-server/01-strategy/_category_.json b/docs/tutorial/04-server/01-strategy/_category_.json new file mode 100644 index 000000000..15c3c6fa5 --- /dev/null +++ b/docs/tutorial/04-server/01-strategy/_category_.json @@ -0,0 +1,3 @@ +{ + "label": "Request Strategy" +} diff --git a/docs/tutorial/04-server/_category_.json b/docs/tutorial/04-server/_category_.json index 90ea63292..78716c63c 100644 --- a/docs/tutorial/04-server/_category_.json +++ b/docs/tutorial/04-server/_category_.json @@ -1,3 +1,5 @@ { - "label": "Server Strategy" + "label": "Server", + "collapsed": false, + "collapsible": false } diff --git a/docs/tutorial/05-cache/01-mode.md b/docs/tutorial/05-cache/01-mode.md index ba3197dca..8ed67bbb9 100644 --- a/docs/tutorial/05-cache/01-mode.md +++ b/docs/tutorial/05-cache/01-mode.md @@ -1,6 +1,5 @@ --- title: Cache Mode -sidebar_position: 10 --- import MemoryCache from '@site/example-links/MemoryCache'; @@ -21,14 +20,14 @@ Memory mode belongs to the single-level cache (L1 cache) mode. By default, the c ```mermaid flowchart LR -A[User request] --> B{Check L1 cache} -B -->|Hit| C[Return data] -B -->|Miss| F[Request API interface] -F --> G[Update L1 cache] -G --> C -C --> H[End] - -style F stroke-width:8px + A[User request] --> B{Check L1 cache} + B -->|Hit| C[Return data] + B -->|Miss| F[Request API interface] + F --> G[Update L1 cache] + G --> C + C --> H[End] + + style F stroke-width:8px ``` By default, GET request has a memory cache time of 300000ms (5 minutes). Developers can also customize the cache setting. Please continue reading. @@ -113,7 +112,7 @@ const alovaInstance = createAlova({ }); ``` -Memory cache mode corresponds to l1 cache. Here we replace the cache adapter with lru-cache shared between processes. You can also [customize the storage adapter](/tutorial/custom/custom-storage-adapter). For example, when you only need a single-level cache, you can also directly set the l1 cache to the redis adapter. +Memory cache mode corresponds to l1 cache. Here we replace the cache adapter with lru-cache shared between processes. You can also [customize the storage adapter](/next/tutorial/advanced/custom/storage-adapter). For example, when you only need a single-level cache, you can also directly set the l1 cache to the redis adapter. ## Restore mode @@ -121,24 +120,24 @@ Restore mode corresponds to multi-level cache, namely L1 and L2 cache. After the ```mermaid flowchart LR -A[User request] --> B{Check L1 cache} -B -->|Hit| C[Return data] -B -->|Miss| D{Check L2 cache} -D -->|Hit| E[Update L1 cache] -E --> C -D -->|Miss| F[Request API interface] -F --> G[Update L2 cache] -G --> E -C --> H[End] - -style F stroke-width:8px + A[User request] --> B{Check L1 cache} + B -->|Hit| C[Return data] + B -->|Miss| D{Check L2 cache} + D -->|Hit| E[Update L1 cache] + E --> C + D -->|Miss| F[Request API interface] + F --> G[Update L2 cache] + G --> E + C --> H[End] + + style F stroke-width:8px ``` ### Client In the client, when the cache has not expired, it will not be invalidated even if the page cache is refreshed. It is generally used for some data that requires server-side management but is basically unchanged, such as the specific dates of holidays each year are different, but will not change again. In this scenario, we only need to set the cache expiration time to the last minute of this year. -When using alova in the client, `localStorage` is used as the L2 storage adapter by default. You can also [customize the storage adapter](/tutorial/custom/custom-storage-adapter). +When using alova in the client, `localStorage` is used as the L2 storage adapter by default. You can also [customize the storage adapter](/next/tutorial/advanced/custom/storage-adapter). Set on the method instance: @@ -188,7 +187,7 @@ Some application scenarios are as follows: 3. Integrate data merging and processing of multiple downstream servers. Multiple serial requests may lead to longer response time, and may also consume performance due to complex data conversion. The converted data can be cached. 4. API rate limit and billing, weather forecast service API updates weather information once an hour, geographic location data API, etc. -When using alova on the server, there is no L2 storage adapter by default. The implementation of file storage adapter and redis adapter are provided in [Server-side L2 storage practice](/tutorial/best-practice/l2-storage). You can also [customize storage adapter](/tutorial/custom/custom-storage-adapter), for example, use MongoDB, MySQL and other databases as storage adapters for response data. +When using alova on the server, there is no L2 storage adapter by default. The implementation of file storage adapter and redis adapter are provided in [Server-side L2 storage practice](/next/tutorial/project/best-practice/l2-storage). You can also [customize storage adapter](/next/tutorial/advanced/custom/storage-adapter), for example, use MongoDB, MySQL and other databases as storage adapters for response data. :::warning Note @@ -328,4 +327,4 @@ cacheFor: { ## Automatic response maintenance instructions -The key of the response data cache is a combination of the request method (method), request address (url), request header parameters (headers), url parameters (params), and request body parameters (requestBody) of the method instance as a unique identifier. Any information or location will be treated as a different key. If you want to customize the cache key, you can refer to [Custom method key](/tutorial/advanced/custom-method-key). +The key of the response data cache is a combination of the request method (method), request address (url), request header parameters (headers), url parameters (params), and request body parameters (requestBody) of the method instance as a unique identifier. Any information or location will be treated as a different key. If you want to customize the cache key, you can refer to [Custom method key](/next/tutorial/advanced/in-depth/custom-method-key). diff --git a/docs/tutorial/05-cache/02-auto-invalidate.md b/docs/tutorial/05-cache/02-auto-invalidate.md index 495505970..54c968281 100644 --- a/docs/tutorial/05-cache/02-auto-invalidate.md +++ b/docs/tutorial/05-cache/02-auto-invalidate.md @@ -1,6 +1,5 @@ --- title: Auto Invalidate -sidebar_position: 20 --- Automatic invalidation cache is to set the matching rules of the invalidation source method in the target cache. When the source method is successfully requested, the target cache will be automatically matched and invalidated, and there is no need to manually clear the cache. When the target cache and the invalidation source are one-to-one or one-to-many, it is very convenient to set the automatic invalidation rule. diff --git a/docs/tutorial/05-cache/03-manually-invalidate.md b/docs/tutorial/05-cache/03-manually-invalidate.md index a97a2ce0e..26df38876 100644 --- a/docs/tutorial/05-cache/03-manually-invalidate.md +++ b/docs/tutorial/05-cache/03-manually-invalidate.md @@ -1,6 +1,5 @@ --- title: Manual Invalidate -sidebar_position: 30 --- Generally, automatic cache invalidation is more concise and is recommended to be used first to invalidate the cache. When automatic cache invalidation does not meet the needs, you can also invalidate the cache by calling `invalidateCache`. @@ -53,7 +52,7 @@ invalidateCache([method1, method2, ...]); ## Dynamically invalidate cache -Sometimes you may not be sure which cache data needs to be invalidated. We can use [method snapshot matcher](/tutorial/advanced/method-matcher) to dynamically find the corresponding method instance. The following example shows how to invalidate the cache of the first five instances of a method called todoList. +Sometimes you may not be sure which cache data needs to be invalidated. We can use [method snapshot matcher](/next/tutorial/client/in-depth/method-matcher) to dynamically find the corresponding method instance. The following example shows how to invalidate the cache of the first five instances of a method called todoList. ```javascript const getTodoList = currentPage => { @@ -89,7 +88,7 @@ onSuccess(() => { }); ``` -> For more usage of method matchers, see [method snapshot matcher](/tutorial/advanced/method-matcher) +> For more usage of method matchers, see [method snapshot matcher](/next/tutorial/client/in-depth/method-matcher) ## Invalidate all caches diff --git a/docs/tutorial/05-cache/04-force-request.md b/docs/tutorial/05-cache/04-force-request.md index 2b52a9b84..30494990f 100644 --- a/docs/tutorial/05-cache/04-force-request.md +++ b/docs/tutorial/05-cache/04-force-request.md @@ -1,6 +1,5 @@ --- title: Forced Request -sidebar_position: 40 --- Forced request is a mechanism that bypasses cache checks to trigger request sending. It is useful when you need to get the latest data under certain conditions. @@ -15,4 +14,4 @@ const response = await alovaInstance.Get('/api/user').send(true); ## Forced request in useHook -Please go to [Automatically manage request status-Forced request](/tutorial/client/use-request) for details. +Please go to [Automatically manage request status-Forced request](/next/tutorial/client/strategy/use-request) for details. diff --git a/docs/tutorial/05-cache/05-set-and-query.md b/docs/tutorial/05-cache/05-set-and-query.md index ce8579804..84e1f0fa4 100644 --- a/docs/tutorial/05-cache/05-set-and-query.md +++ b/docs/tutorial/05-cache/05-set-and-query.md @@ -1,12 +1,11 @@ --- title: Set & Query Cache -sidebar_position: 50 --- import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -Cache also supports update and search. In [cache mode](/tutorial/cache/mode), we mentioned that each cache data is saved with the method instance that sent the request as the key, so when manually updating the cache, the method instance will also be used to search for the corresponding cache data. +Cache also supports update and search. In [cache mode](/next/tutorial/cache/mode), we mentioned that each cache data is saved with the method instance that sent the request as the key, so when manually updating the cache, the method instance will also be used to search for the corresponding cache data. ## Update cache @@ -64,7 +63,7 @@ import { queryCache } from 'alova'; const cachedData = await queryCache(getTodoListByDate('2022-10-01')); ``` -You can also dynamically find method instances through [method snapshot matchers](/tutorial/advanced/method-matcher). +You can also dynamically find method instances through [method snapshot matchers](/next/tutorial/client/in-depth/method-matcher). ```javascript const lastMethod = alovaInstance.snapshots.match('todoList', true); diff --git a/docs/tutorial/05-cache/06-controlled-cache.md b/docs/tutorial/05-cache/06-controlled-cache.md index c75effe38..9b183b025 100644 --- a/docs/tutorial/05-cache/06-controlled-cache.md +++ b/docs/tutorial/05-cache/06-controlled-cache.md @@ -1,6 +1,5 @@ --- title: Controlled Cache -sidebar_position: 60 --- When sending a request, the default response cache will be matched first. In some cases, you may need to use `IndexedDB` as a cache management solution and use a custom `IndexedDB` adapter, but this will make all requests use it as a storage solution, while controlled cache allows you to control the custom cache from a single request. diff --git a/docs/tutorial/05-cache/_category_.json b/docs/tutorial/05-cache/_category_.json index f8845fd87..0a5fe043b 100644 --- a/docs/tutorial/05-cache/_category_.json +++ b/docs/tutorial/05-cache/_category_.json @@ -1,3 +1,5 @@ { - "label": "Cache Details" + "label": "Cache Details", + "collapsed": false, + "collapsible": false } diff --git a/docs/tutorial/06-advanced/05-custom-method-key.md b/docs/tutorial/06-advanced/01-in-depth/05-custom-method-key.md similarity index 98% rename from docs/tutorial/06-advanced/05-custom-method-key.md rename to docs/tutorial/06-advanced/01-in-depth/05-custom-method-key.md index 185fa5e64..194412be7 100644 --- a/docs/tutorial/06-advanced/05-custom-method-key.md +++ b/docs/tutorial/06-advanced/01-in-depth/05-custom-method-key.md @@ -1,6 +1,5 @@ --- title: Custom Method Key -sidebar_position: 50 --- :::info Usage scope diff --git a/docs/tutorial/06-advanced/07-cache-logger.md b/docs/tutorial/06-advanced/01-in-depth/07-cache-logger.md similarity index 98% rename from docs/tutorial/06-advanced/07-cache-logger.md rename to docs/tutorial/06-advanced/01-in-depth/07-cache-logger.md index 1462d92e2..c655c845b 100644 --- a/docs/tutorial/06-advanced/07-cache-logger.md +++ b/docs/tutorial/06-advanced/01-in-depth/07-cache-logger.md @@ -1,6 +1,5 @@ --- title: Cache Logger -sidebar_position: 70 --- :::info Usage scope diff --git a/docs/tutorial/06-advanced/09-ssr.md b/docs/tutorial/06-advanced/01-in-depth/09-ssr.md similarity index 96% rename from docs/tutorial/06-advanced/09-ssr.md rename to docs/tutorial/06-advanced/01-in-depth/09-ssr.md index fe8c1c177..a58aadfd8 100644 --- a/docs/tutorial/06-advanced/09-ssr.md +++ b/docs/tutorial/06-advanced/01-in-depth/09-ssr.md @@ -1,6 +1,5 @@ --- title: Server-Side Rendering(SSR) -sidebar_position: 90 --- import Tabs from '@theme/Tabs'; @@ -37,7 +36,7 @@ In Nuxt3.x, `useAsyncData` is provided to initialize page data on server, and `u ### Nextjs -Nextjs provides fixed server-side initialization page data functions, such as `getStaticProps`, `getServerSideProps`, etc., you can [directly use the method instance](/tutorial/getting-started/quick-start) call apis in the function. +Nextjs provides fixed server-side initialization page data functions, such as `getStaticProps`, `getServerSideProps`, etc., you can [directly use the method instance](/next/tutorial/getting-started/quick-start) call apis in the function. ```jsx export const getServerSideProps = async ctx => { @@ -64,7 +63,7 @@ export default function App(props) { ### Sveltekit -Sveltekit also provides the `load` function to initialize the page data on server, and you can also [directly use the method instance](/tutorial/getting-started/quick-start) call apis in the function. For example, call apis in `+page.server.js`. +Sveltekit also provides the `load` function to initialize the page data on server, and you can also [directly use the method instance](/next/tutorial/getting-started/quick-start) call apis in the function. For example, call apis in `+page.server.js`. ```javascript title=+page.server.js const todoListGetter = alovaInstance.Get('/todo/list', { diff --git a/docs/tutorial/03-client/20-typescript.md b/docs/tutorial/06-advanced/01-in-depth/10-typescript.md similarity index 86% rename from docs/tutorial/03-client/20-typescript.md rename to docs/tutorial/06-advanced/01-in-depth/10-typescript.md index de279e29c..d93dd85f0 100644 --- a/docs/tutorial/03-client/20-typescript.md +++ b/docs/tutorial/06-advanced/01-in-depth/10-typescript.md @@ -1,159 +1,173 @@ ---- -title: Typescript -sidebar_position: 100 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -In terms of Typescript, we have indeed spent a lot of effort on optimization in order to provide a better user experience. We try our best to use automatic type inference to reduce the trouble of defining types for you. - -## Automatically infer alova useHooks states type - -When createAlova creates an alova instance, the state types created by `useRequest`, `useWatcher`, and `useFetcher` will be automatically inferred based on the passed `statesHook`. Currently only Vue, React, and Svelte are supported. - -> `useFetcher` is a useHook used for data fetching. For details, please read [Advanced-Data fetching chapter](/tutorial/advanced/use-fetcher). - -The following are the status types returned by useHooks by default. - - - - -```typescript -const vueAlova = createAlova({ - statesHook: VueHook - // ... -}); -const { - loading, // Ref - data, // Ref<{ data: any }> - error // Ref -} = useRequest(vueAlova.Get<{ data: any }>('/todo/list')); -``` - - - - -```typescript -const reactAlova = createAlova({ - statesHook: ReactHook - // ... -}); -const { - loading, // boolean - data, // { data: any } - error // Error -} = useRequest(reactAlova.Get<{ data: any }>('/todo/list')); -``` - - - - -```typescript -const svelteAlova = createAlova({ - statesHook: SvelteHook - // ... -}); -const { - loading, // Writable - data, // Writable<{ data: any }> - error // Writable -} = useRequest(svelteAlova.Get<{ data: any }>('/todo/list')); -``` - - - - -The type of data will be different depending on the response data type specified in different Method instances, let's continue to look below. - -## Type of response data - -When you specify a type for a data interface, you need to divide it into two situations. - -### Case 1 - -When the response data does not need to be converted by calling `transformData`, the type can be specified directly through generics. - -```typescript -interface Todo { - title: string; - time: string; - done: boolean; -} -const Get = alovaInstance.Get('/todo/list'); -const { data } = useRequest(Get); -// vue: The type of data is Ref -// react: The type of data is Todo[] -// svelte: The type of data is Writable -``` - -### Case 2 - -When the response data needs to be converted by calling `transformData`, the type needs to be specified in the conversion function parameter, and then its return value type will be used as the response data type. - -```typescript -interface Todo { - title: string; - time: string; - done: boolean; -} -const Get = alovaInstance.Get('/todo/list', { - //Write the type into the data parameter, and the headers will be automatically inferred, so you don’t need to specify the type. - transformData(data: Todo[], headers) { - return data.map(item => ({ - ...item, - status: item.done ? 'Completed' : 'Not completed' - })); - } -}); - -const { data } = useRequest(Get); -// vue: The type of data is Ref<(Todo & { status: string })[]> -// react: The type of data is (Todo & { status: string })[] -// svelte: The type of data is Writable<(Todo & { status: string })[]> -``` - -:::warning note - -The response data is converted by the global response interceptor, so when setting the type, it should also be set to the converted type. - -::: - -## Type inferred from request adapter - -Because alova supports custom request adapters, and the request configuration objects, response objects, and response headers of different adapters may be different, so the global `beforeRequest`, `responded` interceptors, and the configuration object when the `Method` instance is created The types will be automatically inferred based on the types provided by the request adapter. Let's look at these types first. - -If you are using [**GlobalFetch**](https://github.com/alovajs/alova/blob/main/src/predefine/GlobalFetch.ts), alova will automatically infer the type using `fetch api`, The types of fetch api are as follows. - -```typescript -declare function fetch(input: RequestInfo | URL, init?: RequestInit): Promise; -``` - -### Method configuration type of instance - -The method configuration type will be automatically inferred as: - -```typescript -// AlovaMethodCommonConfig is a unified request parameter and behavior parameter -// highlight-start -const methodConfig: AlovaMethodCommonConfig & RequestInit = { - // highlight-end - // ... -}; -alovaInstance.Get('/api/user', methodConfig); -``` - -### Global response interceptor parameter type - -The type of responded interceptor will be automatically inferred as: - -```typescript -createAlova({ - // ... - // highlight-start - responded: (response: Response, method: Method) => { - // highlight-end - // ... - } -}); -``` +--- +title: Typescript +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +In terms of Typescript, we have indeed spent a lot of effort on optimization in order to provide a better user experience. We try our best to use automatic type inference to reduce the trouble of defining types for you. + +## Automatically infer alova useHooks states type + +When createAlova creates an alova instance, the state types created by all useHooks such as `useRequest`, `useWatcher`, and `useFetcher` are automatically inferred based on the passed `statesHook`. + +The following are the status types returned by useHooks by default. + + + + +```typescript +const vueAlova = createAlova({ + statesHook: VueHook + // ... +}); +const { + loading, // Ref + data, // Ref<{ data: any }> + error // Ref +} = useRequest(vueAlova.Get<{ data: any }>('/todo/list')); +``` + + + + +```typescript +const reactAlova = createAlova({ + statesHook: ReactHook + // ... +}); +const { + loading, // boolean + data, // { data: any } + error // Error +} = useRequest(reactAlova.Get<{ data: any }>('/todo/list')); +``` + + + + +```typescript +const svelteAlova = createAlova({ + statesHook: SvelteHook + // ... +}); +const { + loading, // Writable + data, // Writable<{ data: any }> + error // Writable +} = useRequest(svelteAlova.Get<{ data: any }>('/todo/list')); +``` + + + + +The type of data will be different depending on the response data type specified in different Method instances, let's continue to look below. + +## Type of response data + +When you specify a type for a data interface, you need to divide it into two situations. + +### Case 1 + +When the response data does not need to be converted by calling `transformData`, the type can be specified directly through generics. + +```typescript +interface Todo { + title: string; + time: string; + done: boolean; +} +const Get = alovaInstance.Get('/todo/list'); +const { data } = useRequest(Get); +// vue: The type of data is Ref +// react: The type of data is Todo[] +// svelte: The type of data is Writable +``` + +### Case 2 + +When the response data needs to be converted by calling `transformData`, the type needs to be specified in the conversion function parameter, and then its return value type will be used as the response data type. + +```typescript +interface Todo { + title: string; + time: string; + done: boolean; +} +const Get = alovaInstance.Get('/todo/list', { + //Write the type into the data parameter, and the headers will be automatically inferred, so you don’t need to specify the type. + transformData(data: Todo[], headers) { + return data.map(item => ({ + ...item, + status: item.done ? 'Completed' : 'Not completed' + })); + } +}); + +const { data } = useRequest(Get); +// vue: The type of data is Ref<(Todo & { status: string })[]> +// react: The type of data is (Todo & { status: string })[] +// svelte: The type of data is Writable<(Todo & { status: string })[]> +``` + +:::warning note + +The response data is converted by the global response interceptor, so when setting the type, it should also be set to the converted type. + +::: + +## Type inferred from request adapter + +Because alova supports custom request adapters, and the request configuration objects, response objects, and response headers of different adapters may be different, so the global `beforeRequest`, `responded` interceptors, and the configuration object when the `Method` instance is created The types will be automatically inferred based on the types provided by the request adapter. Let's look at these types first. + +If you are using `alova/fetch`, alova will automatically infer the type using `fetch api`, The types of fetch api are as follows. + +```typescript +declare function fetch(input: RequestInfo | URL, init?: RequestInit): Promise; +``` + +### Method configuration type of instance + +The method configuration type will be automatically inferred as: + +```typescript +// AlovaMethodCommonConfig is a unified request parameter and behavior parameter +// highlight-start +const methodConfig: AlovaMethodCommonConfig & RequestInit = { + // highlight-end + // ... +}; +alovaInstance.Get('/api/user', methodConfig); +``` + +### Global response interceptor parameter type + +The type of responded interceptor will be automatically inferred as: + +```typescript +createAlova({ + // ... + // highlight-start + responded: (response: Response, method: Method) => { + // highlight-end + // ... + } +}); +``` + +## Custom meta type + +By default, the type of method meta is `any`. When you need a standard format, you can configure the following type in the project's declaration file. + +```ts +import 'alova'; + +declare module 'alova' { + export interface AlovaCustomTypes { + meta: { + role: string; + errorModal: boolean; + }; + } +} +``` diff --git a/docs/tutorial/06-advanced/README.md b/docs/tutorial/06-advanced/01-in-depth/README.md similarity index 100% rename from docs/tutorial/06-advanced/README.md rename to docs/tutorial/06-advanced/01-in-depth/README.md diff --git a/docs/tutorial/06-advanced/01-in-depth/_category_.json b/docs/tutorial/06-advanced/01-in-depth/_category_.json new file mode 100644 index 000000000..41f002209 --- /dev/null +++ b/docs/tutorial/06-advanced/01-in-depth/_category_.json @@ -0,0 +1,3 @@ +{ + "label": "alova In-Depth" +} diff --git a/docs/tutorial/08-custom/01-http-adapter.md b/docs/tutorial/06-advanced/02-custom/01-http-adapter.md similarity index 99% rename from docs/tutorial/08-custom/01-http-adapter.md rename to docs/tutorial/06-advanced/02-custom/01-http-adapter.md index 2b4e60102..17e507224 100644 --- a/docs/tutorial/08-custom/01-http-adapter.md +++ b/docs/tutorial/06-advanced/02-custom/01-http-adapter.md @@ -1,6 +1,5 @@ --- title: Request Adapter -sidebar_position: 10 --- Remember how to create an Alova instance? diff --git a/docs/tutorial/08-custom/02-storage-adapter.md b/docs/tutorial/06-advanced/02-custom/02-storage-adapter.md similarity index 99% rename from docs/tutorial/08-custom/02-storage-adapter.md rename to docs/tutorial/06-advanced/02-custom/02-storage-adapter.md index ae4072066..ce7be8b92 100644 --- a/docs/tutorial/08-custom/02-storage-adapter.md +++ b/docs/tutorial/06-advanced/02-custom/02-storage-adapter.md @@ -1,6 +1,5 @@ --- title: Storage Adapter -sidebar_position: 20 --- import Tabs from '@theme/Tabs'; diff --git a/docs/tutorial/08-custom/03-client-strategy.md b/docs/tutorial/06-advanced/02-custom/03-client-strategy.md similarity index 97% rename from docs/tutorial/08-custom/03-client-strategy.md rename to docs/tutorial/06-advanced/02-custom/03-client-strategy.md index b8a8dc2eb..ac8d58b04 100644 --- a/docs/tutorial/08-custom/03-client-strategy.md +++ b/docs/tutorial/06-advanced/02-custom/03-client-strategy.md @@ -1,19 +1,18 @@ --- title: Client Strategy -sidebar_position: 30 --- alova's client strategies are divided into three types: middleware, interceptor, and useHook. When your project needs customization, you can refer to this section. ## Middleware -Middleware provides powerful capabilities that can control almost all behaviors of a request. You can use it to control request behavior, customize request status, error handling, etc. For details, please go to [Request Middleware](/tutorial/advanced/middleware) to view. The following source code can tell you what the middleware can do. +Middleware provides powerful capabilities that can control almost all behaviors of a request. You can use it to control request behavior, customize request status, error handling, etc. For details, please go to [Request Middleware](/next/tutorial/client/in-depth/middleware) to view. The following source code can tell you what the middleware can do. - [actionDelegationMiddleware](https://github.com/alovajs/alova/blob/main/packages/client/src/middlewares/actionDelegation.ts) Cross-component triggering requests are implemented through middleware. - [useSQRequest](https://github.com/alovajs/alova/blob/main/packages/client/src/hooks/silent/useSQRequest.ts) Implement immediate response requests in middleware without waiting. - [useSerialRequest](https://github.com/alovajs/alova/blob/main/packages/client/src/hooks/serial/useSerialRequest.ts) Serialize requests in middleware and manage response data for multiple requests. - [useRetriableRequest](https://github.com/alovajs/alova/blob/main/packages/client/src/hooks/useRetriableRequest.ts) Retry failed requests in middleware. -- [Delayed update loading](/tutorial/best-practice/middleware) example. +- [Delayed update loading](/next/tutorial/project/best-practice/middleware) example. ## Interceptor diff --git a/docs/tutorial/08-custom/04-server-strategy.md b/docs/tutorial/06-advanced/02-custom/04-server-strategy.md similarity index 98% rename from docs/tutorial/08-custom/04-server-strategy.md rename to docs/tutorial/06-advanced/02-custom/04-server-strategy.md index f68153ed8..fea23a344 100644 --- a/docs/tutorial/08-custom/04-server-strategy.md +++ b/docs/tutorial/06-advanced/02-custom/04-server-strategy.md @@ -1,6 +1,5 @@ --- title: Server Strategy -sidebar_position: 40 --- Server strategy is also called `Server hook`, which is a decorated function of a method instance. diff --git a/docs/tutorial/08-custom/05-stateshook.md b/docs/tutorial/06-advanced/02-custom/05-stateshook.md similarity index 99% rename from docs/tutorial/08-custom/05-stateshook.md rename to docs/tutorial/06-advanced/02-custom/05-stateshook.md index 1f3f52aa0..33b7e4a24 100644 --- a/docs/tutorial/08-custom/05-stateshook.md +++ b/docs/tutorial/06-advanced/02-custom/05-stateshook.md @@ -1,6 +1,5 @@ --- title: States Hook -sidebar_position: 50 --- Remember how to create an Alova instance? diff --git a/docs/tutorial/08-custom/README.md b/docs/tutorial/06-advanced/02-custom/README.md similarity index 88% rename from docs/tutorial/08-custom/README.md rename to docs/tutorial/06-advanced/02-custom/README.md index 53c4300d9..3e24ff75b 100644 --- a/docs/tutorial/08-custom/README.md +++ b/docs/tutorial/06-advanced/02-custom/README.md @@ -12,11 +12,11 @@ In order to meet the running requirements of js in different environments, you c - [localStorage storage adapter](https://github.com/alovajs/alova/blob/main/packages/alova/src/defaults/cacheAdapter.ts) - [vue states hook](https://github.com/alovajs/alova/blob/main/packages/client/src/statesHook/vue.ts) -You can also group multiple types of adapters into a collection, such as [Uniapp adapter](/tutorial/request-adapter/alova-adapter-uniapp). +You can also group multiple types of adapters into a collection, such as [Uniapp adapter](/next/resource/request-adapter/alova-adapter-uniapp). ## Custom client strategy -alova provides 10+ custom client strategy modules, but sometimes you may need to write your own strategy module. Usually, a custom request strategy is based on the combination of the three core useHooks of `useRequest`, `useWatcher` and `useFetcher`, and writes [middleware](/tutorial/advanced/middleware) and cache manipulation functions for them to control their request methods, so as to achieve various request strategies +alova provides 10+ custom client strategy modules, but sometimes you may need to write your own strategy module. Usually, a custom request strategy is based on the combination of the three core useHooks of `useRequest`, `useWatcher` and `useFetcher`, and writes [middleware](/next/tutorial/client/in-depth/middleware) and cache manipulation functions for them to control their request methods, so as to achieve various request strategies The following strategy modules are very representative, and it is strongly recommended that you refer to the source code for inspiration. diff --git a/docs/tutorial/08-custom/_category_.json b/docs/tutorial/06-advanced/02-custom/_category_.json similarity index 100% rename from docs/tutorial/08-custom/_category_.json rename to docs/tutorial/06-advanced/02-custom/_category_.json diff --git a/docs/tutorial/06-advanced/_category_.json b/docs/tutorial/06-advanced/_category_.json index 3462df72e..e29c61856 100644 --- a/docs/tutorial/06-advanced/_category_.json +++ b/docs/tutorial/06-advanced/_category_.json @@ -1,3 +1,5 @@ -{ - "label": "Advanced" -} +{ + "label": "Advanced", + "collapsed": false, + "collapsible": false +} diff --git a/docs/tutorial/07-best-practice/01-manage-apis.md b/docs/tutorial/07-project/01-best-practice/01-manage-apis.md similarity index 93% rename from docs/tutorial/07-best-practice/01-manage-apis.md rename to docs/tutorial/07-project/01-best-practice/01-manage-apis.md index 54127698e..94899f868 100644 --- a/docs/tutorial/07-best-practice/01-manage-apis.md +++ b/docs/tutorial/07-project/01-best-practice/01-manage-apis.md @@ -1,154 +1,153 @@ ---- -title: Manage APIs -sidebar_position: 10 ---- - -In a project, we may need to use hundreds or thousands of request APIs, so managing these request APIs becomes particularly important. - -You may write the request code like the code snippet in [quick start](/tutorial/getting-started/quick-start). all codes in one file. - -```javascript -const { loading, data, error } = useRequest( - alovaInstance.Get('https://api.alovajs.org/profile', { - params: { - id: 1 - } - }) -); -``` - -This is just for beginners to understand, but in actual projects, we do not recommend this, because the method instance is not only used to send requests, it can also be used to operate cache and state, the above usage will make these request api become It's unmanageable, and if you think it's wrong, you might forget a little: - -> The key of the response data cache is uniquely identified by the combination of the method instance’s request method (method), request address (url), request header parameters (headers), url parameters (params), and request body parameters (requestBody). Or different positions will be treated as different keys. - -Therefore, in actual projects, method instances should be managed, and alova instances can also be managed uniformly. - -## api file structure - -First of all, your project needs a folder that uniformly stores method instances and alova instances, for example called `api`, the following is a common api management structure, and you can also use any structure suitable for the project. - -``` -|-api -| |-index.js -> contains all alova instances -| |-methods -| | |-user.js -| | |-article.js -| | |-order.js -| | |-... -|-... -``` - -In short, your project should use a suitable folder structure to organize them. - -> Next, take vue as an example to show the sample code - -## Manage alova instance - -Your project may need to communicate with different servers, or you may need to use special request schemes in specific requests, or use different response interceptors, etc. All of these require creating and maintaining multiple alova instances in the project. It is recommended to Use a separate file to manage them, for example in the above api management structure, will use `api/index.js` to manage. - -```javascript title=api/index.js -import { createAlova } from 'alova'; -import VueHook from 'alova/vue'; -import GlobalFetch from 'alova/GlobalFetch'; -import { axiosRequestAdapter } from '@alova/adapter-axios'; - -// user alova instance -export const userAlova = createAlova({ - baseURL: 'https://api-user.alovajs.org', - statesHook: VueHook, - requestAdapter: GlobalFetch(), - async responded(method) { - method.config.headers.token = 'user token'; - } -}); - -// order alova instance -export const orderAlova = createAlova({ - baseURL: 'https://api-order.alovajs.org', - statesHook: VueHook, - requestAdapter: GlobalFetch(), - async responded(method) { - method.config.headers.token = 'order token'; - } -}); - -// upload alova instance -export const uploadAlova = createAlova({ - baseURL: 'https://api-order.alovajs.org', - statesHook: VueHook, - requestAdapter: axiosRequestAdapter() -}); -``` - -## Manage method instances - -We can use different js files to classify and manage method instances. For example, in the above api management structure, `api/methods/user.js` will be used to manage method instances related to user information, and `api/methods/order.js` will be used `A method instance related to order management. - -In addition, as mentioned above, in addition to sending requests, method instances can also be used to operate caches and states. In order to ensure the number and order of request parameters, we can use a function to correspond to a request API, through The corresponding method instance is returned in the form of incoming request parameters. As long as the incoming parameters are the same, the request information and parameter order of the method instance are also the same, so as to ensure that the method instance used to operate the cache and state is correct. - -```javascript title=api/methods/user.js -import { userAlova } from '..'; - -// Get user information -export const getUserInfo = id => userAlova.Get('/user/' + id); - -// Edit user information -export const editUserInfo = (name, age, mobile) => - userAlova.Post('/user', { - name, - age, - mobile - }); - -// remove user -export const removeUser = id => userAlova.Delete('/user/' + id); - -//... -``` - -In the **user component**, the method function can be directly imported for use, and the method function can be used again to invalidate the corresponding cache after calling `invalidateCache`. - -```html title=views/user.vue - - -``` +--- +title: Manage APIs +--- + +In a project, we may need to use hundreds or thousands of request APIs, so managing these request APIs becomes particularly important. + +You may write the request code like the code snippet in [quick start](/next/tutorial/getting-started/quick-start). all codes in one file. + +```javascript +const { loading, data, error } = useRequest( + alovaInstance.Get('https://api.alovajs.org/profile', { + params: { + id: 1 + } + }) +); +``` + +This is just for beginners to understand, but in actual projects, we do not recommend this, because the method instance is not only used to send requests, it can also be used to operate cache and state, the above usage will make these request api become It's unmanageable, and if you think it's wrong, you might forget a little: + +> The key of the response data cache is uniquely identified by the combination of the method instance’s request method (method), request address (url), request header parameters (headers), url parameters (params), and request body parameters (requestBody). Or different positions will be treated as different keys. + +Therefore, in actual projects, method instances should be managed, and alova instances can also be managed uniformly. + +## api file structure + +First of all, your project needs a folder that uniformly stores method instances and alova instances, for example called `api`, the following is a common api management structure, and you can also use any structure suitable for the project. + +``` +|-api +| |-index.js -> contains all alova instances +| |-methods +| | |-user.js +| | |-article.js +| | |-order.js +| | |-... +|-... +``` + +In short, your project should use a suitable folder structure to organize them. + +> Next, take vue as an example to show the sample code + +## Manage alova instance + +Your project may need to communicate with different servers, or you may need to use special request schemes in specific requests, or use different response interceptors, etc. All of these require creating and maintaining multiple alova instances in the project. It is recommended to Use a separate file to manage them, for example in the above api management structure, will use `api/index.js` to manage. + +```javascript title=api/index.js +import { createAlova } from 'alova'; +import VueHook from 'alova/vue'; +import adapterFetch from 'alova/fetch'; +import { axiosRequestAdapter } from '@alova/adapter-axios'; + +// user alova instance +export const userAlova = createAlova({ + baseURL: 'https://api-user.alovajs.org', + statesHook: VueHook, + requestAdapter: adapterFetch(), + async responded(method) { + method.config.headers.token = 'user token'; + } +}); + +// order alova instance +export const orderAlova = createAlova({ + baseURL: 'https://api-order.alovajs.org', + statesHook: VueHook, + requestAdapter: adapterFetch(), + async responded(method) { + method.config.headers.token = 'order token'; + } +}); + +// upload alova instance +export const uploadAlova = createAlova({ + baseURL: 'https://api-order.alovajs.org', + statesHook: VueHook, + requestAdapter: axiosRequestAdapter() +}); +``` + +## Manage method instances + +We can use different js files to classify and manage method instances. For example, in the above api management structure, `api/methods/user.js` will be used to manage method instances related to user information, and `api/methods/order.js` will be used `A method instance related to order management. + +In addition, as mentioned above, in addition to sending requests, method instances can also be used to operate caches and states. In order to ensure the number and order of request parameters, we can use a function to correspond to a request API, through The corresponding method instance is returned in the form of incoming request parameters. As long as the incoming parameters are the same, the request information and parameter order of the method instance are also the same, so as to ensure that the method instance used to operate the cache and state is correct. + +```javascript title=api/methods/user.js +import { userAlova } from '..'; + +// Get user information +export const getUserInfo = id => userAlova.Get('/user/' + id); + +// Edit user information +export const editUserInfo = (name, age, mobile) => + userAlova.Post('/user', { + name, + age, + mobile + }); + +// remove user +export const removeUser = id => userAlova.Delete('/user/' + id); + +//... +``` + +In the **user component**, the method function can be directly imported for use, and the method function can be used again to invalidate the corresponding cache after calling `invalidateCache`. + +```html title=views/user.vue + + +``` diff --git a/docs/tutorial/07-best-practice/02-skills.md b/docs/tutorial/07-project/01-best-practice/02-skills.md similarity index 93% rename from docs/tutorial/07-best-practice/02-skills.md rename to docs/tutorial/07-project/01-best-practice/02-skills.md index ab20c258e..539089158 100644 --- a/docs/tutorial/07-best-practice/02-skills.md +++ b/docs/tutorial/07-project/01-best-practice/02-skills.md @@ -1,6 +1,5 @@ --- title: Skills -sidebar_position: 20 --- import Tabs from '@theme/Tabs'; @@ -226,7 +225,7 @@ const handleInvalidateCache = id => { If your project needs to use mock data to simulate some or all interfaces in the development environment, and switch back to real network requests in production, you can control it through environment variables. ```javascript -const globalFetch = GlobalFetch(); +const globalFetch = adapterFetch(); const mockAdapter = createAlovaMockAdapter([mockGroup1 /** ... */], { httpAdapter: globalFetch, delay: 1000 @@ -241,7 +240,7 @@ export const alovaInst = createAlova({ }); ``` -And it is recommended that different developers in the team can create different mock interface data according to the version number of each iteration, so as to manage these mock data in the team. For details, please refer to the chapter of [mock Data](/tutorial/request-adapter/alova-mock) . +And it is recommended that different developers in the team can create different mock interface data according to the version number of each iteration, so as to manage these mock data in the team. For details, please refer to the chapter of [mock Data](/next/resource/request-adapter/alova-mock) . ## Use useRequest to make parallel requests @@ -259,8 +258,16 @@ But such a request only applies to simple parallel requests. If you need to perf Manually create a promise object and use `Promise.all` to complete the effect. ```javascript -const { data: todoList, onSuccess: onListSuccess, onError: onListError } = useRequest(todoListGetter); -const { data: todoCounter, onSuccess: onCountSuccess, onError: onCountError } = useRequest(todoCountGetter); +const { + data: todoList, + onSuccess: onListSuccess, + onError: onListError +} = useRequest(todoListGetter); +const { + data: todoCounter, + onSuccess: onCountSuccess, + onError: onCountError +} = useRequest(todoCountGetter); // Manually create promise object const listPromise = new Promise((resolve, reject) => { @@ -302,7 +309,10 @@ Let the first request be sent automatically, and the second request be triggered ```javascript // const { data: todoList, onSuccess } = useRequest(todoListGetter); -const { data: todoDetail, send: sendTodoDetail } = useRequest(todoId => todoDetailGetter(todoId), { immediate: false }); +const { data: todoDetail, send: sendTodoDetail } = useRequest( + todoId => todoDetailGetter(todoId), + { immediate: false } +); // Get the list first, then get the details of the first todo onSuccess(event => { @@ -317,7 +327,9 @@ Using the `send` function returned by the `useRequest` function, calling `send` ```javascript // Let them not automatically send requests first const { send: sendList } = useRequest(todoListGetter, { immediate: false }); -const { send: sendTodoDetail } = useRequest(todoId => todoDetailGetter(todoId), { immediate: false }); +const { send: sendTodoDetail } = useRequest(todoId => todoDetailGetter(todoId), { + immediate: false +}); //Use the promise object returned by the send function const serialRequest = async () => { @@ -327,4 +339,4 @@ const serialRequest = async () => { }; ``` -> For serial requests, it is recommended to use [useSerialRequest](/tutorial/strategy/useSerialRequest) and [useSerialWatcher](/tutorial/strategy/useSerialWatcher) directly. +> For serial requests, it is recommended to use [useSerialRequest](/next/tutorial/client/strategy/use-serial-request) and [useSerialWatcher](/next/tutorial/client/strategy/use-serial-watcher) directly. diff --git a/docs/tutorial/07-best-practice/03-manage-cache-by-indexeddb.md b/docs/tutorial/07-project/01-best-practice/03-manage-cache-by-indexeddb.md similarity index 90% rename from docs/tutorial/07-best-practice/03-manage-cache-by-indexeddb.md rename to docs/tutorial/07-project/01-best-practice/03-manage-cache-by-indexeddb.md index c3cc9a96a..f3099ba28 100644 --- a/docs/tutorial/07-best-practice/03-manage-cache-by-indexeddb.md +++ b/docs/tutorial/07-project/01-best-practice/03-manage-cache-by-indexeddb.md @@ -1,123 +1,122 @@ ---- -title: Manage Cache with IndexedDB -sidebar_position: 30 ---- - -If you are developing applications that require extensive use of local cache, such as graphics editing applications, file management applications, etc., the low-capacity localStorage can no longer meet the development needs. At this time, you can use IndexedDB and alova for large-capacity local cache management. - -This feature is mainly due to alova's [Controlled Cache](/tutorial/cache/controlled-cache) function, which can implement custom cache management. Let's take a look at the practical steps. - -Here is an [example of managed cache with IndexedDB](/tutorial/example/controlled-cache-by-indexeddb) - -Let's take custom management of large image data as an example. - -## Create IndexedDB instance - -First create an IndexedDB instance to operate the local cache, and export the cache operate function. - -```javascript title=db.js -const dbVersion = 1; -let dbInstance; -const request = window.indexedDB.open('MyTestDatabase', dbVersion); -request.onupgradeneeded = ({ target }) => { - dbInstance = target.result; - const imgStore = dbInstance.createObjectStore('images', { - autoIncrement: true - }); - imgStore.createIndex('fileName', 'fileName', { - unique: true - }); -}; -request.onerror = () => { - throw new Error('Database open fail'); -}; -request.onsuccess = ({ target }) => { - dbInstance = target.result; -}; - -// Add new data to IndexedDB -export const addImage2Cache = async (fileName, data) => { - const tx = dbInstance.transaction(['images'], 'readwrite'); - const request = tx.objectStore('images').add({ - fileName, - data - }); - return new Promise((resolve, reject) => { - request.onerror = () => { - reject('data add fail'); - }; - request.onsuccess = ({ result }) => { - resolve(result); - }; - }); -}; - -// Get file data according to fileName -export const getImageFromCache = async fileName => { - const tx = dbInstance.transaction(['images']); - const request = tx.objectStore('images').index('fileName').get(fileName); - return new Promise((resolve, reject) => { - request.onerror = () => { - reject('data add fail'); - }; - request.onsuccess = ({ target }) => { - resolve(target.result); - }; - }); -}; -``` - -## save data - -When saving data, we can save the cache in the `transformData` of the method, because `transformData` will only be triggered when the network request responds, but will not be triggered when the cache is hit. In the sample code, convert the image blob instance to base64 data, cache and return this base64 data. - -```javascript-api.js -import { addImage2Cache } from './db'; - -export const image = fileName => - alovaInst.Get(`/image/${fileName}`, { - // highlight-start - async transformData(imgBlob) { - // Asynchronously convert the blob to base64 - const reader = new FileReader(); - reader.readAsDataURL(imgBlob); - const base64Img = await new Promise(resolve => { - reader.onload = ({ target }) => { - resolve(target.result); - }; - }); - - // Cache image data to IndexedDB - await addImage2Cache(fileName, base64Img); - return base64Img; - } - // highlight-end - }); -``` - -## retrieve data - -Specify `localCache` of this method instance as an asynchronous function to change the cache into a controlled state, match the cache in IndexedDB in this function, and return it if it matches, otherwise return `undefined` and continue to initiate a request to obtain data. - -```javascript title=api.js -import { getImageFromCache } from './db'; - -export const image = fileName => - alovaInst.Get(`/image/${fileName}`, { - async transformData(imgBlob) { - //... - }, - - // highlight-start - async localCache() { - // get cache - const cache = await getImageFromCache(fileName); - return cache && cache.data; - } - // highlight-end - }); -``` - -In this way, a basic custom cache management is basically completed. You can also save the expiration time of the cache, and judge whether it has expired when the cache is matched in `localCache`, so as to realize the cache expiration function. - -IndexedDB is just one example of managing caches asynchronously, you can also connect to your cache servers to manage them. +--- +title: Manage Cache with IndexedDB +--- + +If you are developing applications that require extensive use of local cache, such as graphics editing applications, file management applications, etc., the low-capacity localStorage can no longer meet the development needs. At this time, you can use IndexedDB and alova for large-capacity local cache management. + +This feature is mainly due to alova's [Controlled Cache](/next/tutorial/cache/controlled-cache) function, which can implement custom cache management. Let's take a look at the practical steps. + + + +Let's take custom management of large image data as an example. + +## Create IndexedDB instance + +First create an IndexedDB instance to operate the local cache, and export the cache operate function. + +```javascript title=db.js +const dbVersion = 1; +let dbInstance; +const request = window.indexedDB.open('MyTestDatabase', dbVersion); +request.onupgradeneeded = ({ target }) => { + dbInstance = target.result; + const imgStore = dbInstance.createObjectStore('images', { + autoIncrement: true + }); + imgStore.createIndex('fileName', 'fileName', { + unique: true + }); +}; +request.onerror = () => { + throw new Error('Database open fail'); +}; +request.onsuccess = ({ target }) => { + dbInstance = target.result; +}; + +// Add new data to IndexedDB +export const addImage2Cache = async (fileName, data) => { + const tx = dbInstance.transaction(['images'], 'readwrite'); + const request = tx.objectStore('images').add({ + fileName, + data + }); + return new Promise((resolve, reject) => { + request.onerror = () => { + reject('data add fail'); + }; + request.onsuccess = ({ result }) => { + resolve(result); + }; + }); +}; + +// Get file data according to fileName +export const getImageFromCache = async fileName => { + const tx = dbInstance.transaction(['images']); + const request = tx.objectStore('images').index('fileName').get(fileName); + return new Promise((resolve, reject) => { + request.onerror = () => { + reject('data add fail'); + }; + request.onsuccess = ({ target }) => { + resolve(target.result); + }; + }); +}; +``` + +## save data + +When saving data, we can save the cache in the `transformData` of the method, because `transformData` will only be triggered when the network request responds, but will not be triggered when the cache is hit. In the sample code, convert the image blob instance to base64 data, cache and return this base64 data. + +```javascript-api.js +import { addImage2Cache } from './db'; + +export const image = fileName => + alovaInst.Get(`/image/${fileName}`, { + // highlight-start + async transformData(imgBlob) { + // Asynchronously convert the blob to base64 + const reader = new FileReader(); + reader.readAsDataURL(imgBlob); + const base64Img = await new Promise(resolve => { + reader.onload = ({ target }) => { + resolve(target.result); + }; + }); + + // Cache image data to IndexedDB + await addImage2Cache(fileName, base64Img); + return base64Img; + } + // highlight-end + }); +``` + +## retrieve data + +Specify `localCache` of this method instance as an asynchronous function to change the cache into a controlled state, match the cache in IndexedDB in this function, and return it if it matches, otherwise return `undefined` and continue to initiate a request to obtain data. + +```javascript title=api.js +import { getImageFromCache } from './db'; + +export const image = fileName => + alovaInst.Get(`/image/${fileName}`, { + async transformData(imgBlob) { + //... + }, + + // highlight-start + async localCache() { + // get cache + const cache = await getImageFromCache(fileName); + return cache && cache.data; + } + // highlight-end + }); +``` + +In this way, a basic custom cache management is basically completed. You can also save the expiration time of the cache, and judge whether it has expired when the cache is matched in `localCache`, so as to realize the cache expiration function. + +IndexedDB is just one example of managing caches asynchronously, you can also connect to your cache servers to manage them. diff --git a/docs/tutorial/07-best-practice/04-multiple-servers.md b/docs/tutorial/07-project/01-best-practice/04-multiple-servers.md similarity index 93% rename from docs/tutorial/07-best-practice/04-multiple-servers.md rename to docs/tutorial/07-project/01-best-practice/04-multiple-servers.md index a08908d29..da125fc16 100644 --- a/docs/tutorial/07-best-practice/04-multiple-servers.md +++ b/docs/tutorial/07-project/01-best-practice/04-multiple-servers.md @@ -1,20 +1,19 @@ ---- -title: Multiple servers -sidebar_position: 40 ---- - -If your project needs to request multiple servers, you can create multiple alova instances to correspond to different servers. In order to easily distinguish between different environments, you can also use environment variables to manage the hosts of multiple servers. - -```ts -import { createAlova } from '@alova/core'; - -//Create user-related alova instance -const userAlova = createAlova({ - baseURL: VITE_API_USER -}); - -//Create order-related alova instances -const alova2 = createAlova({ - baseURL: VITE_API_ORDER -}); -``` +--- +title: Multiple servers +--- + +If your project needs to request multiple servers, you can create multiple alova instances to correspond to different servers. In order to easily distinguish between different environments, you can also use environment variables to manage the hosts of multiple servers. + +```ts +import { createAlova } from '@alova/core'; + +//Create user-related alova instance +const userAlova = createAlova({ + baseURL: VITE_API_USER +}); + +//Create order-related alova instances +const alova2 = createAlova({ + baseURL: VITE_API_ORDER +}); +``` diff --git a/docs/tutorial/07-best-practice/05-middleware.md b/docs/tutorial/07-project/01-best-practice/05-middleware.md similarity index 94% rename from docs/tutorial/07-best-practice/05-middleware.md rename to docs/tutorial/07-project/01-best-practice/05-middleware.md index 0341d9f0c..67afb2eb3 100644 --- a/docs/tutorial/07-best-practice/05-middleware.md +++ b/docs/tutorial/07-project/01-best-practice/05-middleware.md @@ -1,29 +1,28 @@ ---- -title: Common middleware practices -sidebar_position: 50 ---- - -## Delay update loading - -When the response is very fast, the loading status will flash once, which will bring a bad experience to the user. Delaying the loading update can make the loading status display after a period of time. If the response is completed within this period, it will not appear. Loading status. Let's implement a middleware with delayed update loading. - -```javascript -const delayLoadingMiddleware = - (delayTimer = 1000) => - async (ctx, next) => { - //Control loading by yourself - ctx.controlLoading(); - - //Delay updates for a specific time - const timer = setTimeout(() => { - ctx.update({ loading: true }); - }, delayTimer); - await next(); - ctx.update({ loading: false }); - clearTimeout(timer); - }; - -useRequest(methodInstance, { - middleware: delayLoadingMiddleware() -}); -``` +--- +title: Common middleware practices +--- + +## Delay update loading + +When the response is very fast, the loading status will flash once, which will bring a bad experience to the user. Delaying the loading update can make the loading status display after a period of time. If the response is completed within this period, it will not appear. Loading status. Let's implement a middleware with delayed update loading. + +```javascript +const delayLoadingMiddleware = + (delayTimer = 1000) => + async (ctx, next) => { + //Control loading by yourself + ctx.controlLoading(); + + //Delay updates for a specific time + const timer = setTimeout(() => { + ctx.update({ loading: true }); + }, delayTimer); + await next(); + ctx.update({ loading: false }); + clearTimeout(timer); + }; + +useRequest(methodInstance, { + middleware: delayLoadingMiddleware() +}); +``` diff --git a/docs/tutorial/07-best-practice/06-parallel-request.md b/docs/tutorial/07-project/01-best-practice/06-parallel-request.md similarity index 98% rename from docs/tutorial/07-best-practice/06-parallel-request.md rename to docs/tutorial/07-project/01-best-practice/06-parallel-request.md index 7aa5fe8d0..7d5e2dbef 100644 --- a/docs/tutorial/07-best-practice/06-parallel-request.md +++ b/docs/tutorial/07-project/01-best-practice/06-parallel-request.md @@ -1,6 +1,5 @@ --- title: Parallel Request -sidebar_position: 60 --- ## Use method diff --git a/docs/tutorial/07-best-practice/07-serial-request.md b/docs/tutorial/07-project/01-best-practice/07-serial-request.md similarity index 98% rename from docs/tutorial/07-best-practice/07-serial-request.md rename to docs/tutorial/07-project/01-best-practice/07-serial-request.md index f01667727..ac1d50511 100644 --- a/docs/tutorial/07-best-practice/07-serial-request.md +++ b/docs/tutorial/07-project/01-best-practice/07-serial-request.md @@ -1,6 +1,5 @@ --- title: Serial Request -sidebar_position: 70 --- ## Use method diff --git a/docs/tutorial/07-best-practice/08-l2-storage.md b/docs/tutorial/07-project/01-best-practice/08-l2-storage.md similarity index 100% rename from docs/tutorial/07-best-practice/08-l2-storage.md rename to docs/tutorial/07-project/01-best-practice/08-l2-storage.md diff --git a/docs/tutorial/07-project/01-best-practice/README.md b/docs/tutorial/07-project/01-best-practice/README.md new file mode 100644 index 000000000..016b3dbb4 --- /dev/null +++ b/docs/tutorial/07-project/01-best-practice/README.md @@ -0,0 +1,9 @@ +--- +title: Best Practices +--- + +import DocCardList from '@theme/DocCardList'; + +Best practices practiced and refined in real projects, I hope it will be helpful to you. + + diff --git a/docs/tutorial/07-project/01-best-practice/_category_.json b/docs/tutorial/07-project/01-best-practice/_category_.json new file mode 100644 index 000000000..ebe6f0133 --- /dev/null +++ b/docs/tutorial/07-project/01-best-practice/_category_.json @@ -0,0 +1,3 @@ +{ + "label": "Best practice" +} diff --git a/docs/tutorial/09-migration/01-v2-to-v3.md b/docs/tutorial/07-project/02-migration/01-v2-to-v3.md similarity index 84% rename from docs/tutorial/09-migration/01-v2-to-v3.md rename to docs/tutorial/07-project/02-migration/01-v2-to-v3.md index f3b1a92e0..0f32a7a4f 100644 --- a/docs/tutorial/09-migration/01-v2-to-v3.md +++ b/docs/tutorial/07-project/02-migration/01-v2-to-v3.md @@ -1,6 +1,5 @@ --- title: v3 upgrade guidelines -sidebar_position: 10 --- alova@3 is beta, so it is not recommended to upgrade in the actual project. diff --git a/docs/tutorial/09-migration/02-from-axios.md b/docs/tutorial/07-project/02-migration/02-from-axios.md similarity index 70% rename from docs/tutorial/09-migration/02-from-axios.md rename to docs/tutorial/07-project/02-migration/02-from-axios.md index 3661c9a20..3f6deaf3b 100644 --- a/docs/tutorial/09-migration/02-from-axios.md +++ b/docs/tutorial/07-project/02-migration/02-from-axios.md @@ -1,6 +1,5 @@ --- title: migrate from axios -sidebar_position: 20 --- Coming soon... diff --git a/docs/tutorial/09-migration/_category_.json b/docs/tutorial/07-project/02-migration/_category_.json similarity index 100% rename from docs/tutorial/09-migration/_category_.json rename to docs/tutorial/07-project/02-migration/_category_.json diff --git a/docs/tutorial/07-project/_category_.json b/docs/tutorial/07-project/_category_.json new file mode 100644 index 000000000..1530de8a2 --- /dev/null +++ b/docs/tutorial/07-project/_category_.json @@ -0,0 +1,5 @@ +{ + "label": "Project", + "collapsed": false, + "collapsible": false +} diff --git a/docusaurus.config.ts b/docusaurus.config.ts index a8f159077..6973b0fa3 100644 --- a/docusaurus.config.ts +++ b/docusaurus.config.ts @@ -52,7 +52,7 @@ const config: Config = { // disable blog blog: false, theme: { - customCss: require.resolve('./src/css/custom.css') + customCss: require.resolve('./src/css/custom.scss') } } ] @@ -116,15 +116,15 @@ const config: Config = { items: [ { label: 'Request Scene Model', - to: 'next/about/rsm' + to: 'about/RSM' }, { label: 'Comparison', - to: 'next/about/comparison' + to: 'about/comparison' }, { - label: 'Q&A', - to: 'next/about/q&a' + label: 'QA', + to: 'about/qa' } ] }, @@ -203,7 +203,6 @@ const config: Config = { }, footer: { style: 'light', - logo: { alt: 'Meta Open Source Logo', src: 'img/logo.svg', @@ -214,7 +213,7 @@ const config: Config = { links: [ { - title: 'Nav', + title: 'Document', items: [ { label: 'Docs', @@ -223,6 +222,31 @@ const config: Config = { { label: 'Example', to: 'tutorial/example/init-page' + }, + { + label: 'API', + to: 'api/alova' + }, + { + label: 'Contributing', + to: 'contributing/overview' + } + ] + }, + { + title: 'Resource', + items: [ + { + label: 'Request Adapter', + to: 'next/resource/request-adapter' + }, + { + label: 'Storage Adapter', + to: 'next/resource/storage-adapter' + }, + { + label: 'Framework Support', + to: 'next/category/framework' } ] }, @@ -301,37 +325,6 @@ const config: Config = { // Optional: path for search page that enabled by default (`false` to disable it) searchPagePath: 'search' } - - // algolia搜索 - // algolia: { - // // The application ID provided by Algolia - // appId: 'LGEFHNJ1SI', - - // // Public API key: it is safe to commit it - // apiKey: '4c4f6078174a5ae66234de817e75e0a8', - - // indexName: 'YOUR_INDEX_NAME', - - // // Optional: see doc section below - // contextualSearch: true, - - // // Optional: Specify domains where the navigation should occur through window.location instead on history.push. Useful when our Algolia config crawls multiple documentation sites and we want to navigate with window.location.href to them. - // externalUrlRegex: 'external\\.com|domain\\.com', - - // // Optional: Replace parts of the item URLs from Algolia. Useful when using the same search index for multiple deployments using a different baseUrl. You can use regexp or string in the `from` param. For example: localhost:3000 vs myCompany.com/docs - // replaceSearchResultPathname: { - // from: '/docs/', // or as RegExp: /\/docs\// - // to: '/' - // }, - - // // Optional: Algolia search parameters - // searchParameters: {}, - - // // Optional: path for search page that enabled by default (`false` to disable it) - // searchPagePath: 'search' - - // //... other Algolia params - // } }, markdown: { @@ -342,6 +335,7 @@ const config: Config = { // 插件 plugins: [ + 'docusaurus-plugin-sass', [ './plugin/baiduStatistics', { diff --git a/i18n/zh-CN/code.json b/i18n/zh-CN/code.json index e091fcf68..25e47b667 100644 --- a/i18n/zh-CN/code.json +++ b/i18n/zh-CN/code.json @@ -1,673 +1,673 @@ -{ - "theme.ErrorPageContent.title": { - "message": "页面已崩溃。", - "description": "The title of the fallback page when the page crashed" - }, - "theme.ErrorPageContent.tryAgain": { - "message": "重试", - "description": "The label of the button to try again when the page crashed" - }, - "theme.NotFound.title": { - "message": "找不到页面", - "description": "The title of the 404 page" - }, - "theme.NotFound.p1": { - "message": "我们找不到您要找的页面。", - "description": "The first paragraph of the 404 page" - }, - "theme.NotFound.p2": { - "message": "请联系原始链接来源网站的所有者,并告知他们链接已损坏。", - "description": "The 2nd paragraph of the 404 page" - }, - "theme.admonition.note": { - "message": "备注", - "description": "The default label used for the Note admonition (:::note)" - }, - "theme.admonition.tip": { - "message": "提示", - "description": "The default label used for the Tip admonition (:::tip)" - }, - "theme.admonition.danger": { - "message": "危险", - "description": "The default label used for the Danger admonition (:::danger)" - }, - "theme.admonition.info": { - "message": "信息", - "description": "The default label used for the Info admonition (:::info)" - }, - "theme.admonition.caution": { - "message": "警告", - "description": "The default label used for the Caution admonition (:::warning)" - }, - "theme.blog.archive.title": { - "message": "历史博文", - "description": "The page & hero title of the blog archive page" - }, - "theme.blog.archive.description": { - "message": "历史博文", - "description": "The page & hero description of the blog archive page" - }, - "theme.BackToTopButton.buttonAriaLabel": { - "message": "回到顶部", - "description": "The ARIA label for the back to top button" - }, - "theme.blog.paginator.navAriaLabel": { - "message": "博文列表分页导航", - "description": "The ARIA label for the blog pagination" - }, - "theme.blog.paginator.newerEntries": { - "message": "较新的博文", - "description": "The label used to navigate to the newer blog posts page (previous page)" - }, - "theme.blog.paginator.olderEntries": { - "message": "较旧的博文", - "description": "The label used to navigate to the older blog posts page (next page)" - }, - "theme.blog.post.paginator.navAriaLabel": { - "message": "博文分页导航", - "description": "The ARIA label for the blog posts pagination" - }, - "theme.blog.post.paginator.newerPost": { - "message": "较新一篇", - "description": "The blog post button label to navigate to the newer/previous post" - }, - "theme.blog.post.paginator.olderPost": { - "message": "较旧一篇", - "description": "The blog post button label to navigate to the older/next post" - }, - "theme.blog.post.plurals": { - "message": "{count} 篇博文", - "description": "Pluralized label for \"{count} posts\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)" - }, - "theme.blog.tagTitle": { - "message": "{nPosts} 含有标签「{tagName}」", - "description": "The title of the page for a blog tag" - }, - "theme.tags.tagsPageLink": { - "message": "查看所有标签", - "description": "The label of the link targeting the tag list page" - }, - "theme.colorToggle.ariaLabel": { - "message": "切换浅色/暗黑模式(当前为{mode})", - "description": "The ARIA label for the navbar color mode toggle" - }, - "theme.colorToggle.ariaLabel.mode.dark": { - "message": "暗黑模式", - "description": "The name for the dark color mode" - }, - "theme.colorToggle.ariaLabel.mode.light": { - "message": "浅色模式", - "description": "The name for the light color mode" - }, - "theme.docs.breadcrumbs.home": { - "message": "主页面", - "description": "The ARIA label for the home page in the breadcrumbs" - }, - "theme.docs.breadcrumbs.navAriaLabel": { - "message": "页面路径", - "description": "The ARIA label for the breadcrumbs" - }, - "theme.docs.DocCard.categoryDescription": { - "message": "{count} 个项目", - "description": "The default description for a category card in the generated index about how many items this category includes" - }, - "theme.docs.paginator.navAriaLabel": { - "message": "文档分页导航", - "description": "The ARIA label for the docs pagination" - }, - "theme.docs.paginator.previous": { - "message": "上一页", - "description": "The label used to navigate to the previous doc" - }, - "theme.docs.paginator.next": { - "message": "下一页", - "description": "The label used to navigate to the next doc" - }, - "theme.docs.tagDocListPageTitle.nDocsTagged": { - "message": "{count} 篇文档带有标签", - "description": "Pluralized label for \"{count} docs tagged\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)" - }, - "theme.docs.tagDocListPageTitle": { - "message": "{nDocsTagged}「{tagName}」", - "description": "The title of the page for a docs tag" - }, - "theme.docs.versions.unreleasedVersionLabel": { - "message": "此为 {siteTitle} {versionLabel} 版尚未发行的文档。", - "description": "The label used to tell the user that he's browsing an unreleased doc version" - }, - "theme.docs.versions.unmaintainedVersionLabel": { - "message": "此为 {siteTitle} {versionLabel} 版的文档,现已不再积极维护。", - "description": "The label used to tell the user that he's browsing an unmaintained doc version" - }, - "theme.docs.versions.latestVersionSuggestionLabel": { - "message": "最新的文档请参阅 {latestVersionLink} ({versionLabel})。", - "description": "The label used to tell the user to check the latest version" - }, - "theme.docs.versions.latestVersionLinkLabel": { - "message": "最新版本", - "description": "The label used for the latest version suggestion link label" - }, - "theme.docs.versionBadge.label": { - "message": "版本:{versionLabel}" - }, - "theme.common.editThisPage": { - "message": "编辑此页", - "description": "The link label to edit the current page" - }, - "theme.common.headingLinkTitle": { - "message": "标题的直接链接", - "description": "Title for link to heading" - }, - "theme.lastUpdated.atDate": { - "message": "于 {date} ", - "description": "The words used to describe on which date a page has been last updated" - }, - "theme.lastUpdated.byUser": { - "message": "由 {user} ", - "description": "The words used to describe by who the page has been last updated" - }, - "theme.lastUpdated.lastUpdatedAtBy": { - "message": "最后{byUser}{atDate}更新", - "description": "The sentence used to display when a page has been last updated, and by who" - }, - "theme.navbar.mobileVersionsDropdown.label": { - "message": "选择版本", - "description": "The label for the navbar versions dropdown on mobile view" - }, - "theme.tags.tagsListLabel": { - "message": "标签:", - "description": "The label alongside a tag list" - }, - "theme.AnnouncementBar.closeButtonAriaLabel": { - "message": "关闭", - "description": "The ARIA label for close button of announcement bar" - }, - "theme.blog.sidebar.navAriaLabel": { - "message": "最近博文导航", - "description": "The ARIA label for recent posts in the blog sidebar" - }, - "theme.CodeBlock.copied": { - "message": "复制成功", - "description": "The copied button label on code blocks" - }, - "theme.CodeBlock.copyButtonAriaLabel": { - "message": "复制代码到剪贴板", - "description": "The ARIA label for copy code blocks button" - }, - "theme.CodeBlock.copy": { - "message": "复制", - "description": "The copy button label on code blocks" - }, - "theme.CodeBlock.wordWrapToggle": { - "message": "切换自动换行", - "description": "The title attribute for toggle word wrapping button of code block lines" - }, - "theme.DocSidebarItem.toggleCollapsedCategoryAriaLabel": { - "message": "打开/收起侧边栏菜单「{label}」", - "description": "The ARIA label to toggle the collapsible sidebar category" - }, - "theme.navbar.mobileLanguageDropdown.label": { - "message": "选择语言", - "description": "The label for the mobile language switcher dropdown" - }, - "theme.TOCCollapsible.toggleButtonLabel": { - "message": "本页总览", - "description": "The label used by the button on the collapsible TOC component" - }, - "theme.blog.post.readMore": { - "message": "阅读更多", - "description": "The label used in blog post item excerpts to link to full blog posts" - }, - "theme.blog.post.readMoreLabel": { - "message": "阅读 {title} 的全文", - "description": "The ARIA label for the link to full blog posts from excerpts" - }, - "theme.blog.post.readingTime.plurals": { - "message": "阅读需 {readingTime} 分钟", - "description": "Pluralized label for \"{readingTime} min read\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)" - }, - "theme.docs.sidebar.collapseButtonTitle": { - "message": "收起侧边栏", - "description": "The title attribute for collapse button of doc sidebar" - }, - "theme.docs.sidebar.collapseButtonAriaLabel": { - "message": "收起侧边栏", - "description": "The title attribute for collapse button of doc sidebar" - }, - "theme.docs.sidebar.closeSidebarButtonAriaLabel": { - "message": "Close navigation bar", - "description": "The ARIA label for close button of mobile sidebar" - }, - "theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": { - "message": "← 回到主菜单", - "description": "The label of the back button to return to main menu, inside the mobile navbar sidebar secondary menu (notably used to display the docs sidebar)" - }, - "theme.docs.sidebar.toggleSidebarButtonAriaLabel": { - "message": "Toggle navigation bar", - "description": "The ARIA label for hamburger menu button of mobile navigation" - }, - "theme.docs.sidebar.expandButtonTitle": { - "message": "展开侧边栏", - "description": "The ARIA label and title attribute for expand button of doc sidebar" - }, - "theme.docs.sidebar.expandButtonAriaLabel": { - "message": "展开侧边栏", - "description": "The ARIA label and title attribute for expand button of doc sidebar" - }, - "theme.common.skipToMainContent": { - "message": "跳到主要内容", - "description": "The skip to content label used for accessibility, allowing to rapidly navigate to main content with keyboard tab/enter navigation" - }, - "theme.tags.tagsPageTitle": { - "message": "标签", - "description": "The title of the tag list page" - }, - "theme.tips.supportMe.prefix": { - "message": "如果你也喜欢alova,", - "description": "The tips of support me snippet1" - }, - "theme.tips.supportMe.linkText": { - "message": "请在Github为它点亮star!", - "description": "The tips of support me snippet2" - }, - - "homepage.title": { - "message": "轻量级请求策略库", - "description": "The title of web page" - }, - "homepage.tagline": { - "message": "一行代码完成各种复杂场景的网络请求,别再花时间在请求这件小事上了,交给我们", - "description": "网站tagline" - }, - "homepage.relationTitle": { - "message": "alova和请求库的关系", - "description": "关系标题" - }, - "homepage.relationDesc": { - "message": "传统promise式的请求库很好地解决了请求发送的问题,只是...它们只是单纯的请求发送工具", - "description": "关系描述" - }, - "homepage.relationAuxi": { - "message": "alova像它们的武器装备,通过alova可以获得更强大的能力,不管您喜欢使用axios、superagent,还是浏览器的fetch-api,alova都可以完美兼容", - "description": "关系附加信息" - }, - "homepage.Examples": { - "message": "示例", - "description": "示例按钮文字" - }, - "homepage.Get Started": { - "message": "快速开始", - "description": "开始按钮文字" - }, - - "homepage.features.Simple and familiar": { - "message": "简单熟悉", - "description": "简单熟悉" - }, - "homepage.features.Simple and familiar.desc": { - "message": "与axios相似的api设计,让您上手更简单熟悉", - "description": "description of feature" - }, - "homepage.features.High performance request strategy": { - "message": "高性能请求策略", - "description": "高性能请求策略" - }, - "homepage.features.High performance request strategy.desc": { - "message": "10+个可直接使用的请求策略模块,选择你想要的使用就好了,还能减小请求带来的性能问题", - "description": "description of feature" - }, - "homepage.features.Request-level cache": { - "message": "请求级缓存", - "description": "请求级缓存" - }, - "homepage.features.Request-level cache.desc": { - "message": "提供内存模式、持久化模式等多种服务端数据缓存模式,提升用户体验,同时降低服务端压力", - "description": "description of feature" - }, - "homepage.features.Lightweight": { - "message": "轻量级", - "description": "轻量级" - }, - "homepage.features.Lightweight.desc": { - "message": "4kb+,只有axios的30%", - "description": "description of feature" - }, - - "homepage.availableScope.title": { - "message": "🎉打破useHook使用边界", - "description": "description of available scope" - }, - "homepage.availableScope.subtitle": { - "message": "现在,alova已经完美兼容了vue options,尽情使用吧!", - "description": "description of available scope desc" - }, - - "homepage.strategy.title": { - "message": "以声明的方式完成你的请求", - "description": "description of request strategy" - }, - "homepage.strategy.subtitle": { - "message": "选择你想要使用的请求模块就好了,节省大量时间,还能让你的应用更加流畅", - "description": "description of request strategy desc" - }, - "homepage.Custom strategy": { - "message": "自定义你的请求策略", - "description": "description of custom strategy" - }, - "homepage.Custom strategy.desc": { - "message": "超强的扩展性可快速构建自定义请求策略", - "description": "description of custom strategy desc" - }, - "homepage.strategy.useRequest": { - "message": "基础请求", - "description": "description of Pagination request strategy" - }, - "homepage.strategy.useRequest.desc": { - "message": "使用useRequest请求数据,它将自动维护当前请求的相关状态", - "description": "description of Pagination request strategy desc" - }, - "homepage.strategy.useRequest.feature1": { - "message": "与axios相似的用法", - "description": "description of useRequest feature1" - }, - "homepage.strategy.useRequest.feature2": { - "message": "自动维护相关的状态", - "description": "description of useRequest feature1" - }, - "homepage.strategy.useRequest.feature3": { - "message": "响应数据缓存", - "description": "description of useRequest feature2" - }, - "homepage.strategy.useRequest.feature4": { - "message": "共享同时发起的相同请求", - "description": "description of useRequest feature2" - }, - "homepage.strategy.useWatcher": { - "message": "状态变化请求", - "description": "description of Pagination request strategy" - }, - "homepage.strategy.useWatcher.desc": { - "message": "在开发例如分页、数据过滤、模糊搜索等功能时,监听状态变化并立即发送请求", - "description": "description of useWatcher desc" - }, - "homepage.strategy.useWatcher.feature1": { - "message": "请求防抖", - "description": "description of useWatcher feature1" - }, - "homepage.strategy.useWatcher.feature2": { - "message": "保证请求时序", - "description": "description of useWatcher feature1" - }, - "homepage.strategy.useWatcher.feature3": { - "message": "过滤状态变化时是否发送请求", - "description": "description of useWatcher feature2" - }, - "homepage.strategy.useFetcher": { - "message": "预加载数据", - "description": "description of Pagination request strategy" - }, - "homepage.strategy.useFetcher.desc": { - "message": "提前预加载数据使界面更快呈现,或跨组件重新拉取数据", - "description": "description of useFetcher desc" - }, - "homepage.strategy.useFetcher.feature1": { - "message": "跨模块/组件更新视图", - "description": "description of useFetcher feature1" - }, - "homepage.strategy.useFetcher.feature2": { - "message": "预加载数据", - "description": "description of useFetcher feature1" - }, - "homepage.strategy.Pagination": { - "message": "分页请求策略", - "description": "description of Pagination request strategy" - }, - "homepage.strategy.Pagination.desc": { - "message": "自动管理分页数据,数据预加载,减少不必要的数据刷新,流畅性提高 300%,编码难度降低 50%", - "description": "description of Pagination request strategy desc" - }, - "homepage.strategy.Pagination.feature1": { - "message": "丰富全面的分页状态和事件", - "description": "description of Pagination feature1" - }, - "homepage.strategy.Pagination.feature2": { - "message": "监听状态改变自动获取指定分页数据", - "description": "description of Pagination feature2" - }, - "homepage.strategy.Pagination.feature3": { - "message": "预加载下一页数据", - "description": "description of Pagination feature3" - }, - "homepage.strategy.Pagination.feature4": { - "message": "高性能的列表操作函数", - "description": "description of Pagination feature4" - }, - "homepage.strategy.Sensorless interact strategy": { - "message": "无感数据交互策略", - "description": "description of Pagination request strategy" - }, - "homepage.strategy.Sensorless interact strategy.desc": { - "message": "全新的交互体验,提交即响应,大幅降低网络波动造成的影响,让你的应用在网络不稳定,甚至断网状态下依然可用", - "description": "description of Sensorless interact strategy desc" - }, - "homepage.strategy.Sensorless interact strategy.feature1": { - "message": "像操作本地数据一样,无需等待网络响应", - "description": "description of Sensorless interact strategy desc" - }, - "homepage.strategy.Sensorless interact strategy.feature2": { - "message": "弱网或断网状态依然可用", - "description": "description of Sensorless interact strategy desc" - }, - "homepage.strategy.Sensorless interact strategy.feature3": { - "message": "更稳定的数据同步,用户无感知的", - "description": "description of Sensorless interact strategy desc" - }, - "homepage.strategy.Sensorless interact strategy.feature4": { - "message": "自由切换无感请求和正常网络请求", - "description": "description of Sensorless interact strategy desc" - }, - "homepage.strategy.Token authentication": { - "message": "Token身份认证", - "description": "description of Token authentication request strategy" - }, - "homepage.strategy.Token authentication.desc": { - "message": "统一管理token认证代码,支持无感刷新 token。", - "description": "description of Token authentication desc" - }, - "homepage.strategy.Token authentication.feature1": { - "message": "支持在客户端和服务端无感刷新 token", - "description": "description of Token authentication desc" - }, - "homepage.strategy.Token authentication.feature2": { - "message": "自动等待 token 刷新完成", - "description": "description of Token authentication desc" - }, - "homepage.strategy.Token authentication.feature3": { - "message": "统一维护 Token 身份认证的所有代码", - "description": "description of Token authentication desc" - }, - "homepage.strategy.Token authentication.feature4": { - "message": "使用元数据设置请求身份", - "description": "description of Token authentication desc" - }, - "homepage.strategy.Token authentication.feature5": { - "message": "放行访客请求", - "description": "description of Token authentication desc" - }, - "homepage.strategy.Cross-component request": { - "message": "跨组件请求策略", - "description": "description of Pagination request strategy" - }, - "homepage.strategy.Cross-component request.desc": { - "message": "消除组件层级的限制,在任意组件中快速地触发任意请求的操作函数", - "description": "description of Cross-component request desc" - }, - "homepage.strategy.Cross-component request.feature1": { - "message": "跨越任意组件层级触发重新请求", - "description": "description of Cross-component request feature1" - }, - "homepage.strategy.Form submit": { - "message": "表单提交策略", - "description": "description of Pagination request strategy" - }, - "homepage.strategy.Form submit.desc": { - "message": "自动管理表单数据,可以快速实现各种表单", - "description": "description of Form submit desc" - }, - "homepage.strategy.Form submit.feature1": { - "message": "保存表单草稿", - "description": "description of Form submit feature1" - }, - "homepage.strategy.Form submit.feature2": { - "message": "多步骤表单", - "description": "description of Form submit feature2" - }, - "homepage.strategy.Form submit.feature3": { - "message": "表单提交自动重置数据", - "description": "description of Form submit feature3" - }, - "homepage.strategy.Upload": { - "message": "文件上传策略", - "description": "description of Upload strategy" - }, - "homepage.strategy.Upload.desc": { - "message": "更简单的文件上传策略,支持对base64、Blob、ArrayBuffer、Canvas数据的自动识别和转换", - "description": "description of Form submit desc" - }, - "homepage.strategy.Upload.feature1": { - "message": "自动识别和转换数据格式", - "description": "description of Form submit feature1" - }, - "homepage.strategy.Upload.feature2": { - "message": "多文件同时上传", - "description": "description of Form submit feature2" - }, - "homepage.strategy.Upload.feature3": { - "message": "图片预览图生成", - "description": "description of Form submit feature3" - }, - "homepage.strategy.Send captcha": { - "message": "发送验证码", - "description": "description of Send captcha strategy" - }, - "homepage.strategy.Send captcha.desc": { - "message": "减掉你在开发验证码发送功能时的繁琐", - "description": "description of Form submit desc" - }, - "homepage.strategy.Send captcha.feature1": { - "message": "自动倒计时", - "description": "description of Form submit feature1" - }, - "homepage.strategy.Auto refetch": { - "message": "自动拉取数据", - "description": "description of Auto refetch strategy" - }, - "homepage.strategy.Auto refetch.desc": { - "message": "通过浏览器事件或轮询自动拉取数据,保证始终展示最新数据", - "description": "description of Auto refetch desc" - }, - "homepage.strategy.Auto refetch.feature1": { - "message": "自由选择4个拉取条件", - "description": "description of Form submit feature1" - }, - "homepage.strategy.Auto refetch.feature2": { - "message": "自定义拉取触发规则", - "description": "description of Form submit feature2" - }, - "homepage.strategy.Auto refetch.feature3": { - "message": "支持请求节流", - "description": "description of Form submit feature2" - }, - "homepage.strategy.Request retry strategy": { - "message": "请求重试策略", - "description": "description of Pagination request strategy" - }, - "homepage.strategy.Request retry strategy.desc": { - "message": "在重要的请求上使用它,可以提高请求的稳定性", - "description": "description of Request retry strategy desc" - }, - "homepage.strategy.Request retry strategy.feature1": { - "message": "自定义是否重试,以及重试延迟", - "description": "description of Request retry strategy feature1" - }, - "homepage.strategy.Request retry strategy.feature2": { - "message": "手动停止重试", - "description": "description of Request retry strategy feature2" - }, - "homepage.strategy.SSE": { - "message": "SSE", - "description": "description of SSE strategy" - }, - "homepage.strategy.SSE.desc": { - "message": "通过Server-sent Events进行请求", - "description": "description of SSE desc" - }, - "homepage.strategy.SSE.feature1": { - "message": "通过全局响应和方法实例的函数transformData自动转换数据", - "description": "description of SSE feature1" - }, - "homepage.strategy.SSE.feature2": { - "message": "EventSource的全部控制", - "description": "description of SSE feature2" - }, - "homepage.strategy.More details": { - "message": "查看详情", - "description": "description of btnDetail of strategy" - }, - - "homepage.support.title": { - "message": "与任何请求工具在任何JS环境下运行", - "description": "description of support title" - }, - "homepage.support.subtitle": { - "message": "use hooks起源于functional component, 但alova创新性地将它兼容了options和class风格的UI框架,这意味着alova的use hooks几乎不受JS环境和UI框架限制,并且可以与你熟悉的请求工具一起使用", - "description": "description of support subtitle" - }, - - "homepage.contributors.title": { - "message": "感谢有你们", - "description": "description of contributors title" - }, - "homepage.contributors.subtitle": { - "message": "alova贡献者们的智慧和力量,正在让alova变得越来越完美", - "description": "description of contributors subtitle" - }, - - "homepage.like.title": { - "message": "如果你也喜欢alova", - "description": "description of support title" - }, - "homepage.like.btnGithub": { - "message": "在Github点星", - "description": "description of like btnGithub" - }, - "homepage.like.btnX": { - "message": "在X关注我们", - "description": "description of like btnGithub" - }, - "homepage.like.btnDiscord": { - "message": "加入Discord", - "description": "description of like btnDiscord" - }, - "homepage.like.btnWechat": { - "message": "加入微信群", - "description": "description of like btnWechat" - }, - "homepage.release.announce": { - "message": "alvoa3.0正在密锣紧鼓中,查看详情", - "description": "description of release announce" - }, - - "ad.project collection": { - "message": "alova用在项目里了吗?快来告诉我", - "description": "description of ad.project collection" - }, - "announcement.content": { - "message": "如果你也喜欢alova,请为它点个star吧!", - "description": "description of announcement title" - }, - "example.open in new tab": { - "message": "遇到问题?点此在新页面中打开示例", - "description": "description of example open in new tab" - } -} +{ + "theme.ErrorPageContent.title": { + "message": "页面已崩溃。", + "description": "The title of the fallback page when the page crashed" + }, + "theme.ErrorPageContent.tryAgain": { + "message": "重试", + "description": "The label of the button to try again when the page crashed" + }, + "theme.NotFound.title": { + "message": "找不到页面", + "description": "The title of the 404 page" + }, + "theme.NotFound.p1": { + "message": "我们找不到您要找的页面。", + "description": "The first paragraph of the 404 page" + }, + "theme.NotFound.p2": { + "message": "请联系原始链接来源网站的所有者,并告知他们链接已损坏。", + "description": "The 2nd paragraph of the 404 page" + }, + "theme.admonition.note": { + "message": "备注", + "description": "The default label used for the Note admonition (:::note)" + }, + "theme.admonition.tip": { + "message": "提示", + "description": "The default label used for the Tip admonition (:::tip)" + }, + "theme.admonition.danger": { + "message": "危险", + "description": "The default label used for the Danger admonition (:::danger)" + }, + "theme.admonition.info": { + "message": "信息", + "description": "The default label used for the Info admonition (:::info)" + }, + "theme.admonition.caution": { + "message": "警告", + "description": "The default label used for the Caution admonition (:::warning)" + }, + "theme.blog.archive.title": { + "message": "历史博文", + "description": "The page & hero title of the blog archive page" + }, + "theme.blog.archive.description": { + "message": "历史博文", + "description": "The page & hero description of the blog archive page" + }, + "theme.BackToTopButton.buttonAriaLabel": { + "message": "回到顶部", + "description": "The ARIA label for the back to top button" + }, + "theme.blog.paginator.navAriaLabel": { + "message": "博文列表分页导航", + "description": "The ARIA label for the blog pagination" + }, + "theme.blog.paginator.newerEntries": { + "message": "较新的博文", + "description": "The label used to navigate to the newer blog posts page (previous page)" + }, + "theme.blog.paginator.olderEntries": { + "message": "较旧的博文", + "description": "The label used to navigate to the older blog posts page (next page)" + }, + "theme.blog.post.paginator.navAriaLabel": { + "message": "博文分页导航", + "description": "The ARIA label for the blog posts pagination" + }, + "theme.blog.post.paginator.newerPost": { + "message": "较新一篇", + "description": "The blog post button label to navigate to the newer/previous post" + }, + "theme.blog.post.paginator.olderPost": { + "message": "较旧一篇", + "description": "The blog post button label to navigate to the older/next post" + }, + "theme.blog.post.plurals": { + "message": "{count} 篇博文", + "description": "Pluralized label for \"{count} posts\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)" + }, + "theme.blog.tagTitle": { + "message": "{nPosts} 含有标签「{tagName}」", + "description": "The title of the page for a blog tag" + }, + "theme.tags.tagsPageLink": { + "message": "查看所有标签", + "description": "The label of the link targeting the tag list page" + }, + "theme.colorToggle.ariaLabel": { + "message": "切换浅色/暗黑模式(当前为{mode})", + "description": "The ARIA label for the navbar color mode toggle" + }, + "theme.colorToggle.ariaLabel.mode.dark": { + "message": "暗黑模式", + "description": "The name for the dark color mode" + }, + "theme.colorToggle.ariaLabel.mode.light": { + "message": "浅色模式", + "description": "The name for the light color mode" + }, + "theme.docs.breadcrumbs.home": { + "message": "主页面", + "description": "The ARIA label for the home page in the breadcrumbs" + }, + "theme.docs.breadcrumbs.navAriaLabel": { + "message": "页面路径", + "description": "The ARIA label for the breadcrumbs" + }, + "theme.docs.DocCard.categoryDescription": { + "message": "{count} 个项目", + "description": "The default description for a category card in the generated index about how many items this category includes" + }, + "theme.docs.paginator.navAriaLabel": { + "message": "文档分页导航", + "description": "The ARIA label for the docs pagination" + }, + "theme.docs.paginator.previous": { + "message": "上一页", + "description": "The label used to navigate to the previous doc" + }, + "theme.docs.paginator.next": { + "message": "下一页", + "description": "The label used to navigate to the next doc" + }, + "theme.docs.tagDocListPageTitle.nDocsTagged": { + "message": "{count} 篇文档带有标签", + "description": "Pluralized label for \"{count} docs tagged\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)" + }, + "theme.docs.tagDocListPageTitle": { + "message": "{nDocsTagged}「{tagName}」", + "description": "The title of the page for a docs tag" + }, + "theme.docs.versions.unreleasedVersionLabel": { + "message": "此为 {siteTitle} {versionLabel} 版尚未发行的文档。", + "description": "The label used to tell the user that he's browsing an unreleased doc version" + }, + "theme.docs.versions.unmaintainedVersionLabel": { + "message": "此为 {siteTitle} {versionLabel} 版的文档,现已不再积极维护。", + "description": "The label used to tell the user that he's browsing an unmaintained doc version" + }, + "theme.docs.versions.latestVersionSuggestionLabel": { + "message": "最新的文档请参阅 {latestVersionLink} ({versionLabel})。", + "description": "The label used to tell the user to check the latest version" + }, + "theme.docs.versions.latestVersionLinkLabel": { + "message": "最新版本", + "description": "The label used for the latest version suggestion link label" + }, + "theme.docs.versionBadge.label": { + "message": "版本:{versionLabel}" + }, + "theme.common.editThisPage": { + "message": "编辑此页", + "description": "The link label to edit the current page" + }, + "theme.common.headingLinkTitle": { + "message": "标题的直接链接", + "description": "Title for link to heading" + }, + "theme.lastUpdated.atDate": { + "message": "于 {date} ", + "description": "The words used to describe on which date a page has been last updated" + }, + "theme.lastUpdated.byUser": { + "message": "由 {user} ", + "description": "The words used to describe by who the page has been last updated" + }, + "theme.lastUpdated.lastUpdatedAtBy": { + "message": "最后{byUser}{atDate}更新", + "description": "The sentence used to display when a page has been last updated, and by who" + }, + "theme.navbar.mobileVersionsDropdown.label": { + "message": "选择版本", + "description": "The label for the navbar versions dropdown on mobile view" + }, + "theme.tags.tagsListLabel": { + "message": "标签:", + "description": "The label alongside a tag list" + }, + "theme.AnnouncementBar.closeButtonAriaLabel": { + "message": "关闭", + "description": "The ARIA label for close button of announcement bar" + }, + "theme.blog.sidebar.navAriaLabel": { + "message": "最近博文导航", + "description": "The ARIA label for recent posts in the blog sidebar" + }, + "theme.CodeBlock.copied": { + "message": "复制成功", + "description": "The copied button label on code blocks" + }, + "theme.CodeBlock.copyButtonAriaLabel": { + "message": "复制代码到剪贴板", + "description": "The ARIA label for copy code blocks button" + }, + "theme.CodeBlock.copy": { + "message": "复制", + "description": "The copy button label on code blocks" + }, + "theme.CodeBlock.wordWrapToggle": { + "message": "切换自动换行", + "description": "The title attribute for toggle word wrapping button of code block lines" + }, + "theme.DocSidebarItem.toggleCollapsedCategoryAriaLabel": { + "message": "打开/收起侧边栏菜单「{label}」", + "description": "The ARIA label to toggle the collapsible sidebar category" + }, + "theme.navbar.mobileLanguageDropdown.label": { + "message": "选择语言", + "description": "The label for the mobile language switcher dropdown" + }, + "theme.TOCCollapsible.toggleButtonLabel": { + "message": "本页总览", + "description": "The label used by the button on the collapsible TOC component" + }, + "theme.blog.post.readMore": { + "message": "阅读更多", + "description": "The label used in blog post item excerpts to link to full blog posts" + }, + "theme.blog.post.readMoreLabel": { + "message": "阅读 {title} 的全文", + "description": "The ARIA label for the link to full blog posts from excerpts" + }, + "theme.blog.post.readingTime.plurals": { + "message": "阅读需 {readingTime} 分钟", + "description": "Pluralized label for \"{readingTime} min read\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)" + }, + "theme.docs.sidebar.collapseButtonTitle": { + "message": "收起侧边栏", + "description": "The title attribute for collapse button of doc sidebar" + }, + "theme.docs.sidebar.collapseButtonAriaLabel": { + "message": "收起侧边栏", + "description": "The title attribute for collapse button of doc sidebar" + }, + "theme.docs.sidebar.closeSidebarButtonAriaLabel": { + "message": "Close navigation bar", + "description": "The ARIA label for close button of mobile sidebar" + }, + "theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": { + "message": "← 回到主菜单", + "description": "The label of the back button to return to main menu, inside the mobile navbar sidebar secondary menu (notably used to display the docs sidebar)" + }, + "theme.docs.sidebar.toggleSidebarButtonAriaLabel": { + "message": "Toggle navigation bar", + "description": "The ARIA label for hamburger menu button of mobile navigation" + }, + "theme.docs.sidebar.expandButtonTitle": { + "message": "展开侧边栏", + "description": "The ARIA label and title attribute for expand button of doc sidebar" + }, + "theme.docs.sidebar.expandButtonAriaLabel": { + "message": "展开侧边栏", + "description": "The ARIA label and title attribute for expand button of doc sidebar" + }, + "theme.common.skipToMainContent": { + "message": "跳到主要内容", + "description": "The skip to content label used for accessibility, allowing to rapidly navigate to main content with keyboard tab/enter navigation" + }, + "theme.tags.tagsPageTitle": { + "message": "标签", + "description": "The title of the tag list page" + }, + "theme.tips.supportMe.prefix": { + "message": "如果你也喜欢alova,", + "description": "The tips of support me snippet1" + }, + "theme.tips.supportMe.linkText": { + "message": "请在Github为它点亮star!", + "description": "The tips of support me snippet2" + }, + + "homepage.title": { + "message": "轻量级请求策略库", + "description": "The title of web page" + }, + "homepage.tagline": { + "message": "一行代码完成各种复杂场景的网络请求,别再花时间在请求这件小事上了,交给我们", + "description": "网站tagline" + }, + "homepage.relationTitle": { + "message": "alova和请求库的关系", + "description": "关系标题" + }, + "homepage.relationDesc": { + "message": "传统promise式的请求库很好地解决了请求发送的问题,只是...它们只是单纯的请求发送工具", + "description": "关系描述" + }, + "homepage.relationAuxi": { + "message": "alova像它们的武器装备,通过alova可以获得更强大的能力,不管您喜欢使用axios、superagent,还是浏览器的fetch-api,alova都可以完美兼容", + "description": "关系附加信息" + }, + "homepage.Examples": { + "message": "示例", + "description": "示例按钮文字" + }, + "homepage.Get Started": { + "message": "快速开始", + "description": "开始按钮文字" + }, + + "homepage.features.Simple and familiar": { + "message": "简单熟悉", + "description": "简单熟悉" + }, + "homepage.features.Simple and familiar.desc": { + "message": "与axios相似的api设计,让您上手更简单熟悉", + "description": "description of feature" + }, + "homepage.features.High performance request strategy": { + "message": "高性能请求策略", + "description": "高性能请求策略" + }, + "homepage.features.High performance request strategy.desc": { + "message": "10+个可直接使用的请求策略模块,选择你想要的使用就好了,还能减小请求带来的性能问题", + "description": "description of feature" + }, + "homepage.features.Request-level cache": { + "message": "请求级缓存", + "description": "请求级缓存" + }, + "homepage.features.Request-level cache.desc": { + "message": "提供内存模式、持久化模式等多种服务端数据缓存模式,提升用户体验,同时降低服务端压力", + "description": "description of feature" + }, + "homepage.features.Lightweight": { + "message": "轻量级", + "description": "轻量级" + }, + "homepage.features.Lightweight.desc": { + "message": "4kb+,只有axios的30%", + "description": "description of feature" + }, + + "homepage.availableScope.title": { + "message": "🎉打破useHook使用边界", + "description": "description of available scope" + }, + "homepage.availableScope.subtitle": { + "message": "现在,alova已经完美兼容了vue options,尽情使用吧!", + "description": "description of available scope desc" + }, + + "homepage.strategy.title": { + "message": "以声明的方式完成你的请求", + "description": "description of request strategy" + }, + "homepage.strategy.subtitle": { + "message": "选择你想要使用的请求模块就好了,节省大量时间,还能让你的应用更加流畅", + "description": "description of request strategy desc" + }, + "homepage.Custom strategy": { + "message": "自定义你的请求策略", + "description": "description of custom strategy" + }, + "homepage.Custom strategy.desc": { + "message": "超强的扩展性可快速构建自定义请求策略", + "description": "description of custom strategy desc" + }, + "homepage.strategy.useRequest": { + "message": "基础请求", + "description": "description of Pagination request strategy" + }, + "homepage.strategy.useRequest.desc": { + "message": "使用useRequest请求数据,它将自动维护当前请求的相关状态", + "description": "description of Pagination request strategy desc" + }, + "homepage.strategy.useRequest.feature1": { + "message": "与axios相似的用法", + "description": "description of useRequest feature1" + }, + "homepage.strategy.useRequest.feature2": { + "message": "自动维护相关的状态", + "description": "description of useRequest feature1" + }, + "homepage.strategy.useRequest.feature3": { + "message": "响应数据缓存", + "description": "description of useRequest feature2" + }, + "homepage.strategy.useRequest.feature4": { + "message": "共享同时发起的相同请求", + "description": "description of useRequest feature2" + }, + "homepage.strategy.useWatcher": { + "message": "状态变化请求", + "description": "description of Pagination request strategy" + }, + "homepage.strategy.useWatcher.desc": { + "message": "在开发例如分页、数据过滤、模糊搜索等功能时,监听状态变化并立即发送请求", + "description": "description of useWatcher desc" + }, + "homepage.strategy.useWatcher.feature1": { + "message": "请求防抖", + "description": "description of useWatcher feature1" + }, + "homepage.strategy.useWatcher.feature2": { + "message": "保证请求时序", + "description": "description of useWatcher feature1" + }, + "homepage.strategy.useWatcher.feature3": { + "message": "过滤状态变化时是否发送请求", + "description": "description of useWatcher feature2" + }, + "homepage.strategy.useFetcher": { + "message": "预加载数据", + "description": "description of Pagination request strategy" + }, + "homepage.strategy.useFetcher.desc": { + "message": "提前预加载数据使界面更快呈现,或跨组件重新拉取数据", + "description": "description of useFetcher desc" + }, + "homepage.strategy.useFetcher.feature1": { + "message": "跨模块/组件更新视图", + "description": "description of useFetcher feature1" + }, + "homepage.strategy.useFetcher.feature2": { + "message": "预加载数据", + "description": "description of useFetcher feature1" + }, + "homepage.strategy.Pagination": { + "message": "分页请求策略", + "description": "description of Pagination request strategy" + }, + "homepage.strategy.Pagination.desc": { + "message": "自动管理分页数据,数据预加载,减少不必要的数据刷新,流畅性提高 300%,编码难度降低 50%", + "description": "description of Pagination request strategy desc" + }, + "homepage.strategy.Pagination.feature1": { + "message": "丰富全面的分页状态和事件", + "description": "description of Pagination feature1" + }, + "homepage.strategy.Pagination.feature2": { + "message": "监听状态改变自动获取指定分页数据", + "description": "description of Pagination feature2" + }, + "homepage.strategy.Pagination.feature3": { + "message": "预加载下一页数据", + "description": "description of Pagination feature3" + }, + "homepage.strategy.Pagination.feature4": { + "message": "高性能的列表操作函数", + "description": "description of Pagination feature4" + }, + "homepage.strategy.Sensorless interact strategy": { + "message": "无感数据交互策略", + "description": "description of Pagination request strategy" + }, + "homepage.strategy.Sensorless interact strategy.desc": { + "message": "全新的交互体验,提交即响应,大幅降低网络波动造成的影响,让你的应用在网络不稳定,甚至断网状态下依然可用", + "description": "description of Sensorless interact strategy desc" + }, + "homepage.strategy.Sensorless interact strategy.feature1": { + "message": "像操作本地数据一样,无需等待网络响应", + "description": "description of Sensorless interact strategy desc" + }, + "homepage.strategy.Sensorless interact strategy.feature2": { + "message": "弱网或断网状态依然可用", + "description": "description of Sensorless interact strategy desc" + }, + "homepage.strategy.Sensorless interact strategy.feature3": { + "message": "更稳定的数据同步,用户无感知的", + "description": "description of Sensorless interact strategy desc" + }, + "homepage.strategy.Sensorless interact strategy.feature4": { + "message": "自由切换无感请求和正常网络请求", + "description": "description of Sensorless interact strategy desc" + }, + "homepage.strategy.Token authentication": { + "message": "Token身份认证", + "description": "description of Token authentication request strategy" + }, + "homepage.strategy.Token authentication.desc": { + "message": "统一管理token认证代码,支持无感刷新 token。", + "description": "description of Token authentication desc" + }, + "homepage.strategy.Token authentication.feature1": { + "message": "支持在客户端和服务端无感刷新 token", + "description": "description of Token authentication desc" + }, + "homepage.strategy.Token authentication.feature2": { + "message": "自动等待 token 刷新完成", + "description": "description of Token authentication desc" + }, + "homepage.strategy.Token authentication.feature3": { + "message": "统一维护 Token 身份认证的所有代码", + "description": "description of Token authentication desc" + }, + "homepage.strategy.Token authentication.feature4": { + "message": "使用元数据设置请求身份", + "description": "description of Token authentication desc" + }, + "homepage.strategy.Token authentication.feature5": { + "message": "放行访客请求", + "description": "description of Token authentication desc" + }, + "homepage.strategy.Cross-component request": { + "message": "跨组件请求策略", + "description": "description of Pagination request strategy" + }, + "homepage.strategy.Cross-component request.desc": { + "message": "消除组件层级的限制,在任意组件中快速地触发任意请求的操作函数", + "description": "description of Cross-component request desc" + }, + "homepage.strategy.Cross-component request.feature1": { + "message": "跨越任意组件层级触发重新请求", + "description": "description of Cross-component request feature1" + }, + "homepage.strategy.Form submit": { + "message": "表单提交策略", + "description": "description of Pagination request strategy" + }, + "homepage.strategy.Form submit.desc": { + "message": "自动管理表单数据,可以快速实现各种表单", + "description": "description of Form submit desc" + }, + "homepage.strategy.Form submit.feature1": { + "message": "保存表单草稿", + "description": "description of Form submit feature1" + }, + "homepage.strategy.Form submit.feature2": { + "message": "多步骤表单", + "description": "description of Form submit feature2" + }, + "homepage.strategy.Form submit.feature3": { + "message": "表单提交自动重置数据", + "description": "description of Form submit feature3" + }, + "homepage.strategy.Upload": { + "message": "文件上传策略", + "description": "description of Upload strategy" + }, + "homepage.strategy.Upload.desc": { + "message": "更简单的文件上传策略,支持对base64、Blob、ArrayBuffer、Canvas数据的自动识别和转换", + "description": "description of Form submit desc" + }, + "homepage.strategy.Upload.feature1": { + "message": "自动识别和转换数据格式", + "description": "description of Form submit feature1" + }, + "homepage.strategy.Upload.feature2": { + "message": "多文件同时上传", + "description": "description of Form submit feature2" + }, + "homepage.strategy.Upload.feature3": { + "message": "图片预览图生成", + "description": "description of Form submit feature3" + }, + "homepage.strategy.Send captcha": { + "message": "发送验证码", + "description": "description of Send captcha strategy" + }, + "homepage.strategy.Send captcha.desc": { + "message": "减掉你在开发验证码发送功能时的繁琐", + "description": "description of Form submit desc" + }, + "homepage.strategy.Send captcha.feature1": { + "message": "自动倒计时", + "description": "description of Form submit feature1" + }, + "homepage.strategy.Auto refetch": { + "message": "自动拉取数据", + "description": "description of Auto refetch strategy" + }, + "homepage.strategy.Auto refetch.desc": { + "message": "通过浏览器事件或轮询自动拉取数据,保证始终展示最新数据", + "description": "description of Auto refetch desc" + }, + "homepage.strategy.Auto refetch.feature1": { + "message": "自由选择4个拉取条件", + "description": "description of Form submit feature1" + }, + "homepage.strategy.Auto refetch.feature2": { + "message": "自定义拉取触发规则", + "description": "description of Form submit feature2" + }, + "homepage.strategy.Auto refetch.feature3": { + "message": "支持请求节流", + "description": "description of Form submit feature2" + }, + "homepage.strategy.Request retry strategy": { + "message": "请求重试策略", + "description": "description of Pagination request strategy" + }, + "homepage.strategy.Request retry strategy.desc": { + "message": "在重要的请求上使用它,可以提高请求的稳定性", + "description": "description of Request retry strategy desc" + }, + "homepage.strategy.Request retry strategy.feature1": { + "message": "自定义是否重试,以及重试延迟", + "description": "description of Request retry strategy feature1" + }, + "homepage.strategy.Request retry strategy.feature2": { + "message": "手动停止重试", + "description": "description of Request retry strategy feature2" + }, + "homepage.strategy.SSE": { + "message": "SSE", + "description": "description of SSE strategy" + }, + "homepage.strategy.SSE.desc": { + "message": "通过Server-sent Events进行请求", + "description": "description of SSE desc" + }, + "homepage.strategy.SSE.feature1": { + "message": "通过全局响应和方法实例的函数transformData自动转换数据", + "description": "description of SSE feature1" + }, + "homepage.strategy.SSE.feature2": { + "message": "EventSource的全部控制", + "description": "description of SSE feature2" + }, + "homepage.strategy.More details": { + "message": "查看详情", + "description": "description of btnDetail of strategy" + }, + + "homepage.support.title": { + "message": "与任何请求工具在任何JS环境下运行", + "description": "description of support title" + }, + "homepage.support.subtitle": { + "message": "use hooks起源于functional component, 但alova创新性地将它兼容了options和class风格的UI框架,这意味着alova的use hooks几乎不受JS环境和UI框架限制,并且可以与你熟悉的请求工具一起使用", + "description": "description of support subtitle" + }, + + "homepage.contributors.title": { + "message": "感谢有你们", + "description": "description of contributors title" + }, + "homepage.contributors.subtitle": { + "message": "alova贡献者们的智慧和力量,正在让alova变得越来越完美", + "description": "description of contributors subtitle" + }, + + "homepage.like.title": { + "message": "如果你也喜欢alova", + "description": "description of support title" + }, + "homepage.like.btnGithub": { + "message": "在Github点星", + "description": "description of like btnGithub" + }, + "homepage.like.btnX": { + "message": "在X关注我们", + "description": "description of like btnGithub" + }, + "homepage.like.btnDiscord": { + "message": "加入Discord", + "description": "description of like btnDiscord" + }, + "homepage.like.btnWechat": { + "message": "加入微信群", + "description": "description of like btnWechat" + }, + "homepage.release.announce": { + "message": "alova v3.0.0-beta已发布", + "description": "description of release announce" + }, + + "ad.project collection": { + "message": "alova用在项目里了吗?快来告诉我", + "description": "description of ad.project collection" + }, + "announcement.content": { + "message": "如果你也喜欢alova,请为它点个star吧!", + "description": "description of announcement title" + }, + "example.open in new tab": { + "message": "遇到问题?点此在新页面中打开示例", + "description": "description of example open in new tab" + } +} diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current.json b/i18n/zh-CN/docusaurus-plugin-content-docs/current.json index 9efa96a05..a1eea20e0 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current.json +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current.json @@ -19,6 +19,10 @@ "message": "开始", "description": "The label for category Get Started in sidebar tutorial" }, + "sidebar.tutorial.category.Basic": { + "message": "基础", + "description": "The label for category Get Started in sidebar tutorial" + }, "sidebar.tutorial.category.Cache Details": { "message": "缓存详解", "description": "The label for category Cache Details in sidebar tutorial" @@ -27,17 +31,29 @@ "message": "结合框架", "description": "The label for category Combine Framework in sidebar tutorial" }, - "sidebar.tutorial.category.Client Strategy": { - "message": "客户端策略", - "description": "The label for category Client Strategy in sidebar tutorial" + "sidebar.tutorial.category.Client": { + "message": "客户端", + "description": "The label for category Client in sidebar tutorial" }, - "sidebar.tutorial.category.Server Strategy": { - "message": "服务端策略", - "description": "The label for category Server Strategy in sidebar tutorial" - }, - "sidebar.tutorial.category.Strategy": { + "sidebar.tutorial.category.Request Strategy": { "message": "请求策略", - "description": "The label for category Strategy in sidebar tutorial" + "description": "The label for category Request Strategy in sidebar tutorial" + }, + "sidebar.tutorial.category.Server": { + "message": "服务端", + "description": "The label for category Server in sidebar tutorial" + }, + "sidebar.tutorial.category.In-Depth": { + "message": "深入指南", + "description": "The label for category In-Depth in sidebar tutorial" + }, + "sidebar.tutorial.category.alova In-Depth": { + "message": "深入alova", + "description": "The label for category alova In-Depth in sidebar tutorial" + }, + "sidebar.tutorial.category.Project": { + "message": "项目", + "description": "The label for category Project in sidebar tutorial" }, "sidebar.tutorial.category.Sensorless data interaction": { "message": "无感数据交互", diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/about/01-RSM.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/about/01-RSM.md index d2db81034..ea86a506c 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/about/01-RSM.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/about/01-RSM.md @@ -1,61 +1,60 @@ ---- -title: 请求场景模型(RSM) -sidebar_position: 10 ---- - -## 什么是请求场景模型 - -请求场景模型是以客户端视角的,描述客户端从触发请求意图到接收请求结果的抽象模型,分别由请求时机、请求行为、请求事件以及响应管理四个阶段组成。例如在进行一次请求时经常需要思考以下问题, - -1. 什么时候发出请求; -2. 是否要展示请求状态; -3. 是否需要对请求进行失败重试; -4. 要如何加工响应数据; -5. 是否需要对请求参数加密; -6. 是否要对高频使用的响应数据做缓存; -7. 如何进行跨页面操作数据; -8. 弱网或断网环境下需要如何处理请求; -9. ... - -`fetch`或`axios`往往更专注于如何与服务端交互,但对于上面的问题我们总是需要自己处理,这些有利于应用性能和稳定性的功能,总会让程序员们编写出低维护性的代码。请求场景模型就是从准备请求到响应数据加工完毕的所有环节进行抽象,从而覆盖以前端为视角的,整个 CS 交互生命周期的模型。`alova`就是一个以请求场景模型的库,它是对`axios`等请求库的一种补充,而非替代品。 - -> CS 交互:泛指所有客户端类型和服务端的数据交互 - -## 请求场景模型 - -![RSM](/img/rsm-cn.png) - -## 请求时机 - -描述在什么时候需要发出请求,在`alova`中以`useHook`实现。 - -- 初始化展示数据,如刚进入某个界面或子界面; -- 人机交互触发 CS 交互,需要变更数据重新发出请求,如翻页、筛选、排序、模糊搜索等; -- 以防抖方式发送请求,避免视图数据闪动,以及降低服务端压力 -- 预加载数据,如分页内预先加载下一页内容、预测用户点击某个按钮后预先拉取数据; -- 操作服务端数据,需发出增删改查请求,如提交数据、删除数据等; -- 同步服务端状态,如数据变化较快的场景下轮询请求、操作了某个数据后重新拉取数据; - -## 请求行为 - -描述以怎样的方式处理请求,在`alova`中以 Method 抽象实现。 - -- 占位请求,请求时展示 loading、骨架图、或者是上次使用的真实数据; -- 缓存高频响应,多次执行请求会使用保鲜数据; -- 多请求串行与并行; -- 重要接口的重试机制,降低网络不稳定造成的请求失败概率; -- 静默提交,当只关心提交数据时,提交请求后直接响应成功事件,后台保证请求成功; -- 离线提交,离线时将提交数据暂存到本地,网络连接后再提交; - -## 请求事件 - -表示携带请求参数发送请求,获得响应,`alova`可以与`axios`、`fetch`、`XMLHttpRequest`等任意请求库或原生方案共同协作。 - -## 响应管理 - -`alova`将响应数据状态化并统一管理,以请求层面的方式刷新视图数据、操作缓存,避免了在组件层面的操作,更加优雅和统一。 - -- 移除缓存响应数据,再次发起请求时将从服务端拉取; -- 更新缓存响应数据,可更新任意位置响应数据,非常有利于跨页面更新数据; -- 刷新响应数据,可重新刷新任意位置的响应数据,也非常有利于跨页面更新数据; -- 自定义设置缓存,在请求批量数据时,可手动对批量数据一一设置缓存,从而满足后续单条数据的缓存命中; +--- +title: 请求场景模型(RSM) +--- + +## 什么是请求场景模型 + +请求场景模型是以客户端视角的,描述客户端从触发请求意图到接收请求结果的抽象模型,分别由请求时机、请求行为、请求事件以及响应管理四个阶段组成。例如在进行一次请求时经常需要思考以下问题, + +1. 什么时候发出请求; +2. 是否要展示请求状态; +3. 是否需要对请求进行失败重试; +4. 要如何加工响应数据; +5. 是否需要对请求参数加密; +6. 是否要对高频使用的响应数据做缓存; +7. 如何进行跨页面操作数据; +8. 弱网或断网环境下需要如何处理请求; +9. ... + +`fetch`或`axios`往往更专注于如何与服务端交互,但对于上面的问题我们总是需要自己处理,这些有利于应用性能和稳定性的功能,总会让程序员们编写出低维护性的代码。请求场景模型就是从准备请求到响应数据加工完毕的所有环节进行抽象,从而覆盖以前端为视角的,整个 CS 交互生命周期的模型。`alova`就是一个以请求场景模型的库,它是对`axios`等请求库的一种补充,而非替代品。 + +> CS 交互:泛指所有客户端类型和服务端的数据交互 + +## 请求场景模型 + +![RSM](/img/rsm-cn.png) + +## 请求时机 + +描述在什么时候需要发出请求,在`alova`中以`useHook`实现。 + +- 初始化展示数据,如刚进入某个界面或子界面; +- 人机交互触发 CS 交互,需要变更数据重新发出请求,如翻页、筛选、排序、模糊搜索等; +- 以防抖方式发送请求,避免视图数据闪动,以及降低服务端压力 +- 预加载数据,如分页内预先加载下一页内容、预测用户点击某个按钮后预先拉取数据; +- 操作服务端数据,需发出增删改查请求,如提交数据、删除数据等; +- 同步服务端状态,如数据变化较快的场景下轮询请求、操作了某个数据后重新拉取数据; + +## 请求行为 + +描述以怎样的方式处理请求,在`alova`中以 Method 抽象实现。 + +- 占位请求,请求时展示 loading、骨架图、或者是上次使用的真实数据; +- 缓存高频响应,多次执行请求会使用保鲜数据; +- 多请求串行与并行; +- 重要接口的重试机制,降低网络不稳定造成的请求失败概率; +- 静默提交,当只关心提交数据时,提交请求后直接响应成功事件,后台保证请求成功; +- 离线提交,离线时将提交数据暂存到本地,网络连接后再提交; + +## 请求事件 + +表示携带请求参数发送请求,获得响应,`alova`可以与`axios`、`fetch`、`XMLHttpRequest`等任意请求库或原生方案共同协作。 + +## 响应管理 + +`alova`将响应数据状态化并统一管理,以请求层面的方式刷新视图数据、操作缓存,避免了在组件层面的操作,更加优雅和统一。 + +- 移除缓存响应数据,再次发起请求时将从服务端拉取; +- 更新缓存响应数据,可更新任意位置响应数据,非常有利于跨页面更新数据; +- 刷新响应数据,可重新刷新任意位置的响应数据,也非常有利于跨页面更新数据; +- 自定义设置缓存,在请求批量数据时,可手动对批量数据一一设置缓存,从而满足后续单条数据的缓存命中; diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/about/02-comparison.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/about/02-comparison.md index 6839ce538..593088dc1 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/about/02-comparison.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/about/02-comparison.md @@ -1,93 +1,92 @@ ---- -title: 与其他库比较 -sidebar_position: 20 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -## 与 axios 对比 - -axios 提供了基于 promise 的非常简单易用的 HTTP 请求功能,只需要简单的一行代码即可发送和接收请求,并且可以在浏览器和 nodejs 环境下运行,是一个非常优秀的请求 js 库。 - -但是 axios 聚焦于请求发送和接收响应,这意味着如果你需要自行编写更多代码来主动优化请求功能,而 alova 像是 axios 的武器装备,将 axios 与 alova 组合使用可以获得更强大的请求能力,以下是 alova 为 axios 附加的请求管理能力。 - -### alova 为 axios 提供自动化请求状态管理 - -仅使用 axios 时,通常需要你自行维护请求相关状态,使用 alova 的 use hook 后可以获得自动化的请求状态管理能力。 - - - - -```javascript -// vue3代码示例 -const loading = ref(false); -const data = ref({}); -const error = ref(null); -const request = async () => { - try { - loading.value = true; - data.value = await axios.get('/xxx'); - } catch (e) { - error.value = e; - } - loading.value = false; -}; -mounted(request); -``` - - - - -```javascript -// 将axios作为alova的请求适配器 -const { loading, data, error } = useRequest(alova.Get('/xxx')); -``` - - - - -### alova 提供开箱即用的高性能请求策略 - -alova 为你提供了[多个高性能的请求策略模块](/tutorial/strategy),你可以根据不同请求场景使用不同的模块,这是 axios 不具备的。 - -### alova 为 axios 提供响应数据缓存 - -alova 分别提供了 3 种缓存模式来满足不同的缓存场景,分别为内存模式、缓存占位模式、恢复模式。它们是组件无关的,只要请求地址和参数相同都可以命中缓存,除非你关闭了它。响应数据缓存可以极大地提高请求流畅性,降低服务端压力。 - -### alova 为 axios 提供请求共享功能 - -请求共享在同时发送多个相同请求时,将会复用同一个请求,它也可以提升应用流畅性和降低服务端压力。 - -### alova 为 axios 提供数据预拉取 - -提前请求将要使用的数据,也可以极大提升应用流畅性。 - -### alova 可管理请求状态 - -你可以使用 alova 跨任意的组件层级来访问其他组件内的状态化数据,这可以让你减少跨组件通信的一些麻烦。 - -## 与 react-query、swr 对比 - -react-query 是一个强大的异步状态管理,swr 是一个用于数据请求的 React Hooks 库,它们的共同特性也是使用 use hook 来发送和管理请求,和数据缓存功能,对于它们,alova 有以下不同之处。 - -### alova 的目标不同 - -实际上,alova 的 use hook 也是参考了 react-query 和 swr 的设计,但是 alova 选择了请求策略库的方向,你可以在不同的请求场景下使用不同的请求策略模块,让你在编写更少量代码同时,也能实现更高效地 Client-Server 数据交互。 - -### Method 代理设计 - -react-query 和 swr 都是在 use hook 中直接使用`axios`或`fetch api`发送请求,而 alova 使用了 `Method` 代理的设计模式,这样设计具有以下 3 个好处: - -1. 统一的使用体验,不会因平台或 UI 框架不同而存在不同的使用方式。 -2. `axios`和`fetch api`等请求库以请求适配器的方式,与每个 api 解耦,这让 alova 提供了统一的开发体验和完美的代码迁移。 -3. 每个 method 实例都代表不同的 api,你可以将同一个 api 的请求参数与请求行为参数聚合到同一个 method 实例中,而不会分散开,更适合管理大量的 api。 -4. alova 通过对 method 实例上的请求参数序列化,实现了自动化管理响应数据缓存,你不需要指定缓存 key,而 react-query 和 swr 都需要自定义设置`queryKey`来管理缓存。 - -### 高灵活性 - -alova 通过各种适配器、中间件实现了很高的灵活性,不仅可以运行在任何 js 环境,还可以支持用户自定义不同场景下的请求模块。 - -### 轻量化 - -alova 很轻量,体积只有 react-query 和 axios 的 30%+。与 swr 体积相似,但提供更丰富的功能。 +--- +title: 与其他库比较 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## 与 axios 对比 + +axios 提供了基于 promise 的非常简单易用的 HTTP 请求功能,只需要简单的一行代码即可发送和接收请求,并且可以在浏览器和 nodejs 环境下运行,是一个非常优秀的请求 js 库。 + +但是 axios 聚焦于请求发送和接收响应,这意味着如果你需要自行编写更多代码来主动优化请求功能,而 alova 像是 axios 的武器装备,将 axios 与 alova 组合使用可以获得更强大的请求能力,以下是 alova 为 axios 附加的请求管理能力。 + +### alova 为 axios 提供自动化请求状态管理 + +仅使用 axios 时,通常需要你自行维护请求相关状态,使用 alova 的 use hook 后可以获得自动化的请求状态管理能力。 + + + + +```javascript +// vue3代码示例 +const loading = ref(false); +const data = ref({}); +const error = ref(null); +const request = async () => { + try { + loading.value = true; + data.value = await axios.get('/xxx'); + } catch (e) { + error.value = e; + } + loading.value = false; +}; +mounted(request); +``` + + + + +```javascript +// 将axios作为alova的请求适配器 +const { loading, data, error } = useRequest(alova.Get('/xxx')); +``` + + + + +### alova 提供开箱即用的高性能请求策略 + +alova 为你提供了[多个高性能的请求策略模块](/next/tutorial/client/strategy),你可以根据不同请求场景使用不同的模块,这是 axios 不具备的。 + +### alova 为 axios 提供响应数据缓存 + +alova 分别提供了 3 种缓存模式来满足不同的缓存场景,分别为内存模式、缓存占位模式、恢复模式。它们是组件无关的,只要请求地址和参数相同都可以命中缓存,除非你关闭了它。响应数据缓存可以极大地提高请求流畅性,降低服务端压力。 + +### alova 为 axios 提供请求共享功能 + +请求共享在同时发送多个相同请求时,将会复用同一个请求,它也可以提升应用流畅性和降低服务端压力。 + +### alova 为 axios 提供数据预拉取 + +提前请求将要使用的数据,也可以极大提升应用流畅性。 + +### alova 可管理请求状态 + +你可以使用 alova 跨任意的组件层级来访问其他组件内的状态化数据,这可以让你减少跨组件通信的一些麻烦。 + +## 与 react-query、swr 对比 + +react-query 是一个强大的异步状态管理,swr 是一个用于数据请求的 React Hooks 库,它们的共同特性也是使用 use hook 来发送和管理请求,和数据缓存功能,对于它们,alova 有以下不同之处。 + +### alova 的目标不同 + +实际上,alova 的 use hook 也是参考了 react-query 和 swr 的设计,但是 alova 选择了请求策略库的方向,你可以在不同的请求场景下使用不同的请求策略模块,让你在编写更少量代码同时,也能实现更高效地 Client-Server 数据交互。 + +### Method 代理设计 + +react-query 和 swr 都是在 use hook 中直接使用`axios`或`fetch api`发送请求,而 alova 使用了 `Method` 代理的设计模式,这样设计具有以下 3 个好处: + +1. 统一的使用体验,不会因平台或 UI 框架不同而存在不同的使用方式。 +2. `axios`和`fetch api`等请求库以请求适配器的方式,与每个 api 解耦,这让 alova 提供了统一的开发体验和完美的代码迁移。 +3. 每个 method 实例都代表不同的 api,你可以将同一个 api 的请求参数与请求行为参数聚合到同一个 method 实例中,而不会分散开,更适合管理大量的 api。 +4. alova 通过对 method 实例上的请求参数序列化,实现了自动化管理响应数据缓存,你不需要指定缓存 key,而 react-query 和 swr 都需要自定义设置`queryKey`来管理缓存。 + +### 高灵活性 + +alova 通过各种适配器、中间件实现了很高的灵活性,不仅可以运行在任何 js 环境,还可以支持用户自定义不同场景下的请求模块。 + +### 轻量化 + +alova 很轻量,体积只有 react-query 和 axios 的 30%+。与 swr 体积相似,但提供更丰富的功能。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/about/03-qa.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/about/03-qa.md new file mode 100644 index 000000000..0ae23d6cd --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/about/03-qa.md @@ -0,0 +1,132 @@ +--- +title: 提问&回答 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## 为什么创造 alova? + +数据请求一直是应用程序必不可少的重要部分,自从 XMLHttpRequest 诞生以来请求方案层出不穷,客户端的数据交互探索一直聚焦于请求的简单性,如`$.ajax`、`axios`、`fetch api`以及`react-query`等请求工具,编码形式从回调函数、Promise,再到 usehook 不断发展,这些 js 库在请求简单性上已经做得很好了,不过它们只提供了通用的功能,这意味着对于不同的请求场景例如共享请求、分页请求、表单提交、上传和下载文件等,开发者依然需要自己编写复杂的代码,降低开发效率,性能也无法得到保证,在越来越看重的用户体验的时代,应用的流畅性变得越来越重要。 + +同时,客户端和服务端的协作也是割裂的,前端工程师需要查阅 API 文档并手动编写 API 函数,并且服务端 API 的任何更改都需要主动通知前端工程师,这将会使产品变得更加不可控。 + +**而我们认为还有更简单的方案,即根据请求场景,例如分页、表单提交、断点续传等,选择对应的 useHook,它将帮你管理数据,控制何时应该发送请求**。从而让开发者在编写少量代码也能实现更高效地 Client-Server 数据交互。 + +同时,alova 具有很灵活的扩展能力来实现不同场景下的请求策略,你也可以自定义自己的请求场景,这部分内容在[自定义章节](/next/tutorial/advanced/custom)。 + +为了覆盖更多请求场景,我们还将请求场景抽象成了 [请求场景模型(RSM)](/about/RSM),它很好地解释了 alova 的请求策略方案。在未来,alova 将承载着我们对请求策略的探索之路继续前行。 + +## 替代请求库? + +alova 是一个请求策略库,它的创建初衷是对不同请求场景提供特定的请求策略解决方案,从而更简洁优雅地实现流畅的请求体验,而例如`$.ajax`、`axios`和`fetch-api`等对请求发送和响应接收提供了很好的支持,它们是 [RSM](/about/RSM) 流程中必不可少的一个环节(请求事件),alova 仍然需要依靠它们进行请求,因此我们可以将 alova 看作是请求库的一种武装,让请求库变得更加强大。 + +## 为什么要深度绑定 UI 框架? + +对一个 js 库来说解耦意味着更多场景下的使用,例如 axios 可以在 nodejs 中使用,但同时意味着开发者需要写更多的模板代码,比如使用 useHooks 封装 axios 等。而 alova 摒弃了解耦带来的更多使用场景,将使用范围定位在与 UI 框架配合使用,以最精简的方式使用 alova,这是为了开发者的收益方面而考量的,在一个 UI 框架盛行的时候,深度绑定可以为开发者提供直接使用的功能,提升开发者的使用体验,而不需要太多的模板代码。 + +## 如何通过 cdn 使用 alova? + + + + +```html + + + + + + + + + +
+
Loading...
+
{{ error.message }}
+ responseData: {{ data }} +
+ + + +``` + +
+ + +```html + + + + + + + + + + + +
+ + + +``` + +
+ + +:::tip + +svelte 依赖于编译工具,不能通过 CDN 直接使用,详情见 [svelte.dev](https://svelte.dev/) + +::: + + +
+ +## 在 React-Native 中要注意什么? + +使用 alova 开发 React-Native 应用时,你也可以使用 `alova/fetch`。 + +但是有以下的注意事项: + +**metro 版本** + +在 alova 中的`package.json`中使用了`exports`来定义多个导出项,因此需要确保这两点: + +1. metro 版本高于 0.76.0 +2. 在`metro.config.js`中开启`resolver.unstable_enablePackageExports`。[详情点此查看](https://facebook.github.io/metro/docs/configuration/#unstable_enablepackageexports-experimental) diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/api/01-alova.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/api/01-alova.md index 8e8a04786..b1bf84241 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/api/01-alova.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/api/01-alova.md @@ -1,6 +1,5 @@ --- title: alova实例 -sidebar_position: 10 --- ## createAlova() @@ -17,19 +16,18 @@ function createAlova(options?: AlovaOptions): Alova; 1. config: 配置参数 -| 参数名 | 类型 | 说明 | -| -------------- | --------------------------- | ----------------------------------------------------------------------------------------- | -| baseURL | string | 基础路径,默认为空,[查看详情](/tutorial/getting-started/alova) | -| statesHook | object | 状态管理钩子,选填,[查看详情](/tutorial/combine-framework) | -| requestAdapter | object | 请求适配器,必填,[查看详情](/tutorial/custom/custom-http-adapter) | -| timeout | number | 超时时间,默认不超时,[查看详情](/tutorial/getting-started/alova) | -| localCache | object | 本地缓存配置,默认 GET 有 5000ms 缓存,[查看详情](/tutorial/cache/mode) | -| storageAdapter | object | 本地存储适配器,默认为`localStorage`,[查看详情](/tutorial/custom/custom-storage-adapter) | -| beforeRequest | function | 请求前钩子,[查看详情](/tutorial/getting-started/global-interceptor) | -| responded | object \| function | 请求响应钩子,[查看详情](/tutorial/getting-started/global-interceptor) | -| shareRequest | boolean | 共享请求,[查看详情](/tutorial/getting-started/alova) | -| errorLogger | boolean\| null \| function | 错误日志,[查看详情](/tutorial/advanced/error-logger) | -| cacheLogger | boolean \| null \| function | 缓存日志,[查看详情](/tutorial/advanced/cache-logger) | +| 参数名 | 类型 | 说明 | +| -------------- | --------------------------- | ------------------------------------------------------------------------------------------------ | +| baseURL | string | 基础路径,默认为空,[查看详情](/next/tutorial/getting-started/basic/alova) | +| statesHook | object | 状态管理钩子,选填,[查看详情](/next/tutorial/getting-started/basic/combine-framework) | +| requestAdapter | object | 请求适配器,必填,[查看详情](/next/tutorial/advanced/custom/http-adapter) | +| timeout | number | 超时时间,默认不超时,[查看详情](/next/tutorial/getting-started/basic/alova) | +| localCache | object | 本地缓存配置,默认 GET 有 5000ms 缓存,[查看详情](/next/tutorial/cache/mode) | +| storageAdapter | object | 本地存储适配器,默认为`localStorage`,[查看详情](/next/tutorial/advanced/custom/storage-adapter) | +| beforeRequest | function | 请求前钩子,[查看详情](/next/tutorial/getting-started/basic/global-interceptor) | +| responded | object \| function | 请求响应钩子,[查看详情](/next/tutorial/getting-started/basic/global-interceptor) | +| shareRequest | boolean | 共享请求,[查看详情](/next/tutorial/getting-started/basic/alova) | +| cacheLogger | boolean \| null \| function | 缓存日志,[查看详情](/next/tutorial/advanced/in-depth/cache-logger) | - **返回** @@ -39,11 +37,13 @@ Alova 实例 ```ts import { createAlova } from 'alova'; +import VueHook from 'alova/vue'; +import adapterFetch from 'alova/fetch'; const alova = createAlova({ baseURL: 'https://example.com', statesHook: VueHook, - requestAdapter: GlobalFetch(), + requestAdapter: adapterFetch(), timeout: 3000 // ... }); @@ -51,7 +51,7 @@ const alova = createAlova({ ## alova.id -alova 实例 id,用于区分不同的 alova 实例,可在[method 匹配器](/tutorial/advanced/method-matcher)中精准匹配指定 alova 的 method 实例。 +alova 实例 id,用于区分不同的 alova 实例,可在[method 匹配器](/next/tutorial/client/in-depth/method-matcher)中精准匹配指定 alova 的 method 实例。 - **类型**:string @@ -108,18 +108,16 @@ interface Alova { 1. url: 请求地址 2. config: 配置参数 -| 参数名 | 类型 | 说明 | -| -------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| headers | object | 请求头,[查看详情](/tutorial/getting-started/method) | -| params | object | 请求参数,[查看详情](/tutorial/getting-started/method) | -| name | string | method 对象名称,在 [updateState](/tutorial/advanced/update-across-components)、[invalidateCache](/tutorial/cache/manually-invalidate)、[setCache](/tutorial/cache/set-and-query)、以及 [fetch 函数](/tutorial/advanced/use-fetcher)中可以通过名称或通配符获取对应 method 实例 | -| timeout | number | 请求超时时间,[查看详情](/tutorial/getting-started/method) | -| localCache | LocalCacheConfig | 响应缓存时间,[查看详情](/tutorial/cache/mode) | -| hitSource | string | 打击源方法实例,当源方法实例请求成功时,当前方法实例的缓存将被失效,[查看详情](/tutorial/cache/auto-invalidate) | -| enableDownload | boolean | 开启下载进度信息,[查看详情](/tutorial/combine-framework/download-upload-progress) | -| enableUpload | boolean | 开启上传进度信息,[查看详情](/tutorial/combine-framework/download-upload-progress) | -| transformData | function | 转换响应数据,[查看详情](/tutorial/combine-framework/response) | -| shareRequest | boolean | 请求级共享请求开关,[查看详情](/tutorial/getting-started/method) | +| 参数名 | 类型 | 说明 | +| ------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| headers | object | 请求头,[查看详情](/next/tutorial/getting-started/basic/method) | +| params | object | 请求参数,[查看详情](/next/tutorial/getting-started/basic/method) | +| name | string | method 对象名称,在 [updateState](/next/tutorial/client/in-depth/update-across-components)、[invalidateCache](/next/tutorial/cache/manually-invalidate)、[setCache](/next/tutorial/cache/set-and-query)、以及 [fetch 函数](/next/tutorial/client/strategy/use-fetcher)中可以通过名称或通配符获取对应 method 实例 | +| timeout | number | 请求超时时间,[查看详情](/next/tutorial/getting-started/basic/method) | +| localCache | LocalCacheConfig | 响应缓存时间,[查看详情](/next/tutorial/cache/mode) | +| hitSource | string | 打击源方法实例,当源方法实例请求成功时,当前方法实例的缓存将被失效,[查看详情](/next/tutorial/cache/auto-invalidate) | +| transformData | function | 转换响应数据,[查看详情](/next/tutorial/getting-started/basic/method) | +| shareRequest | boolean | 请求级共享请求开关,[查看详情](/next/tutorial/getting-started/basic/method) | > 除了可配置上面的参数外,还支持请求适配器支持的其他参数。 @@ -146,7 +144,11 @@ const getUsers = alovaInstance.Get('/users', { ```ts interface Alova { - Post(url: string, data?: object | FormData | string | null, config?: AlovaMethodCreateConfig): Method; + Post( + url: string, + data?: object | FormData | string | null, + config?: AlovaMethodCreateConfig + ): Method; } ``` @@ -184,7 +186,11 @@ const postUsers = alovaInstance.Post( ```ts interface Alova { - Delete(url: string, data?: object | FormData | string | null, config?: AlovaMethodCreateConfig): Method; + Delete( + url: string, + data?: object | FormData | string | null, + config?: AlovaMethodCreateConfig + ): Method; } ``` @@ -220,7 +226,11 @@ const deleteUsers = alovaInstance.Delete( ```ts interface Alova { - Put(url: string, data?: object | FormData | string | null, config?: AlovaMethodCreateConfig): Method; + Put( + url: string, + data?: object | FormData | string | null, + config?: AlovaMethodCreateConfig + ): Method; } ``` @@ -278,7 +288,11 @@ method 实例 ```ts interface Alova { - Patch(url: string, data?: object | FormData | string | null, config?: AlovaMethodCreateConfig): Method; + Patch( + url: string, + data?: object | FormData | string | null, + config?: AlovaMethodCreateConfig + ): Method; } ``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/api/02-method.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/api/02-method.md index 00430e831..d8937b344 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/api/02-method.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/api/02-method.md @@ -1,6 +1,5 @@ --- title: method实例 -sidebar_position: 20 --- 一个 method 实例对应一个请求信息描述,它拥有一次请求的 url、请求头、请求参数,以及响应数据处理、缓存数据处理等请求行为参数。通过 method 实例,你可以在任意的 js 环境下感受到统一的使用体验,只需要非常少的改动就可以正常运行起来,此外,method 实例将请求参数和请求行为参数放在了一起,更便于 api 的管理,而不是分散在多个代码文件中。 @@ -52,7 +51,7 @@ interface MethodConstructor { 1. `type`:请求类型 2. `context`:alova 实例 3. `url`:请求 url -4. `config`:配置参数, 类型与[alova.Get](/api/alova#alovaget)的 config 参数类型一致 +4. `config`:配置参数, 类型与[alova.Get](/next/api/alova#alovaget)的 config 参数类型一致 5. `data`:请求体数据 - **示例** @@ -97,7 +96,7 @@ const methodKey = getMethodKey(method); ## matchSnapshotMethod() -以[method 匹配器](/tutorial/advanced/method-matcher)的匹配方式获取已经请求过的 method 实例快照,并返回匹配的结果。 +以[method 匹配器](/next/tutorial/client/in-depth/method-matcher)的匹配方式获取已经请求过的 method 实例快照,并返回匹配的结果。 - **类型** @@ -110,7 +109,10 @@ type MethodFilter = filter?: MethodFilterHandler; alova?: Alova; }; -function matchSnapshotMethod(matcher: MethodFilter, matchAll?: boolean): Method[] | Method | undefined; +function matchSnapshotMethod( + matcher: MethodFilter, + matchAll?: boolean +): Method[] | Method | undefined; ``` - **参数** @@ -151,7 +153,7 @@ interface Method { ## method.baseURL -请求的基础路径,继承于[alova 实例](/api/alova)。 +请求的基础路径,继承于[alova 实例](/next/api/alova)。 - **类型** @@ -223,7 +225,7 @@ interface Method { ## method.meta -method 的 元数据,用于记录请求特性信息,[详情查看](/tutorial/getting-started/method-metadata)。 +method 的 元数据,用于记录请求特性信息,[详情查看](/next/tutorial/getting-started/basic/method-metadata)。 - **类型** @@ -235,7 +237,7 @@ interface Method { ## method.config -通过`alova.Get/alova.Post`等方法创建 method 时的配置信息,[详情查看](/api/alova#alovaget)。 +通过`alova.Get/alova.Post`等方法创建 method 时的配置信息,[详情查看](/next/api/alova#alovaget)。 - **类型** @@ -311,7 +313,10 @@ method.abort(); ```ts interface Method { - then(onFulfilled?: (value: Response) => any, onRejected?: (reason: any) => any): Promise; + then( + onFulfilled?: (value: Response) => any, + onRejected?: (reason: any) => any + ): Promise; } ``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/api/03-core-hooks.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/api/03-core-hooks.md index 3ba7087fa..4ec8a5bc5 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/api/03-core-hooks.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/api/03-core-hooks.md @@ -1,268 +1,267 @@ ---- -title: 核心useHooks -sidebar_position: 30 ---- - -## useRequest - -表示一次请求的发送,执行 useRequest 时默认会发送一次请求,并创建和维护状态化的请求相关数据,如`loading/data/error`等。在页面获取初始数据时是最常用的方法,同时也支持关闭它的默认的请求发送,这在提交数据等通过点击事件触发的请求场景非常有用。 - -> 前往[useRequest](/tutorial/combine-framework/use-request)查看详情。 - -### 类型 - -```ts -function useRequest( - methodHandler: Method | (...args: any[]) => Method, - config?: RequestHookConfig -): UseHookReturnType; -``` - -### 参数 - -1. `methodHandler`: 可传入 method 实例和函数两种形式,当指定为函数时的可接收`send`传入的参数,并要求返回一个 method 实例。 -2. `config`: hook 的配置参数。 - -| 名称 | 描述 | 类型 | 默认值 | 版本 | -| ------------- | ------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | ------ | ---- | -| immediate | 是否立即发起请求 | boolean | true | - | -| initialData | 初始的 data 值,在首次响应前 data 值为初始值,未设置时为`undefined` | any | - | - | -| force | 是否强制请求,可设置为函数动态返回 boolean 值 | boolean \| (...args: any[]) => boolean \| false | - | - | -| managedStates | 额外的监管状态,可通过 updateState 更新 | Record\ | - | - | -| middleware | 中间件函数,[了解 alova 中间件](/tutorial/advanced/middleware) | (context: [AlovaFrontMiddlewareContext](#alovafrontmiddlewarecontext), next: [AlovaGuardNext](#alovaguardnext)) => Promise\ | - | - | - -#### AlovaFrontMiddlewareContext - -| 名称 | 描述 | 类型 | 版本 | -| ---------------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- | -| method | 当前请求的 method 对象 | Method | - | -| cachedResponse | 命中的缓存数据 | any | - | -| config | 当前的 use hook 配置 | Record\ | - | -| sendArgs | 响应处理回调的参数,该参数由 use hooks 的 send 传入 | any[] | - | -| frontStates | use hook 前端状态集合,如 data、loading、error 等 | [FrontRequestState](#frontrequeststate) | - | -| send | 发送请求函数 | (...args: any[]) => void | Promise | -| abort | 中断函数 | () => void | - | -| decorateSuccess | 装饰成功回调函数 | (decorator: (
handler: (event: [AlovaSuccessEvent](#alovasuccessevent)) => void,
event: [AlovaSuccessEvent](#alovasuccessevent),
index: number,
length: number
) => void) => void | - | -| decorateError | 装饰失败回调函数 | (decorator: (
handler: (event: [AlovaErrorEvent](#alovaerrorevent)) => void,
event: [AlovaErrorEvent](#alovaerrorevent),
index: number,
length: number
) => void) => void | - | -| decorateComplete | 装饰完成回调函数 | (decorator: (
handler: (event: [AlovaCompleteEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovacompleteevent),
index: number,
length: number
) => void) => void | - | -| update | 更新当前 use hook 前端状态的函数,在 react 中较有用 | (newFrontStates: [FrontRequestState](#frontrequeststate)) => void; | - | -| controlLoading | 将自定义控制 loading 的状态,调用内部不再触发 loading 状态的变更,传入 control 为 false 时将取消控制 | (control?: boolean) => void | - | - -#### AlovaGuardNext - -```typescript -type AlovaGuardNext = (guardNextConfig?: { - force?: boolean | (...args: any[]) => boolean; - method?: Method; -}): Promise; -``` - -#### FrontRequestState - -以下属性值将会根据`statesHook`自动推断出对应 UI 框架的响应式数据类型,在 vue3 中为`Ref`类型,在 react 中为普通值,在 svelte 中为`Writable`类型 - -| 名称 | 描述 | 类型 | 版本 | -| ----------- | ------------ | ------------------ | ---- | -| loading | 请求加载状态 | boolean | - | -| data | 响应数据 | any | - | -| error | 请求错误信息 | Error \| undefined | - | -| downloading | 下载进度信息 | Object | - | -| uploading | 上传进度信息 | Object | - | - -#### AlovaSuccessEvent - -| 名称 | 描述 | 类型 | 版本 | -| --------- | --------------------------------------------------- | ------- | ---- | -| method | 当前请求的 method 对象 | Method | - | -| sendArgs | 响应处理回调的参数,该参数由 use hooks 的 send 传入 | any[] | - | -| data | 响应数据 | any | - | -| fromCache | 响应数据是否来自缓存 | boolean | - | - -#### AlovaErrorEvent - -| 名称 | 描述 | 类型 | 版本 | -| -------- | --------------------------------------------------- | ------ | ---- | -| method | 当前请求的 method 对象 | Method | - | -| sendArgs | 响应处理回调的参数,该参数由 use hooks 的 send 传入 | any[] | - | -| error | 响应错误实例 | Error | - | - -#### AlovaCompleteEvent - -| 名称 | 描述 | 类型 | 版本 | -| --------- | --------------------------------------------------- | -------------------- | ---- | -| method | 当前请求的 method 对象 | Method | - | -| sendArgs | 响应处理回调的参数,该参数由 use hooks 的 send 传入 | any[] | - | -| status | 响应状态,成功时为 success,失败时为 error | 'success' \| 'error' | - | -| data | 响应数据,成功时有值 | any | - | -| fromCache | 响应数据是否来自缓存,成功时有值 | boolean | - | -| error | 响应错误实例,失败时有值 | Error | - | - -### 返回值 - -`UseHookReturnType`包含响应数据和请求相关的状态、操作函数和事件绑定函数,它们会根据`statesHook`自动推断出对应 UI 框架的响应式数据类型,在 vue3 中为`Ref`类型,在 react 中为普通值,在 svelte 中为`Writable`类型。 - -#### 响应式数据 - -| 名称 | 描述 | 类型 | 版本 | -| ----------- | ------------ | ------------------ | ---- | -| loading | 请求加载状态 | boolean | - | -| data | 响应数据 | any | - | -| error | 请求错误信息 | Error \| undefined | - | -| downloading | 下载进度信息 | Object | - | -| uploading | 上传进度信息 | Object | - | - -#### 操作函数 - -| 名称 | 描述 | 函数参数 | 返回值 | 版本 | -| ------ | --------------------------------------------------- | ------------------------------------------------------- | ------- | ---- | -| send | 发送请求函数 | ...args: any[] | - | - | -| abort | 中断函数 | - | Promise | - | -| update | 更新当前 use hook 前端状态的函数,在 react 中较有用 | newFrontStates: [FrontRequestState](#frontrequeststate) | - | - -#### 事件 - -| 名称 | 描述 | 回调参数 | 版本 | -| ---------- | ---------------- | ------------------------------------------------ | ---- | -| onSuccess | 请求成功事件绑定 | event: [AlovaSuccessEvent](#alovasuccessevent) | - | -| onError | 请求错误事件绑定 | event: [AlovaErrorEvent](#alovaerrorevent) | - | -| onComplete | 请求完成事件绑定 | event: [AlovaCompleteEvent](#alovacompleteevent) | - | - -## useWatcher - -监听状态,并在状态变化后发起请求,在一些需要随数据变化而重新请求的场景下,如分页、数据筛选、模糊搜索使用。 - -> 前往[状态变化请求](/tutorial/combine-framework/use-watcher)查看详情。 - -### 类型 - -```typescript -function useWatcher( - handler: Method | (...args: any[]) => Method, - watchingStates: State[], - config?: WatcherHookConfig -): UseHookReturnType; -``` - -### 参数 - -1. `handler`: 可传入 method 实例和函数两种形式,当指定为函数时的可接收`send`传入的参数,并要求返回一个 method 实例。 -2. `config`: hook 的配置参数。 - -| 名称 | 描述 | 类型 | 默认值 | 版本 | -| ------------- | -------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | ---------- | ---- | -| immediate | 是否立即发起请求 | boolean | true | - | -| initialData | 初始的 data 值,在首次响应前 data 值为初始值,未设置时为`undefined` | any | - | - | -| force | 是否强制请求,可设置为函数动态返回 boolean 值 | boolean \| (...args: any[]) => boolean \| false | -| managedStates | 额外的监管状态,可通过 updateState 更新 | Record\ | - | -| debounce | 请求防抖时间(毫秒),传入数组时可按 watchingStates 的顺序单独设置防抖时间 | number \| number[] | - | -| middleware | 中间件函数,[了解 alova 中间件](/tutorial/advanced/middleware) | (context: [AlovaFrontMiddlewareContext](#alovafrontmiddlewarecontext), next: [AlovaGuardNext](#alovaguardnext)) => Promise\ | - | - | -| sendable | 监听的状态改变时是否发送请求 | (methodInstance: AlovaEvent) => boolean | () => true | - | -| abortLast | 是否中断上一次的未响应请求 | boolean | true | - | - -### 返回值 - -`UseHookReturnType`包含响应数据和请求相关的状态、操作函数和事件绑定函数,它们会根据 statesHook 自动推断出对应 UI 框架的响应式数据类型,在 vue3 中为 Ref 类型,在 react 中为普通值,在 svelte 中为 Writable 类型。 - -#### 响应式数据 - -| 名称 | 描述 | 类型 | 版本 | -| ----------- | ------------ | ------------------ | ---- | -| loading | 请求加载状态 | boolean | - | -| data | 响应数据 | any | - | -| error | 请求错误信息 | Error \| undefined | - | -| downloading | 下载进度信息 | Object | - | -| uploading | 上传进度信息 | Object | - | - -#### 操作函数 - -| 名称 | 描述 | 函数参数 | 返回值 | 版本 | -| ------ | --------------------------------------------------- | ------------------------------------------------------- | ------- | ---- | -| send | 发送请求函数 | ...args: any[] | Promise | - | -| abort | 中断函数 | - | - | - | -| update | 更新当前 use hook 前端状态的函数,在 react 中较有用 | newFrontStates: [FrontRequestState](#frontrequeststate) | - | - -#### 事件 - -| 名称 | 描述 | 回调参数 | 版本 | -| ---------- | ---------------- | ------------------------------------------------ | ---- | -| onSuccess | 请求成功事件绑定 | event: [AlovaSuccessEvent](#alovasuccessevent) | - | -| onError | 请求错误事件绑定 | event: [AlovaErrorEvent](#alovaerrorevent) | - | -| onComplete | 请求完成事件绑定 | event: [AlovaCompleteEvent](#alovacompleteevent) | - | - -## useFetcher - -通过`useFetcher`用来拉取数据,在预加载数据和跨模块更新状态时很有用。 - -> 前往[数据拉取](/tutorial/advanced/use-fetcher)查看详情。 - -### 类型 - -```typescript -function useFetcher(config?: FetcherHookConfig): UseFetchHookReturnType; -``` - -### 参数 - -1. `config`: hook 的配置参数。 - -| 名称 | 描述 | 类型 | 默认值 | 版本 | -| ---------- | -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ | ---- | -| force | 是否强制请求,可设置为函数动态返回 boolean 值 | boolean | (...args: any[]) => boolean \| false | - | -| middleware | 中间件函数,[了解 alova 中间件](/tutorial/advanced/middleware) | (context: [AlovaFetcherMiddlewareContext](#alovafetchermiddlewarecontext), next: [AlovaGuardNext](#alovaguardnext)) => Promise\ | - | - | - -#### AlovaFetcherMiddlewareContext - -| 名称 | 描述 | 类型 | 版本 | -| ---------------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- | -| method | 当前请求的 method 对象 | Method | - | -| cachedResponse | 命中的缓存数据 | any | - | -| config | 当前的 use hook 配置 | Record\ | - | -| fetchArgs | 响应处理回调的参数,该参数由 useFetcher 的 fetch 传入 | any[] | - | -| fetchStates | use hook 预加载状态集合,如 fetching、error 等 | [FetchRequestState](#fetchrequeststate) | - | -| fetch | 数据预加载函数 | (method: Method, ...args: any[]) => void | Promise | -| abort | 中断函数 | () => void | - | -| decorateSuccess | 装饰成功回调函数 | (decorator: (
handler: (event: [AlovaSuccessEvent](#alovasuccessevent)) => void,
event: [AlovaSuccessEvent](#alovasuccessevent),
index: number,
length: number
) => void) => void | - | -| decorateError | 装饰失败回调函数 | (decorator: (
handler: (event: [AlovaErrorEvent](#alovaerrorevent)) => void,
event: [AlovaErrorEvent](#alovaerrorevent),
index: number,
length: number
) => void) => void | - | -| decorateComplete | 装饰完成回调函数 | (decorator: (
handler: (event: [AlovaCompleteEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovacompleteevent),
index: number,
length: number
) => void) => void | - | -| update | 更新当前 use hook 预加载状态的函数,在 react 中较有用 | (newFrontStates: [FetchRequestState](#fetchrequeststate)) => void; | - | -| controlFetching | 调用后将自定义控制 fetching 的状态,内部不再触发 fetching 状态的变更,传入 control 为 false 时将取消控制 | (control?: boolean) => void | - | - -#### FetchRequestState - -以下属性值将会根据`statesHook`自动推断出对应 UI 框架的响应式数据类型,在 vue3 中为`Ref`类型,在 react 中为普通值,在 svelte 中为`Writable`类型 - -| 名称 | 描述 | 类型 | 版本 | -| ----------- | -------------- | ------------------ | ---- | -| fetching | 预加载请求状态 | boolean | - | -| error | 请求错误信息 | Error \| undefined | - | -| downloading | 下载进度信息 | Object | - | -| uploading | 上传进度信息 | Object | - | - -### 返回值 - -`UseFetchHookReturnType`包含请求相关的状态、操作函数和事件绑定函数,它们会根据 statesHook 自动推断出对应 UI 框架的响应式数据类型,在 vue3 中为 Ref 类型,在 react 中为普通值,在 svelte 中为 Writable 类型。 - -#### 响应式数据 - -| 名称 | 描述 | 类型 | 版本 | -| ----------- | -------------- | ------------------ | ---- | -| fetching | 预加载请求状态 | boolean | - | -| error | 请求错误信息 | Error \| undefined | - | -| downloading | 下载进度信息 | Object | - | -| uploading | 上传进度信息 | Object | - | - -#### 操作函数 - -| 名称 | 描述 | 函数参数 | 返回值 | 版本 | -| ------ | --------------------------------------------------- | ------------------------------------------------------- | ------- | ---- | -| fetch | 数据预加载函数 | 1. method: 预加载的 Method 实例
2. ...args: any[] | Promise | - | -| abort | 中断函数 | - | - | - | -| update | 更新当前 use hook 前端状态的函数,在 react 中较有用 | newFrontStates: [FrontRequestState](#frontrequeststate) | - | - -#### 事件 - -| 名称 | 描述 | 回调参数 | 版本 | -| ---------- | ---------------- | ---------------------------------------------------------------------------------------------------------------- | ---- | -| onSuccess | 请求成功事件绑定 | event: [AlovaSuccessEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovasuccessevent) | - | -| onError | 请求错误事件绑定 | event: [AlovaErrorEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovaerrorevent) | - | -| onComplete | 请求完成事件绑定 | event: [AlovaCompleteEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovacompleteevent) | - | +--- +title: 核心useHooks +--- + +## useRequest + +表示一次请求的发送,执行 useRequest 时默认会发送一次请求,并创建和维护状态化的请求相关数据,如`loading/data/error`等。在页面获取初始数据时是最常用的方法,同时也支持关闭它的默认的请求发送,这在提交数据等通过点击事件触发的请求场景非常有用。 + +> 前往[useRequest](/next/tutorial/client/strategy/use-request)查看详情。 + +### 类型 + +```ts +function useRequest( + methodHandler: Method | (...args: any[]) => Method, + config?: RequestHookConfig +): UseHookReturnType; +``` + +### 参数 + +1. `methodHandler`: 可传入 method 实例和函数两种形式,当指定为函数时的可接收`send`传入的参数,并要求返回一个 method 实例。 +2. `config`: hook 的配置参数。 + +| 名称 | 描述 | 类型 | 默认值 | 版本 | +| ------------- | -------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | ------ | ---- | +| immediate | 是否立即发起请求 | boolean | true | - | +| initialData | 初始的 data 值,在首次响应前 data 值为初始值,未设置时为`undefined` | any | - | - | +| force | 是否强制请求,可设置为函数动态返回 boolean 值 | boolean \| (...args: any[]) => boolean \| false | - | - | +| managedStates | 额外的监管状态,可通过 updateState 更新 | Record\ | - | - | +| middleware | 中间件函数,[了解 alova 中间件](/next/tutorial/client/in-depth/middleware) | (context: [AlovaFrontMiddlewareContext](#alovafrontmiddlewarecontext), next: [AlovaGuardNext](#alovaguardnext)) => Promise\ | - | - | + +#### AlovaFrontMiddlewareContext + +| 名称 | 描述 | 类型 | 版本 | +| ---------------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- | +| method | 当前请求的 method 对象 | Method | - | +| cachedResponse | 命中的缓存数据 | any | - | +| config | 当前的 use hook 配置 | Record\ | - | +| sendArgs | 响应处理回调的参数,该参数由 use hooks 的 send 传入 | any[] | - | +| frontStates | use hook 前端状态集合,如 data、loading、error 等 | [FrontRequestState](#frontrequeststate) | - | +| send | 发送请求函数 | (...args: any[]) => void | Promise | +| abort | 中断函数 | () => void | - | +| decorateSuccess | 装饰成功回调函数 | (decorator: (
handler: (event: [AlovaSuccessEvent](#alovasuccessevent)) => void,
event: [AlovaSuccessEvent](#alovasuccessevent),
index: number,
length: number
) => void) => void | - | +| decorateError | 装饰失败回调函数 | (decorator: (
handler: (event: [AlovaErrorEvent](#alovaerrorevent)) => void,
event: [AlovaErrorEvent](#alovaerrorevent),
index: number,
length: number
) => void) => void | - | +| decorateComplete | 装饰完成回调函数 | (decorator: (
handler: (event: [AlovaCompleteEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovacompleteevent),
index: number,
length: number
) => void) => void | - | +| update | 更新当前 use hook 前端状态的函数,在 react 中较有用 | (newFrontStates: [FrontRequestState](#frontrequeststate)) => void; | - | +| controlLoading | 将自定义控制 loading 的状态,调用内部不再触发 loading 状态的变更,传入 control 为 false 时将取消控制 | (control?: boolean) => void | - | + +#### AlovaGuardNext + +```typescript +type AlovaGuardNext = (guardNextConfig?: { + force?: boolean | (...args: any[]) => boolean; + method?: Method; +}): Promise; +``` + +#### FrontRequestState + +以下属性值将会根据`statesHook`自动推断出对应 UI 框架的响应式数据类型,在 vue3 中为`Ref`类型,在 react 中为普通值,在 svelte 中为`Writable`类型 + +| 名称 | 描述 | 类型 | 版本 | +| ----------- | ------------ | ------------------ | ---- | +| loading | 请求加载状态 | boolean | - | +| data | 响应数据 | any | - | +| error | 请求错误信息 | Error \| undefined | - | +| downloading | 下载进度信息 | Object | - | +| uploading | 上传进度信息 | Object | - | + +#### AlovaSuccessEvent + +| 名称 | 描述 | 类型 | 版本 | +| --------- | --------------------------------------------------- | ------- | ---- | +| method | 当前请求的 method 对象 | Method | - | +| sendArgs | 响应处理回调的参数,该参数由 use hooks 的 send 传入 | any[] | - | +| data | 响应数据 | any | - | +| fromCache | 响应数据是否来自缓存 | boolean | - | + +#### AlovaErrorEvent + +| 名称 | 描述 | 类型 | 版本 | +| -------- | --------------------------------------------------- | ------ | ---- | +| method | 当前请求的 method 对象 | Method | - | +| sendArgs | 响应处理回调的参数,该参数由 use hooks 的 send 传入 | any[] | - | +| error | 响应错误实例 | Error | - | + +#### AlovaCompleteEvent + +| 名称 | 描述 | 类型 | 版本 | +| --------- | --------------------------------------------------- | -------------------- | ---- | +| method | 当前请求的 method 对象 | Method | - | +| sendArgs | 响应处理回调的参数,该参数由 use hooks 的 send 传入 | any[] | - | +| status | 响应状态,成功时为 success,失败时为 error | 'success' \| 'error' | - | +| data | 响应数据,成功时有值 | any | - | +| fromCache | 响应数据是否来自缓存,成功时有值 | boolean | - | +| error | 响应错误实例,失败时有值 | Error | - | + +### 返回值 + +`UseHookReturnType`包含响应数据和请求相关的状态、操作函数和事件绑定函数,它们会根据`statesHook`自动推断出对应 UI 框架的响应式数据类型,在 vue3 中为`Ref`类型,在 react 中为普通值,在 svelte 中为`Writable`类型。 + +#### 响应式数据 + +| 名称 | 描述 | 类型 | 版本 | +| ----------- | ------------ | ------------------ | ---- | +| loading | 请求加载状态 | boolean | - | +| data | 响应数据 | any | - | +| error | 请求错误信息 | Error \| undefined | - | +| downloading | 下载进度信息 | Object | - | +| uploading | 上传进度信息 | Object | - | + +#### 操作函数 + +| 名称 | 描述 | 函数参数 | 返回值 | 版本 | +| ------ | --------------------------------------------------- | ------------------------------------------------------- | ------- | ---- | +| send | 发送请求函数 | ...args: any[] | - | - | +| abort | 中断函数 | - | Promise | - | +| update | 更新当前 use hook 前端状态的函数,在 react 中较有用 | newFrontStates: [FrontRequestState](#frontrequeststate) | - | + +#### 事件 + +| 名称 | 描述 | 回调参数 | 版本 | +| ---------- | ---------------- | ------------------------------------------------ | ---- | +| onSuccess | 请求成功事件绑定 | event: [AlovaSuccessEvent](#alovasuccessevent) | - | +| onError | 请求错误事件绑定 | event: [AlovaErrorEvent](#alovaerrorevent) | - | +| onComplete | 请求完成事件绑定 | event: [AlovaCompleteEvent](#alovacompleteevent) | - | + +## useWatcher + +监听状态,并在状态变化后发起请求,在一些需要随数据变化而重新请求的场景下,如分页、数据筛选、模糊搜索使用。 + +> 前往[状态变化请求](/next/tutorial/client/strategy/use-watcher)查看详情。 + +### 类型 + +```typescript +function useWatcher( + handler: Method | (...args: any[]) => Method, + watchingStates: State[], + config?: WatcherHookConfig +): UseHookReturnType; +``` + +### 参数 + +1. `handler`: 可传入 method 实例和函数两种形式,当指定为函数时的可接收`send`传入的参数,并要求返回一个 method 实例。 +2. `config`: hook 的配置参数。 + +| 名称 | 描述 | 类型 | 默认值 | 版本 | +| ------------- | -------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | ---------- | ---- | +| immediate | 是否立即发起请求 | boolean | true | - | +| initialData | 初始的 data 值,在首次响应前 data 值为初始值,未设置时为`undefined` | any | - | - | +| force | 是否强制请求,可设置为函数动态返回 boolean 值 | boolean \| (...args: any[]) => boolean \| false | +| managedStates | 额外的监管状态,可通过 updateState 更新 | Record\ | - | +| debounce | 请求防抖时间(毫秒),传入数组时可按 watchingStates 的顺序单独设置防抖时间 | number \| number[] | - | +| middleware | 中间件函数,[了解 alova 中间件](/next/tutorial/client/in-depth/middleware) | (context: [AlovaFrontMiddlewareContext](#alovafrontmiddlewarecontext), next: [AlovaGuardNext](#alovaguardnext)) => Promise\ | - | - | +| sendable | 监听的状态改变时是否发送请求 | (methodInstance: AlovaEvent) => boolean | () => true | - | +| abortLast | 是否中断上一次的未响应请求 | boolean | true | - | + +### 返回值 + +`UseHookReturnType`包含响应数据和请求相关的状态、操作函数和事件绑定函数,它们会根据 statesHook 自动推断出对应 UI 框架的响应式数据类型,在 vue3 中为 Ref 类型,在 react 中为普通值,在 svelte 中为 Writable 类型。 + +#### 响应式数据 + +| 名称 | 描述 | 类型 | 版本 | +| ----------- | ------------ | ------------------ | ---- | +| loading | 请求加载状态 | boolean | - | +| data | 响应数据 | any | - | +| error | 请求错误信息 | Error \| undefined | - | +| downloading | 下载进度信息 | Object | - | +| uploading | 上传进度信息 | Object | - | + +#### 操作函数 + +| 名称 | 描述 | 函数参数 | 返回值 | 版本 | +| ------ | --------------------------------------------------- | ------------------------------------------------------- | ------- | ---- | +| send | 发送请求函数 | ...args: any[] | Promise | - | +| abort | 中断函数 | - | - | - | +| update | 更新当前 use hook 前端状态的函数,在 react 中较有用 | newFrontStates: [FrontRequestState](#frontrequeststate) | - | + +#### 事件 + +| 名称 | 描述 | 回调参数 | 版本 | +| ---------- | ---------------- | ------------------------------------------------ | ---- | +| onSuccess | 请求成功事件绑定 | event: [AlovaSuccessEvent](#alovasuccessevent) | - | +| onError | 请求错误事件绑定 | event: [AlovaErrorEvent](#alovaerrorevent) | - | +| onComplete | 请求完成事件绑定 | event: [AlovaCompleteEvent](#alovacompleteevent) | - | + +## useFetcher + +通过`useFetcher`用来拉取数据,在预加载数据和跨模块更新状态时很有用。 + +> 前往[数据拉取](/next/tutorial/client/strategy/use-fetcher)查看详情。 + +### 类型 + +```typescript +function useFetcher(config?: FetcherHookConfig): UseFetchHookReturnType; +``` + +### 参数 + +1. `config`: hook 的配置参数。 + +| 名称 | 描述 | 类型 | 默认值 | 版本 | +| ---------- | -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ | ---- | +| force | 是否强制请求,可设置为函数动态返回 boolean 值 | boolean | (...args: any[]) => boolean \| false | - | +| middleware | 中间件函数,[了解 alova 中间件](/next/tutorial/client/in-depth/middleware) | (context: [AlovaFetcherMiddlewareContext](#alovafetchermiddlewarecontext), next: [AlovaGuardNext](#alovaguardnext)) => Promise\ | - | - | + +#### AlovaFetcherMiddlewareContext + +| 名称 | 描述 | 类型 | 版本 | +| ---------------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- | +| method | 当前请求的 method 对象 | Method | - | +| cachedResponse | 命中的缓存数据 | any | - | +| config | 当前的 use hook 配置 | Record\ | - | +| fetchArgs | 响应处理回调的参数,该参数由 useFetcher 的 fetch 传入 | any[] | - | +| fetchStates | use hook 预加载状态集合,如 fetching、error 等 | [FetchRequestState](#fetchrequeststate) | - | +| fetch | 数据预加载函数 | (method: Method, ...args: any[]) => void | Promise | +| abort | 中断函数 | () => void | - | +| decorateSuccess | 装饰成功回调函数 | (decorator: (
handler: (event: [AlovaSuccessEvent](#alovasuccessevent)) => void,
event: [AlovaSuccessEvent](#alovasuccessevent),
index: number,
length: number
) => void) => void | - | +| decorateError | 装饰失败回调函数 | (decorator: (
handler: (event: [AlovaErrorEvent](#alovaerrorevent)) => void,
event: [AlovaErrorEvent](#alovaerrorevent),
index: number,
length: number
) => void) => void | - | +| decorateComplete | 装饰完成回调函数 | (decorator: (
handler: (event: [AlovaCompleteEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovacompleteevent),
index: number,
length: number
) => void) => void | - | +| update | 更新当前 use hook 预加载状态的函数,在 react 中较有用 | (newFrontStates: [FetchRequestState](#fetchrequeststate)) => void; | - | +| controlFetching | 调用后将自定义控制 fetching 的状态,内部不再触发 fetching 状态的变更,传入 control 为 false 时将取消控制 | (control?: boolean) => void | - | + +#### FetchRequestState + +以下属性值将会根据`statesHook`自动推断出对应 UI 框架的响应式数据类型,在 vue3 中为`Ref`类型,在 react 中为普通值,在 svelte 中为`Writable`类型 + +| 名称 | 描述 | 类型 | 版本 | +| ----------- | -------------- | ------------------ | ---- | +| fetching | 预加载请求状态 | boolean | - | +| error | 请求错误信息 | Error \| undefined | - | +| downloading | 下载进度信息 | Object | - | +| uploading | 上传进度信息 | Object | - | + +### 返回值 + +`UseFetchHookReturnType`包含请求相关的状态、操作函数和事件绑定函数,它们会根据 statesHook 自动推断出对应 UI 框架的响应式数据类型,在 vue3 中为 Ref 类型,在 react 中为普通值,在 svelte 中为 Writable 类型。 + +#### 响应式数据 + +| 名称 | 描述 | 类型 | 版本 | +| ----------- | -------------- | ------------------ | ---- | +| fetching | 预加载请求状态 | boolean | - | +| error | 请求错误信息 | Error \| undefined | - | +| downloading | 下载进度信息 | Object | - | +| uploading | 上传进度信息 | Object | - | + +#### 操作函数 + +| 名称 | 描述 | 函数参数 | 返回值 | 版本 | +| ------ | --------------------------------------------------- | ------------------------------------------------------- | ------- | ---- | +| fetch | 数据预加载函数 | 1. method: 预加载的 Method 实例
2. ...args: any[] | Promise | - | +| abort | 中断函数 | - | - | - | +| update | 更新当前 use hook 前端状态的函数,在 react 中较有用 | newFrontStates: [FrontRequestState](#frontrequeststate) | - | + +#### 事件 + +| 名称 | 描述 | 回调参数 | 版本 | +| ---------- | ---------------- | ---------------------------------------------------------------------------------------------------------------- | ---- | +| onSuccess | 请求成功事件绑定 | event: [AlovaSuccessEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovasuccessevent) | - | +| onError | 请求错误事件绑定 | event: [AlovaErrorEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovaerrorevent) | - | +| onComplete | 请求完成事件绑定 | event: [AlovaCompleteEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovacompleteevent) | - | diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/api/04-cache.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/api/04-cache.md index dbab8559b..002c2948c 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/api/04-cache.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/api/04-cache.md @@ -1,13 +1,12 @@ --- title: 缓存操作 -sidebar_position: 40 --- ## invalidateCache() 主动失效缓存。 -> 前往[手动失效缓存](/tutorial/cache/manually-invalidate)查看详情。 +> 前往[手动失效缓存](/next/tutorial/cache/manually-invalidate)查看详情。 - **类型** @@ -25,7 +24,7 @@ function invalidateCache(matcher?: Method | Method[] | MethodFilter): void; - **参数** -1. `matcher`:缓存失效的匹配器,值为 method 实例或数组,也可以设置为[method 匹配器](/tutorial/advanced/method-matcher)。 +1. `matcher`:缓存失效的匹配器,值为 method 实例或数组,也可以设置为[method 匹配器](/next/tutorial/client/in-depth/method-matcher)。 - **返回** @@ -48,7 +47,7 @@ invalidateCache({ 设置响应缓存。 -> 前往[缓存更新与查找](/tutorial/cache/set-and-query)查看详情。 +> 前往[缓存更新与查找](/next/tutorial/cache/set-and-query)查看详情。 - **类型** @@ -69,7 +68,7 @@ function setCache( - **参数** -1. `matcher`:值为 method 实例、method 的 name 字符串、method 的 name 正则表达式,也可以设置为[method 匹配器](/tutorial/advanced/method-matcher),将会为所有符合条件的 method 实例设置缓存数据。 +1. `matcher`:值为 method 实例、method 的 name 字符串、method 的 name 正则表达式,也可以设置为[method 匹配器](/next/tutorial/client/in-depth/method-matcher),将会为所有符合条件的 method 实例设置缓存数据。 2. `dataOrUpdater`:缓存数据或更新函数,如果为函数,则需要返回新的缓存数据,如果返回`undefined`或不返回则取消更新。 - **返回** @@ -96,7 +95,7 @@ setCache( 查询缓存。 -> 前往[缓存更新与查找](/tutorial/cache/set-and-query)查看详情。 +> 前往[缓存更新与查找](/next/tutorial/cache/set-and-query)查看详情。 - **类型** @@ -114,7 +113,7 @@ function queryCache(matcher?: Method | MethodFilter): R | undefined; - **参数** -1. `matcher`:值为 method 实例、method 的 name 字符串、method 的 name 正则表达式,也可以设置为[method 匹配器](/tutorial/advanced/method-matcher),将会为符合条件的第一个 method 实例查询缓存数据。 +1. `matcher`:值为 method 实例、method 的 name 字符串、method 的 name 正则表达式,也可以设置为[method 匹配器](/next/tutorial/client/in-depth/method-matcher),将会为符合条件的第一个 method 实例查询缓存数据。 - **返回** diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/api/05-states.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/api/05-states.md index bea840a6d..40fdd2036 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/api/05-states.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/api/05-states.md @@ -1,6 +1,5 @@ --- title: 响应状态操作 -sidebar_position: 50 --- ## updateState @@ -14,11 +13,11 @@ sidebar_position: 50 这个问题常常出现在跨页面更新状态时,因为当页面跳转时我们容易忽略的是,默认情况下上一个页面已经被销毁了,因此,如果你希望跨页面更新状态,这边有两个建议: 1. 将页面组件持久化,以保证被更新的状态还可以被查找到; -2. 使用 [setCache](/tutorial/cache/set-and-query) 替代`updateState`,其原理是,当上一个页面的请求存在缓存时,更新它的缓存以保证再次创建页面时,所触发的请求可以命中更新后的缓存,达到同样的效果。 +2. 使用 [setCache](/next/tutorial/cache/set-and-query) 替代`updateState`,其原理是,当上一个页面的请求存在缓存时,更新它的缓存以保证再次创建页面时,所触发的请求可以命中更新后的缓存,达到同样的效果。 -> 前往[跨页面/模块更新响应状态](/tutorial/advanced/update-across-components)查看详情。 +> 前往[跨页面/模块更新响应状态](/next/tutorial/client/in-depth/update-across-components)查看详情。 -> 使用 updateState 管理额外状态请参考[额外状态管理](/tutorial/advanced/manage-extra-states)。 +> 使用 updateState 管理额外状态请参考[额外状态管理](/next/tutorial/client/in-depth/manage-extra-states)。 - **类型** @@ -40,7 +39,7 @@ function updateState( - **参数** -- `matcher`:值为 method 实例、method 的 name 字符串、method 的 name 正则表达式,也可以设置为[method 匹配器](/tutorial/advanced/method-matcher),如果匹配到符合条件的 method,将会调用`handleUpdate`。 +- `matcher`:值为 method 实例、method 的 name 字符串、method 的 name 正则表达式,也可以设置为[method 匹配器](/next/tutorial/client/in-depth/method-matcher),如果匹配到符合条件的 method,将会调用`handleUpdate`。 - `handleUpdate`:更新函数或更新函数集合,如果是函数集合,将会调用集合上对应的更新函数,并将返回值作为更新结果。 - `options`:可选项。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/api/06-global-config.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/api/06-global-config.md index 45be3e559..f923a2a80 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/api/06-global-config.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/api/06-global-config.md @@ -1,36 +1,35 @@ ---- -title: 全局配置 -sidebar_position: 60 ---- - -## globalConfig() - -全局配置。 - -- **类型** - -```ts -function globalConfig(config: AlovaGlobalConfig): void; -``` - -- **参数** - -1. config: 配置 - -| 参数名 | 类型 | 说明 | -| -------------- | ------ | -------------------------------------------------------------------------------- | -| limitSnapshots | number | method 快照数量限制,设置为 0 表示关闭保存快照,关闭后 method 快照匹配器将不可用 | - -- **返回** - -无 - -- **示例** - -```ts -import { globalConfig } from 'alova'; - -globalConfig({ - limitSnapshots: 10 -}); -``` +--- +title: 全局配置 +--- + +## globalConfig() + +全局配置。 + +- **类型** + +```ts +function globalConfig(config: AlovaGlobalConfig): void; +``` + +- **参数** + +1. config: 配置 + +| 参数名 | 类型 | 说明 | +| -------------- | ------ | -------------------------------------------------------------------------------- | +| limitSnapshots | number | method 快照数量限制,设置为 0 表示关闭保存快照,关闭后 method 快照匹配器将不可用 | + +- **返回** + +无 + +- **示例** + +```ts +import { globalConfig } from 'alova'; + +globalConfig({ + limitSnapshots: 10 +}); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/contributing/01-overview.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/contributing/01-overview.md index 7c68f3b19..9ca89693e 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/contributing/01-overview.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/contributing/01-overview.md @@ -1,176 +1,175 @@ ---- -title: 贡献指南 -sidebar_position: 10 ---- - -# alova 贡献指南 - -你好,很高兴在这遇到你,这是一份详细的 alova 贡献指南,它包含对 alova 各个方面的贡献提供了详细的指导,请继续往下看。 - -## 前言 - -在过去的一段时间里,我们在 Github issues 和 Github Disscussion 中收到了来自世界各地的开发者积极参与的信息,深感荣幸,这意味着 alova 正在被越来越多的开发者喜爱。即便如此,alova 也还属于新秀,它依然还有很长一段路需要走。 - -**我们期望将 alova 打造成每位愿意参与的人的共同项目,我们以开放包容的态度鼓励每个人成为 alova 社区的贡献者。而且,我们认为贡献 alova 不局限于代码贡献,而是参与任何有利于 alova 发展的活动都属于贡献 alova,** 现在参与贡献可以为你赢得更多的有效贡献机会,它可以让你为全世界的开发者提供你的价值,即使你是一位初级开发者,只要想法符合 [alova 的使命和设计理念](#alova-使命和设计理念),也请大方地参与进来! - -> 这里有一份[社区行为公约](./code-of-conduct),请参阅。 - -## 贡献目录 - -这里提供 13 个可贡献之处供你选择,但不局限于这些,你可以选择自己希望参与的部分后,链接到对应位置详细阅读: - -- [在项目中使用 alova](#在项目中使用-alova) -- [为 alova 点星](#为-alova-点星) -- [报告 bug](#报告-bug) -- [提出新特性想法](#提出新特性想法) -- [Pull Request](#pull-request) -- [基于 alova 编写适配器和策略库](#基于-alova-编写适配器和策略库) -- [参与社区交流/PR review](#参与社区交流pr-review) -- [发布和传播有利于 alova 的信息](#发布和传播有利于-alova-的信息) -- [分享使用经验](#分享使用经验) -- [项目合作](#项目合作) -- [项目捐赠](#项目捐赠) -- [更正或编写文档](#更正或编写文档) -- [翻译文档](#翻译文档) - -## alova 使命和设计理念 - -### alova 使命 - -alova 的使命为它指出了明确的发展方向,它清晰地定义了 alova 什么应该做。 - -alova 是一个轻量级的请求策略库,**它的使命就是让开发者在编写少量代码的同时,也能实现更高效地 Client-Server 数据交互**。 - -对于开发者来说,alova 为他们提供了简单的 api 和开箱即用的高级请求功能,以及各种简单的、高性能的请求策略模块,对于应用的用户来说,它们可以享受到 alova 的高性能数据交互带来的流畅体验,因此,alova 具备了以下特性: - -1. 与 axios 相似的 api 设计,让使用者学习成本更低; -2. 深度绑定 UI 框架,大大提高开发者的使用收益; -3. 开箱即用的高级功能,避免重复封装,例如请求共享、请求缓存等,减少开发者重复封装; -4. 平台无关的编码方式,可在不同平台完美迁移; -5. 高扩展性设计,可封装高复用、高性能的业务相关的请求策略; -6. 高聚合低耦合的 method 设计,提高 api 代码维护性; - -### alova 设计理念 - -设计理念指出了它应该如何设计,以下为 alova 的核心设计理念。 - -1. Method 代理设计,高聚合、平台无关的设计,贯穿请求始终,你在任意请求函数中都应该可以访问到它,从另一个角度说,与请求相关的信息也应该被放在 method 实例中; -2. 轻量级,在编码中尽量保持源码简洁,例如避免重复代码、合并变量声明、原型链函数封装、无相似 api、tree shaking,但长变量名是被允许的,因为在编译时它将会被单个字母替代; -3. 高扩展性设计,其一,alova 的设计中大量使用了适配器模式和钩子函数,例如适配器有`requestAdapter`、`storageAdapter`等,钩子函数有`beforeRequest`、`reseponded`、`transformData`、`localCache`等,而且大多存在默认行为,这样设计的目的是为了在保留高扩展性的同时,使用也足够简单;其二,全局请求参数可覆盖,例如`timeout`、`shareRequest`等,对于特别的请求可单独设置这些参数。 -4. api 设计具有普适性,其一,它表示此 api 的功能具有较高的抽象层级,而不是针对某一个具体业务而提出的;其二,api 设计具有可扩展性,以适应 api 的迭代 - -> api 普适性设计仅适用于 alova 库,如果你正在构思一个请求策略,那么可以根据具体业务来设计。 - -## 选择你感兴趣的贡献点 - -### 在项目中使用 alova - -我们认为,你们在项目中使用 alova 也是属于 alova 的贡献者,这也是在告诉人们,alova 是值得信任的开源项目,请在[这个 issue](https://github.com/alovajs/alova/issues/165)中提交你的项目,这可能会获得在 alova 官网展示你项目的机会。 - -### 为 alova 点星 - -虽然这可能会被认为微不足道,但这代表了你对 alova 的认可,对 alova 来说每一个 star 也是至关重要,请在[alova 的 Github 仓库](https://github.com/alovajs/alova)的右上角为我们点亮星星,这对我们很重要。 - -### 报告 bug - -请移步到[Github new issue](https://github.com/alovajs/alova/issues/new/choose)中选择对应的模板提交,详细说明将会在提交 issue 中展示。 - -**请注意:** 如果你想问 alova 相关的问题,请到[Github Disscussion](https://github.com/alovajs/alova/discussions)中创建,在 issue 中提问将会被立即关闭。 - -### 提出新特性想法 - -为了让 alova 可以实现它的价值和目标,在提交一个新特性想法前,请仔细阅读[alova 使命和设计理念](#alova-使命和设计理念),并保证你的新想法符合 alova 的使命和设计理念。 - -然后,请到[🚀 新特性提案](https://github.com/alovajs/alova/issues/new?assignees=&labels=feature-request&projects=&template=FEATURE_REQUEST_zh-CN.yml)中提交,详细说明将会在提交 issue 时展示。 - -### Pull Request - -你可以通过 pull request 贡献以下 3 个方面的代码。如果你是一位有意向参与的新伙伴,在[Github 贡献列表](https://github.com/alovajs/alova/contribute)中列出了所有的`good first issue`的 issues,它用来告诉有兴趣参与贡献的新伙伴,这个是一个好的开始。 - -#### bug 修改 - -在 Github issues 中被标记为[`bug:confirmed`](https://github.com/alovajs/alova/labels/bug%3Aconfirmed)的 issues,都是已被确认的 bug,你可以自由选择。 - -如果你自己遇到了 bug,也请先[报告 bug](#报告-bug)确保 bug 被确认,以避免造成无效的 pull request。 - -#### 新特性开发 - -在 Github issues 中被标记为[`feature-request:confirmed`](https://github.com/alovajs/alova/labels/feature-request%3Aconfirmed)的 issues,都是已被确认的新特性,你可以自由选择。 - -如果你有一个添加新特性的想法,也请先[提交一个新特性想法的 issue](#提出新特性想法)确保想法被确认,以避免造成无效的 pull request。 - -#### 项目配置 - -如果你很擅长项目配置,并发现了 alova 项目的不足之处,例如不够完整的配置、配置版本太老旧、自动化不足(包含项目开发自动化和 Github 仓库管理的自动化),你也可以按[新特性开发](#新特性开发)的流程进行贡献。 - -:::warning 重要 - -1. 在开发前请仔细阅读[开发指南](./developing-guidelines),它可以一步步地指导你如何贡献代码。 -2. 在你确定了一个需要解决的 issue 时,请确保它没有被其他人的 pull request 标记,它表示已被人先占有。 - -::: - -### 基于 alova 编写适配器和策略库 - -alova 提供了高扩展特性,你可以基于它编写自己的 js 库。 - -#### 自定义适配器 - -自定义各类适配器以满足不同环境下的运行要求,以下几个方向可供参考: - -1. 自定义 statesHook,满足在不同 UI 框架下执行,例如`solid/qwik`,目前内置支持`react/vue/svelte`,请阅读[自定义 statesHook](/tutorial/custom/custom-stateshook); -2. 自定义请求适配器,让 alova 可以与更多请求方案协作,例如`GraphQL/SSE`等; -3. 自定义存储适配器,满足不同环境的存储,例如`react-native`; -4. 以上任意的组合,例如官方的[uniapp 适配器](https://github.com/alovajs/adapter-uniapp),其中包含了请求适配器、存储适配器。 - -#### 自定义请求策略 - -请求策略可以帮助开发者更高效地编写出高性能功能,虽然官方的 [alova/scene](/tutorial/strategy) 提供了一些常用的请求策略,但还不足以满足广大开发者各种请求相关的业务场景,基于 alova 自定义你自己的可复用请求策略是一个不错的选择,也可以将它们发布到 npm 上给大家使用。 - -:::tip 提交你的项目 - -如果你编写了基于 alova 的 js 库,请在[这个 issue](https://github.com/alovajs/alova/issues/165)中提交你的项目,这可以让你的项目获得在 alova 官网展示的机会。 - -::: - -### 参与社区交流/PR review - -如果你对技术交流感兴趣,那么参与更多的社区交流可能更适合你,你可以在 Github issues 中参与 bug 和新特性的讨论,也可以在[Github Disscussion](https://github.com/alovajs/alova/discussions)中、[Discord](https://discord.gg/S47QGJgkVb)或[QQ 频道](https://pd.qq.com/s/1cdjx0nnw)中为别人解答问题,这可以让你与世界各地的人交流,是一件很有趣的事情。 - -同时,你也可以在[pull request](https://github.com/alovajs/alova/pulls)中参与 PR review,这也是一种交流的主题。 - -### 发布和传播 alova 的信息 - -你可以在任何社交平台、短视频平台,或技术分享平台发布或转发传播任何有利于 alova 发展的信息,这有利于提高 alova 的影响力。我们将会筛选出相关的文章或视频在 alova 官网展示它们。这里有一些优质的文章: - -- [是时候该换掉你的 axios 了](https://juejin.cn/post/7213923957824979000) -- [(深度)开源框架/库的伟大与罪恶](https://juejin.cn/post/7215608036394729532) -- [Alova.js 筆記-試用相較 axios 更輕量、更高集成的請求庫](https://uu9924079.medium.com/alova-js-%E7%AD%86%E8%A8%98-%E8%A9%A6%E7%94%A8%E7%9B%B8%E8%BC%83-axios-%E6%9B%B4%E8%BC%95%E9%87%8F-%E6%9B%B4%E9%AB%98%E9%9B%86%E6%88%90%E7%9A%84%E8%AB%8B%E6%B1%82%E5%BA%AB-546ec5424df9) - -### 分享使用经验 - -如果你有值得分享的 alova 使用经验,或者较好的实践案例,你可以在[Github Disscussion 的 Practices](https://github.com/alovajs/alova/discussions/categories/practices)中分享,较好的分享也将会在官方文档中展示。 - -### 项目合作 - -我们欢迎与任何组织或个人进行项目合作,这可以帮助我们扩大 alova 的影响力,加速项目的发展。如果您有任何合作建议或意向,请发送邮件到**hujou555@gmail.com**与我们联系。 - -### 项目捐赠 - -您可以在以下三个渠道为项目捐赠,捐赠特权说明请进入捐赠页面查看。 - -1. [Github sponsors](https://github.com/alovajs) -2. [OpenCollective](https://opencollective.com/alova) -3. [afdian](https://afdian.net/a/huzhen555) - -### 更正或编写文档 - -如果你需要添加新的文档内容,或发现 alova 的文档有错误,例如错误的示例、错误的用词、描述不正确、或未提及的内容,你可以[新建文档仓库 issue](https://github.com/alovajs/alovajs.github.io/issues/new),或[新建文档仓库 pull request](https://github.com/alovajs/alovajs.github.io/fork)直接修改错误,这应该是更好的选择,我们欢迎任何对文档的改进建议或贡献。 - -### 翻译文档 - -如果你擅长不同的语言,欢迎你为 alova 文档进行翻译,这将有助于扩展 alova 的使用范围和受众群体。 - -## 成为核心团队成员 - -具体查看[这边的内容](./become-core-member) +--- +title: 贡献指南 +--- + +# alova 贡献指南 + +你好,很高兴在这遇到你,这是一份详细的 alova 贡献指南,它包含对 alova 各个方面的贡献提供了详细的指导,请继续往下看。 + +## 前言 + +在过去的一段时间里,我们在 Github issues 和 Github Disscussion 中收到了来自世界各地的开发者积极参与的信息,深感荣幸,这意味着 alova 正在被越来越多的开发者喜爱。即便如此,alova 也还属于新秀,它依然还有很长一段路需要走。 + +**我们期望将 alova 打造成每位愿意参与的人的共同项目,我们以开放包容的态度鼓励每个人成为 alova 社区的贡献者。而且,我们认为贡献 alova 不局限于代码贡献,而是参与任何有利于 alova 发展的活动都属于贡献 alova,** 现在参与贡献可以为你赢得更多的有效贡献机会,它可以让你为全世界的开发者提供你的价值,即使你是一位初级开发者,只要想法符合 [alova 的使命和设计理念](#alova-使命和设计理念),也请大方地参与进来! + +> 这里有一份[社区行为公约](./code-of-conduct),请参阅。 + +## 贡献目录 + +这里提供 13 个可贡献之处供你选择,但不局限于这些,你可以选择自己希望参与的部分后,链接到对应位置详细阅读: + +- [在项目中使用 alova](#在项目中使用-alova) +- [为 alova 点星](#为-alova-点星) +- [报告 bug](#报告-bug) +- [提出新特性想法](#提出新特性想法) +- [Pull Request](#pull-request) +- [基于 alova 编写适配器和策略库](#基于-alova-编写适配器和策略库) +- [参与社区交流/PR review](#参与社区交流pr-review) +- [发布和传播有利于 alova 的信息](#发布和传播有利于-alova-的信息) +- [分享使用经验](#分享使用经验) +- [项目合作](#项目合作) +- [项目捐赠](#项目捐赠) +- [更正或编写文档](#更正或编写文档) +- [翻译文档](#翻译文档) + +## alova 使命和设计理念 + +### alova 使命 + +alova 的使命为它指出了明确的发展方向,它清晰地定义了 alova 什么应该做。 + +alova 是一个轻量级的请求策略库,**它的使命就是让开发者在编写少量代码的同时,也能实现更高效地 Client-Server 数据交互**。 + +对于开发者来说,alova 为他们提供了简单的 api 和开箱即用的高级请求功能,以及各种简单的、高性能的请求策略模块,对于应用的用户来说,它们可以享受到 alova 的高性能数据交互带来的流畅体验,因此,alova 具备了以下特性: + +1. 与 axios 相似的 api 设计,让使用者学习成本更低; +2. 深度绑定 UI 框架,大大提高开发者的使用收益; +3. 开箱即用的高级功能,避免重复封装,例如请求共享、请求缓存等,减少开发者重复封装; +4. 平台无关的编码方式,可在不同平台完美迁移; +5. 高扩展性设计,可封装高复用、高性能的业务相关的请求策略; +6. 高聚合低耦合的 method 设计,提高 api 代码维护性; + +### alova 设计理念 + +设计理念指出了它应该如何设计,以下为 alova 的核心设计理念。 + +1. Method 代理设计,高聚合、平台无关的设计,贯穿请求始终,你在任意请求函数中都应该可以访问到它,从另一个角度说,与请求相关的信息也应该被放在 method 实例中; +2. 轻量级,在编码中尽量保持源码简洁,例如避免重复代码、合并变量声明、原型链函数封装、无相似 api、tree shaking,但长变量名是被允许的,因为在编译时它将会被单个字母替代; +3. 高扩展性设计,其一,alova 的设计中大量使用了适配器模式和钩子函数,例如适配器有`requestAdapter`、`storageAdapter`等,钩子函数有`beforeRequest`、`reseponded`、`transformData`、`localCache`等,而且大多存在默认行为,这样设计的目的是为了在保留高扩展性的同时,使用也足够简单;其二,全局请求参数可覆盖,例如`timeout`、`shareRequest`等,对于特别的请求可单独设置这些参数。 +4. api 设计具有普适性,其一,它表示此 api 的功能具有较高的抽象层级,而不是针对某一个具体业务而提出的;其二,api 设计具有可扩展性,以适应 api 的迭代 + +> api 普适性设计仅适用于 alova 库,如果你正在构思一个请求策略,那么可以根据具体业务来设计。 + +## 选择你感兴趣的贡献点 + +### 在项目中使用 alova + +我们认为,你们在项目中使用 alova 也是属于 alova 的贡献者,这也是在告诉人们,alova 是值得信任的开源项目,请在[这个 issue](https://github.com/alovajs/alova/issues/165)中提交你的项目,这可能会获得在 alova 官网展示你项目的机会。 + +### 为 alova 点星 + +虽然这可能会被认为微不足道,但这代表了你对 alova 的认可,对 alova 来说每一个 star 也是至关重要,请在[alova 的 Github 仓库](https://github.com/alovajs/alova)的右上角为我们点亮星星,这对我们很重要。 + +### 报告 bug + +请移步到[Github new issue](https://github.com/alovajs/alova/issues/new/choose)中选择对应的模板提交,详细说明将会在提交 issue 中展示。 + +**请注意:** 如果你想问 alova 相关的问题,请到[Github Disscussion](https://github.com/alovajs/alova/discussions)中创建,在 issue 中提问将会被立即关闭。 + +### 提出新特性想法 + +为了让 alova 可以实现它的价值和目标,在提交一个新特性想法前,请仔细阅读[alova 使命和设计理念](#alova-使命和设计理念),并保证你的新想法符合 alova 的使命和设计理念。 + +然后,请到[🚀 新特性提案](https://github.com/alovajs/alova/issues/new?assignees=&labels=feature-request&projects=&template=FEATURE_REQUEST_zh-CN.yml)中提交,详细说明将会在提交 issue 时展示。 + +### Pull Request + +你可以通过 pull request 贡献以下 3 个方面的代码。如果你是一位有意向参与的新伙伴,在[Github 贡献列表](https://github.com/alovajs/alova/contribute)中列出了所有的`good first issue`的 issues,它用来告诉有兴趣参与贡献的新伙伴,这个是一个好的开始。 + +#### bug 修改 + +在 Github issues 中被标记为[`bug:confirmed`](https://github.com/alovajs/alova/labels/bug%3Aconfirmed)的 issues,都是已被确认的 bug,你可以自由选择。 + +如果你自己遇到了 bug,也请先[报告 bug](#报告-bug)确保 bug 被确认,以避免造成无效的 pull request。 + +#### 新特性开发 + +在 Github issues 中被标记为[`feature-request:confirmed`](https://github.com/alovajs/alova/labels/feature-request%3Aconfirmed)的 issues,都是已被确认的新特性,你可以自由选择。 + +如果你有一个添加新特性的想法,也请先[提交一个新特性想法的 issue](#提出新特性想法)确保想法被确认,以避免造成无效的 pull request。 + +#### 项目配置 + +如果你很擅长项目配置,并发现了 alova 项目的不足之处,例如不够完整的配置、配置版本太老旧、自动化不足(包含项目开发自动化和 Github 仓库管理的自动化),你也可以按[新特性开发](#新特性开发)的流程进行贡献。 + +:::warning 重要 + +1. 在开发前请仔细阅读[开发指南](./developing-guidelines),它可以一步步地指导你如何贡献代码。 +2. 在你确定了一个需要解决的 issue 时,请确保它没有被其他人的 pull request 标记,它表示已被人先占有。 + +::: + +### 基于 alova 编写适配器和策略库 + +alova 提供了高扩展特性,你可以基于它编写自己的 js 库。 + +#### 自定义适配器 + +自定义各类适配器以满足不同环境下的运行要求,以下几个方向可供参考: + +1. 自定义 statesHook,满足在不同 UI 框架下执行,例如`solid/qwik`,目前内置支持`react/vue/svelte`,请阅读[自定义 statesHook](/next/tutorial/advanced/custom/stateshook); +2. 自定义请求适配器,让 alova 可以与更多请求方案协作,例如`GraphQL/SSE`等; +3. 自定义存储适配器,满足不同环境的存储,例如`react-native`; +4. 以上任意的组合,例如官方的[uniapp 适配器](https://github.com/alovajs/adapter-uniapp),其中包含了请求适配器、存储适配器。 + +#### 自定义请求策略 + +请求策略可以帮助开发者更高效地编写出高性能功能,虽然官方的 [alova/scene](/next/tutorial/client/strategy) 提供了一些常用的请求策略,但还不足以满足广大开发者各种请求相关的业务场景,基于 alova 自定义你自己的可复用请求策略是一个不错的选择,也可以将它们发布到 npm 上给大家使用。 + +:::tip 提交你的项目 + +如果你编写了基于 alova 的 js 库,请在[这个 issue](https://github.com/alovajs/alova/issues/165)中提交你的项目,这可以让你的项目获得在 alova 官网展示的机会。 + +::: + +### 参与社区交流/PR review + +如果你对技术交流感兴趣,那么参与更多的社区交流可能更适合你,你可以在 Github issues 中参与 bug 和新特性的讨论,也可以在[Github Disscussion](https://github.com/alovajs/alova/discussions)中、[Discord](https://discord.gg/S47QGJgkVb)或[QQ 频道](https://pd.qq.com/s/1cdjx0nnw)中为别人解答问题,这可以让你与世界各地的人交流,是一件很有趣的事情。 + +同时,你也可以在[pull request](https://github.com/alovajs/alova/pulls)中参与 PR review,这也是一种交流的主题。 + +### 发布和传播 alova 的信息 + +你可以在任何社交平台、短视频平台,或技术分享平台发布或转发传播任何有利于 alova 发展的信息,这有利于提高 alova 的影响力。我们将会筛选出相关的文章或视频在 alova 官网展示它们。这里有一些优质的文章: + +- [是时候该换掉你的 axios 了](https://juejin.cn/post/7213923957824979000) +- [(深度)开源框架/库的伟大与罪恶](https://juejin.cn/post/7215608036394729532) +- [Alova.js 筆記-試用相較 axios 更輕量、更高集成的請求庫](https://uu9924079.medium.com/alova-js-%E7%AD%86%E8%A8%98-%E8%A9%A6%E7%94%A8%E7%9B%B8%E8%BC%83-axios-%E6%9B%B4%E8%BC%95%E9%87%8F-%E6%9B%B4%E9%AB%98%E9%9B%86%E6%88%90%E7%9A%84%E8%AB%8B%E6%B1%82%E5%BA%AB-546ec5424df9) + +### 分享使用经验 + +如果你有值得分享的 alova 使用经验,或者较好的实践案例,你可以在[Github Disscussion 的 Practices](https://github.com/alovajs/alova/discussions/categories/practices)中分享,较好的分享也将会在官方文档中展示。 + +### 项目合作 + +我们欢迎与任何组织或个人进行项目合作,这可以帮助我们扩大 alova 的影响力,加速项目的发展。如果您有任何合作建议或意向,请发送邮件到**hujou555@gmail.com**与我们联系。 + +### 项目捐赠 + +您可以在以下三个渠道为项目捐赠,捐赠特权说明请进入捐赠页面查看。 + +1. [Github sponsors](https://github.com/alovajs) +2. [OpenCollective](https://opencollective.com/alova) +3. [afdian](https://afdian.net/a/huzhen555) + +### 更正或编写文档 + +如果你需要添加新的文档内容,或发现 alova 的文档有错误,例如错误的示例、错误的用词、描述不正确、或未提及的内容,你可以[新建文档仓库 issue](https://github.com/alovajs/alovajs.github.io/issues/new),或[新建文档仓库 pull request](https://github.com/alovajs/alovajs.github.io/fork)直接修改错误,这应该是更好的选择,我们欢迎任何对文档的改进建议或贡献。 + +### 翻译文档 + +如果你擅长不同的语言,欢迎你为 alova 文档进行翻译,这将有助于扩展 alova 的使用范围和受众群体。 + +## 成为核心团队成员 + +具体查看[这边的内容](./become-core-member) diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/contributing/02-become-core-member.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/contributing/02-become-core-member.md index 798bca5a2..9f9d5a3cc 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/contributing/02-become-core-member.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/contributing/02-become-core-member.md @@ -1,36 +1,35 @@ ---- -title: 成为核心团队成员 -sidebar_position: 20 ---- - -🤝🤝🤝 如果你也认同 alovajs 的理念,那就让我们一同创造下一代请求库吧! - -## 职责 - -1. 负责 vscode 插件项目、请求场景模块和适配器项目,以及鸿蒙版 alovajs 的开发和维护。 -2. 编写和优化相关的开发文档。 -3. 深度参与项目设计和实现,推广维护等整个生命周期。 -4. 参与项目发展的决策和商业化探索。 - -## 收益 - -1. 核心成员头衔,有机会在前端领域提升自己的声誉,从而拓宽职业发展道路。 -2. 更多公开分享的机会,与更多前端人员接触的机会。 -3. 通过深度参与项目的设计、实现和运营,拓宽项目思维和技术经验。 -4. 对项目的决策权。 -5. 盈利分成。 - -## 要求 - -1. 认同 alova 设计理念和发展方向,并且愿意付出努力一同前行。 -2. 对技术的热情和责任心、团队协作。 -3. 技术能力能覆盖现有需求即可。 -4. 有一定的业余时间。 - -## 如何加入 - -有意向者请添加以下微信,并注明 **alova 核心成员申请**。 - -import wechatQrcode from '@site/static/img/wechat_personal.jpg'; - -wechat_qrcode +--- +title: 成为核心团队成员 +--- + +🤝🤝🤝 如果你也认同 alovajs 的理念,那就让我们一同创造下一代请求库吧! + +## 职责 + +1. 负责 vscode 插件项目、请求场景模块和适配器项目,以及鸿蒙版 alovajs 的开发和维护。 +2. 编写和优化相关的开发文档。 +3. 深度参与项目设计和实现,推广维护等整个生命周期。 +4. 参与项目发展的决策和商业化探索。 + +## 收益 + +1. 核心成员头衔,有机会在前端领域提升自己的声誉,从而拓宽职业发展道路。 +2. 更多公开分享的机会,与更多前端人员接触的机会。 +3. 通过深度参与项目的设计、实现和运营,拓宽项目思维和技术经验。 +4. 对项目的决策权。 +5. 盈利分成。 + +## 要求 + +1. 认同 alova 设计理念和发展方向,并且愿意付出努力一同前行。 +2. 对技术的热情和责任心、团队协作。 +3. 技术能力能覆盖现有需求即可。 +4. 有一定的业余时间。 + +## 如何加入 + +有意向者请添加以下微信,并注明 **alova 核心成员申请**。 + +import wechatQrcode from '@site/static/img/wechat_personal.jpg'; + +wechat_qrcode diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/contributing/03-developing-guidelines.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/contributing/03-developing-guidelines.md index c3177a591..074226f62 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/contributing/03-developing-guidelines.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/contributing/03-developing-guidelines.md @@ -1,138 +1,137 @@ ---- -title: 开发指南 -sidebar_position: 30 ---- - -:::info 版本要求 - -Node.js 16+, npm 8+ - -::: - -## 1. fork 仓库 - -[打开 alova 仓库 fork 页](https://github.com/alovajs/alova/fork),点击“Create fork”fork 仓库,并将已 fork 的仓库克隆到本地。 - -## 2. 克隆项目到本地 - -使用`git clone`命令,或`Github Desktop`应用克隆项目。 - -## 3. 新建 pull request - -你可以在编写完代码后[通过 fork 仓库创建 pull request](https://docs.github.com/zh/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork),也可以分为任意多次提交代码,而无需一次提交完整代码。 - -## 4. 在本地编码 - -### 安装依赖 - -使用`npm install`安装依赖。 - -### 安装推荐插件(vscode) - -如果你使用 vscode,将会推荐你安装以下插件: - -- eslint:检查代码质量 -- prettier:格式化代码 -- jest:自动执行单元测试用例,以及执行单个合集或单元测试用例 -- EditorConfig:保证文件格式一致 - -### 项目结构 - -``` -|-.github -| |-ISSUE_TEMPLATE -> github issues模板 -| |-workflows -> github action -|-.husky -> husky配置 -|-.vscode -> vscode配置 -|-config -> rollup打包文件 -|-src -> 源代码 -|-test -> 单元测试 -| |-browser -> 浏览器环境单元测试 -| |-server -> SSR单元测试 -| |-components -> 单元测试组件 -| |-mockServer.ts -> mock接口(msw) -|-typings -> ts类型声明 -|-其他配置文件 - -``` - -### 编码规范 - -#### 代码格式 - -如果你安装了`prettier`插件,在每次保存文件时会自动进行格式化代码,因此你可以不必在意格式的问题。 - -#### 尽量减少代码 - -alova 的特性之一是轻量化,因此在编码时需要尽量减少编码量,这里有几个需要遵循的编码规范: - -1. 避免出现相同的代码块,这可以减少库的代码量,但两行代码可能就不值得封装; -2. 使用一个变量声明符聚合变量的声明,例如: - -```javascript -// ❌ -const a = 1; -const b = 2; - -// ✅ -const a = 1, - b = 2; -``` - -3. 使用常量保存固定值、原型方法,在编译`uglify`阶段减少代码量。在`src/utils/variables.ts`中定义了常用的固定值和原型方法。 - -```javascript -// ❌ -if (a === false) { - // ... -} -arr.forEach(item => { - // ... -}); - -// ✅ -import { falseValue, forEach } from '@/utils/variables'; -if (a === falseValue) { - // ... -} -forEach(arr, item => { - // ... -}); -``` - -## 5. 单元测试指南 - -编写完代码后,添加对应的单元测试用例,尽量包含边缘情况的测试。 - -alova 项目使用 jest 作为单元测试框架,使用 msw 作为模拟服务器。建议使用 TDD 模式。每次修改代码后,运行对应的单元测试并通过它。 - -:::warning 重要 - -当准备提交代码时,请确保通过了全部单元测试,当您处理 pull request 时,可以有多个小提交,GitHub 可以在合并之前自动压缩它们。 - -::: - -1. 添加浏览器相关单元测试用例,请添加到`test/browser`中对应的测试合集,如果没有合适的测试合集可自行创建; -2. 添加 SSR 相关单元测试用例,请添加到`test/server`中对应的测试合集,如果没有合适的测试合集可自行创建; - -### 运行和调试单个测试用例或合集 - -建议使用**jest**插件(上面推荐的插件之一)对单个用例或合集进行测试,你可以在测试用例中右键点击运行指定的用例,选择`Run Test`运行此测试用例,选择`Debug Test`断点调试此测试用例,如图: - -![image](https://github.com/alovajs/alova/assets/29848971/a94ba9db-c100-472f-b870-6bcecb031bea) - -### 运行全部测试用例 - -1. 使用**jest**插件运行,如下图: - -![image](https://github.com/alovajs/alova/assets/29848971/5af3ff15-16b7-4b28-9ae6-d0b5a236b181) - -2. 通过命令行`npm run test:browser`运行浏览器单元测试,通过`npm run test:node`运行 SSR 单元测试,通过`npm run test`同时运行两者。 - -## 6. 提交代码 - -alova 使用了 [semantic-release](https://semantic-release.gitbook.io) 作为自动发布工具,它可以在合并代码到`main`后自动发布新版本包,以及生成`CHANGELOG`,但需要确保提交的消息格式遵循[提交信息约定](https://www.conventionalcommits.org/zh-hans/v1.0.0/),建议你尽量使用`npm run commit`来自动生成符合规范的 git message。 - -## 7.编写文档 - -如果你正在添加新特性,可尝试添加新特性的相关文档说明,详细请阅读[更正或编写文档](/contributing/overview#更正或编写文档),否则请在 pull request 中说明。 +--- +title: 开发指南 +--- + +:::info 版本要求 + +Node.js 16+, npm 8+ + +::: + +## 1. fork 仓库 + +[打开 alova 仓库 fork 页](https://github.com/alovajs/alova/fork),点击“Create fork”fork 仓库,并将已 fork 的仓库克隆到本地。 + +## 2. 克隆项目到本地 + +使用`git clone`命令,或`Github Desktop`应用克隆项目。 + +## 3. 新建 pull request + +你可以在编写完代码后[通过 fork 仓库创建 pull request](https://docs.github.com/zh/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork),也可以分为任意多次提交代码,而无需一次提交完整代码。 + +## 4. 在本地编码 + +### 安装依赖 + +使用`npm install`安装依赖。 + +### 安装推荐插件(vscode) + +如果你使用 vscode,将会推荐你安装以下插件: + +- eslint:检查代码质量 +- prettier:格式化代码 +- jest:自动执行单元测试用例,以及执行单个合集或单元测试用例 +- EditorConfig:保证文件格式一致 + +### 项目结构 + +``` +|-.github +| |-ISSUE_TEMPLATE -> github issues模板 +| |-workflows -> github action +|-.husky -> husky配置 +|-.vscode -> vscode配置 +|-config -> rollup打包文件 +|-src -> 源代码 +|-test -> 单元测试 +| |-browser -> 浏览器环境单元测试 +| |-server -> SSR单元测试 +| |-components -> 单元测试组件 +| |-mockServer.ts -> mock接口(msw) +|-typings -> ts类型声明 +|-其他配置文件 + +``` + +### 编码规范 + +#### 代码格式 + +如果你安装了`prettier`插件,在每次保存文件时会自动进行格式化代码,因此你可以不必在意格式的问题。 + +#### 尽量减少代码 + +alova 的特性之一是轻量化,因此在编码时需要尽量减少编码量,这里有几个需要遵循的编码规范: + +1. 避免出现相同的代码块,这可以减少库的代码量,但两行代码可能就不值得封装; +2. 使用一个变量声明符聚合变量的声明,例如: + +```javascript +// ❌ +const a = 1; +const b = 2; + +// ✅ +const a = 1, + b = 2; +``` + +3. 使用常量保存固定值、原型方法,在编译`uglify`阶段减少代码量。在`src/utils/variables.ts`中定义了常用的固定值和原型方法。 + +```javascript +// ❌ +if (a === false) { + // ... +} +arr.forEach(item => { + // ... +}); + +// ✅ +import { falseValue, forEach } from '@/utils/variables'; +if (a === falseValue) { + // ... +} +forEach(arr, item => { + // ... +}); +``` + +## 5. 单元测试指南 + +编写完代码后,添加对应的单元测试用例,尽量包含边缘情况的测试。 + +alova 项目使用 jest 作为单元测试框架,使用 msw 作为模拟服务器。建议使用 TDD 模式。每次修改代码后,运行对应的单元测试并通过它。 + +:::warning 重要 + +当准备提交代码时,请确保通过了全部单元测试,当您处理 pull request 时,可以有多个小提交,GitHub 可以在合并之前自动压缩它们。 + +::: + +1. 添加浏览器相关单元测试用例,请添加到`test/browser`中对应的测试合集,如果没有合适的测试合集可自行创建; +2. 添加 SSR 相关单元测试用例,请添加到`test/server`中对应的测试合集,如果没有合适的测试合集可自行创建; + +### 运行和调试单个测试用例或合集 + +建议使用**jest**插件(上面推荐的插件之一)对单个用例或合集进行测试,你可以在测试用例中右键点击运行指定的用例,选择`Run Test`运行此测试用例,选择`Debug Test`断点调试此测试用例,如图: + +![image](https://github.com/alovajs/alova/assets/29848971/a94ba9db-c100-472f-b870-6bcecb031bea) + +### 运行全部测试用例 + +1. 使用**jest**插件运行,如下图: + +![image](https://github.com/alovajs/alova/assets/29848971/5af3ff15-16b7-4b28-9ae6-d0b5a236b181) + +2. 通过命令行`npm run test:browser`运行浏览器单元测试,通过`npm run test:node`运行 SSR 单元测试,通过`npm run test`同时运行两者。 + +## 6. 提交代码 + +alova 使用了 [semantic-release](https://semantic-release.gitbook.io) 作为自动发布工具,它可以在合并代码到`main`后自动发布新版本包,以及生成`CHANGELOG`,但需要确保提交的消息格式遵循[提交信息约定](https://www.conventionalcommits.org/zh-hans/v1.0.0/),建议你尽量使用`npm run commit`来自动生成符合规范的 git message。 + +## 7.编写文档 + +如果你正在添加新特性,可尝试添加新特性的相关文档说明,详细请阅读[更正或编写文档](/next/contributing/overview#更正或编写文档),否则请在 pull request 中说明。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/contributing/04-code-of-conduct.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/contributing/04-code-of-conduct.md index 42f683071..593d57076 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/contributing/04-code-of-conduct.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/contributing/04-code-of-conduct.md @@ -1,90 +1,89 @@ ---- -title: 行为准则 -sidebar_position: 40 ---- - -## 我们的承诺 - -身为社区成员、贡献者和领袖,我们承诺使社区参与者不受骚扰,无论其年龄、体型、可见或不可见的缺陷、族裔、性征、性别认同和表达、经验水平、教育程度、社会与经济地位、国籍、相貌、种族、种姓、肤色、宗教信仰、性倾向或性取向如何。 - -我们承诺以有助于建立开放、友善、多样化、包容、健康社区的方式行事和互动。 - -## 我们的准则 - -有助于为我们的社区创造积极环境的行为例子包括但不限于: - -- 表现出对他人的同情和善意 -- 尊重不同的主张、观点和感受 -- 提出和大方接受建设性意见 -- 承担责任并向受我们错误影响的人道歉 -- 注重社区共同诉求,而非个人得失 - -不当行为例子包括: - -- 使用情色化的语言或图像,及性引诱或挑逗 -- 嘲弄、侮辱或诋毁性评论,以及人身或政治攻击 -- 公开或私下的骚扰行为 -- 未经他人明确许可,公布他人的私人信息,如物理或电子邮件地址 -- 其他有理由认定为违反职业操守的不当行为 - -## 责任和权力 - -社区领袖有责任解释和落实我们所认可的行为准则,并妥善公正地对他们认为不当、威胁、冒犯或有害的任何行为采取纠正措施。 - -社区领导有权力和责任删除、编辑或拒绝或拒绝与本行为准则不相符的评论(comment)、提交(commits)、代码、维基(wiki)编辑、议题(issues)或其他贡献,并在适当时机知采取措施的理由。 - -## 适用范围 - -本行为准则适用于所有社区场合,也适用于在公共场所代表社区时的个人。 - -代表社区的情形包括使用官方电子邮件地址、通过官方社交媒体帐户发帖或在线上或线下活动中担任指定代表。 - -## 监督 - -辱骂、骚扰或其他不可接受的行为可通过 [插入联系方式] 向负责监督的社区领袖报告。 -所有投诉都将得到及时和公平的审查和调查。 - -所有社区领袖都有义务尊重任何事件报告者的隐私和安全。 - -## 处理方针 - -社区领袖将遵循下列社区处理方针来明确他们所认定违反本行为准则的行为的处理方式: - -### 1. 纠正 - -**社区影响**:使用不恰当的语言或其他在社区中被认定为不符合职业道德或不受欢迎的行为。 - -**处理意见**:由社区领袖发出非公开的书面警告,明确说明违规行为的性质,并解释举止如何不妥。或将要求公开道歉。 - -### 2. 警告 - -**社区影响**:单个或一系列违规行为。 - -**处理意见**:警告并对连续性行为进行处理。在指定时间内,不得与相关人员互动,包括主动与行为准则执行者互动。这包括避免在社区场所和外部渠道中的互动。违反这些条款可能会导致临时或永久封禁。 - -### 3. 临时封禁 - -**社区影响**: 严重违反社区准则,包括持续的不当行为。 - -**处理意见**: 在指定时间内,暂时禁止与社区进行任何形式的互动或公开交流。在此期间,不得与相关人员进行公开或私下互动,包括主动与行为准则执行者互动。违反这些条款可能会导致永久封禁。 - -### 4. 永久封禁 - -**社区影响**:行为模式表现出违反社区准则,包括持续的不当行为、骚扰个人或攻击或贬低某个类别的个体。 - -**处理意见**:永久禁止在社区内进行任何形式的公开互动。 - -## 参见 - -本行为准则改编自 [Contributor Covenant][homepage] 2.1 版, 参见 [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]。 - -社区处理方针灵感来源于 [Mozilla's code of conduct enforcement ladder][mozilla coc]。 - -有关本行为准则的常见问题的答案,参见 [https://www.contributor-covenant.org/faq][faq]。 -其他语言翻译参见 [https://www.contributor-covenant.org/translations][translations]。 - -[homepage]: https://www.contributor-covenant.org -[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html -[mozilla coc]: https://github.com/mozilla/diversity -[faq]: https://www.contributor-covenant.org/faq -[translations]: https://www.contributor-covenant.org/translations +--- +title: 行为准则 +--- + +## 我们的承诺 + +身为社区成员、贡献者和领袖,我们承诺使社区参与者不受骚扰,无论其年龄、体型、可见或不可见的缺陷、族裔、性征、性别认同和表达、经验水平、教育程度、社会与经济地位、国籍、相貌、种族、种姓、肤色、宗教信仰、性倾向或性取向如何。 + +我们承诺以有助于建立开放、友善、多样化、包容、健康社区的方式行事和互动。 + +## 我们的准则 + +有助于为我们的社区创造积极环境的行为例子包括但不限于: + +- 表现出对他人的同情和善意 +- 尊重不同的主张、观点和感受 +- 提出和大方接受建设性意见 +- 承担责任并向受我们错误影响的人道歉 +- 注重社区共同诉求,而非个人得失 + +不当行为例子包括: + +- 使用情色化的语言或图像,及性引诱或挑逗 +- 嘲弄、侮辱或诋毁性评论,以及人身或政治攻击 +- 公开或私下的骚扰行为 +- 未经他人明确许可,公布他人的私人信息,如物理或电子邮件地址 +- 其他有理由认定为违反职业操守的不当行为 + +## 责任和权力 + +社区领袖有责任解释和落实我们所认可的行为准则,并妥善公正地对他们认为不当、威胁、冒犯或有害的任何行为采取纠正措施。 + +社区领导有权力和责任删除、编辑或拒绝或拒绝与本行为准则不相符的评论(comment)、提交(commits)、代码、维基(wiki)编辑、议题(issues)或其他贡献,并在适当时机知采取措施的理由。 + +## 适用范围 + +本行为准则适用于所有社区场合,也适用于在公共场所代表社区时的个人。 + +代表社区的情形包括使用官方电子邮件地址、通过官方社交媒体帐户发帖或在线上或线下活动中担任指定代表。 + +## 监督 + +辱骂、骚扰或其他不可接受的行为可通过 [插入联系方式] 向负责监督的社区领袖报告。 +所有投诉都将得到及时和公平的审查和调查。 + +所有社区领袖都有义务尊重任何事件报告者的隐私和安全。 + +## 处理方针 + +社区领袖将遵循下列社区处理方针来明确他们所认定违反本行为准则的行为的处理方式: + +### 1. 纠正 + +**社区影响**:使用不恰当的语言或其他在社区中被认定为不符合职业道德或不受欢迎的行为。 + +**处理意见**:由社区领袖发出非公开的书面警告,明确说明违规行为的性质,并解释举止如何不妥。或将要求公开道歉。 + +### 2. 警告 + +**社区影响**:单个或一系列违规行为。 + +**处理意见**:警告并对连续性行为进行处理。在指定时间内,不得与相关人员互动,包括主动与行为准则执行者互动。这包括避免在社区场所和外部渠道中的互动。违反这些条款可能会导致临时或永久封禁。 + +### 3. 临时封禁 + +**社区影响**: 严重违反社区准则,包括持续的不当行为。 + +**处理意见**: 在指定时间内,暂时禁止与社区进行任何形式的互动或公开交流。在此期间,不得与相关人员进行公开或私下互动,包括主动与行为准则执行者互动。违反这些条款可能会导致永久封禁。 + +### 4. 永久封禁 + +**社区影响**:行为模式表现出违反社区准则,包括持续的不当行为、骚扰个人或攻击或贬低某个类别的个体。 + +**处理意见**:永久禁止在社区内进行任何形式的公开互动。 + +## 参见 + +本行为准则改编自 [Contributor Covenant][homepage] 2.1 版, 参见 [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]。 + +社区处理方针灵感来源于 [Mozilla's code of conduct enforcement ladder][mozilla coc]。 + +有关本行为准则的常见问题的答案,参见 [https://www.contributor-covenant.org/faq][faq]。 +其他语言翻译参见 [https://www.contributor-covenant.org/translations][translations]。 + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[mozilla coc]: https://github.com/mozilla/diversity +[faq]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/01-alova-mock.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/01-alova-mock.md index 36676f14b..fe384e083 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/01-alova-mock.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/01-alova-mock.md @@ -1,6 +1,5 @@ --- title: 模拟数据 -sidebar_position: 10 --- import Tabs from '@theme/Tabs'; @@ -101,7 +100,7 @@ export default defineMock( 在调用`createAlova`时创建一个模拟请求适配器,并将 mock 接口传入即可完成。 ```javascript -import GlobalFetch from 'alova/GlobalFetch'; +import adapterFetch from 'alova/fetch'; import { createAlovaMockAdapter } from '@alova/mock'; import mockGroup1 from './mockGroup1'; @@ -111,7 +110,7 @@ const mockAdapter = createAlovaMockAdapter([mockGroup1, /** ... */], { enable: true, // 非模拟请求适配器,用于未匹配mock接口时发送请求 - httpAdapter: GlobalFetch(), + httpAdapter: adapterFetch(), // mock接口响应延迟,单位毫秒 delay: 1000, @@ -120,7 +119,7 @@ const mockAdapter = createAlovaMockAdapter([mockGroup1, /** ... */], { mockRequestLogger: true, // 模拟接口回调,data为返回的模拟数据,你可以用它构造任何你想要的对象返回给alova - // 以下为默认的回调函数,它适用于使用GlobalFetch请求适配器 + // 以下为默认的回调函数,它适用于使用 `alova/fetch` 请求适配器 // 如果你使用的是其他请求适配器,在模拟接口回调中请自定义返回适合适配器的数据结构 onMockResponse: data => new Response(JSON.stringify(data)) }); @@ -223,7 +222,7 @@ import Augustv1_1 from './August-v1.1'; import kevinv1_1 from './kevin-v1.1'; const mockAdapter = createAlovaMockAdapter([Augustv1_1, kevinv1_1], { - httpAdapter: GlobalFetch(), + httpAdapter: adapterFetch(), delay: 1000 }); export const alovaInst = createAlova({ @@ -238,9 +237,9 @@ export const alovaInst = createAlova({ mock 数据一般只作用于开发环境,在生产环境下将会切换到实际的接口中,因此这段 mock 代码在生产环境就变得没有作用,此时我们可以通过环境变量的判断来排除这块代码,你只需要这样做: ```javascript -const globalFetch = GlobalFetch(); +const fetchAdapter = adapterFetch(); const mockAdapter = createAlovaMockAdapter([mockGroup1, /** ... */], { - httpAdapter: globalFetch, + httpAdapter: fetchAdapter, delay: 1000, }); @@ -273,7 +272,7 @@ export default defineMock({ ## 转换模拟数据 -**@alova/mock** 默认将响应数据包装为 Response 实例,将响应头默认包装为 Headers 实例,这是针对`GlobalFetch`进行适配的,但如果使用其他的请求适配器,就需要将模拟数据转换为相应的格式。 +**@alova/mock** 默认将响应数据包装为 Response 实例,将响应头默认包装为 Headers 实例,这是针对`alova/fetch`进行适配的,但如果使用其他的请求适配器,就需要将模拟数据转换为相应的格式。 ### 转换响应数据 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/02-alova-adapter-xhr.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/02-alova-adapter-xhr.md index 780837842..b5d1ae7de 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/02-alova-adapter-xhr.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/02-alova-adapter-xhr.md @@ -1,337 +1,336 @@ ---- -title: XMLHttpRequest适配器 -sidebar_position: 20 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -## 安装 - - - - -```bash -npm install @alova/adapter-xhr --save -``` - - - - -```bash -yarn add @alova/adapter-xhr -``` - - - - -## 使用方法 - -### 创建 alova - -使用 **xhrRequestAdapter** 作为 alova 的请求适配器。 - -```javascript -import { createAlova } from 'alova'; -import { xhrRequestAdapter } from '@alova/adapter-xhr'; - -const alovaInst = createAlova({ - // ... - requestAdapter: xhrResponseAdapter() - // ... -}); -``` - -### 请求 - -XMLHttpRequest 适配器提供了基本的配置参数,包含`responseType`、`withCredentials`、`mimeType`、`auth`,具体如下: - - - - -```html - -
加载中...
-
请求数据为:{{ data }}
-
- - -``` - -
- - -```jsx -const list = () => - alovaInst.Get('/list', { - /** - * 设置响应数据类型 - * 可以设置更改响应类型。 值为:“arraybuffer”、“blob”、“document”、“json”和“text” - * 默认为“json” - */ - responseType: 'text', - - /** - * 当凭证要包含在跨源请求中时为true。 当它们被排除在跨源请求中以及当 cookie 在其响应中被忽略时为 false。 默认为false - */ - withCredentials: true, - - /** - * 设置响应数据的mimeType - */ - mimeType: 'text/plain; charset=x-user-defined', - - /** - * auth 表示使用 HTTP Basic 身份验证,并提供凭据。 - * 这将设置一个 `Authorization` 标头,覆盖任何现有的 - * 使用 `headers` 设置的 `Authorization` 自定义标头。 - * 请注意,只有 HTTP Basic 身份验证可以通过此参数进行配置。 - * 对于 Bearer 令牌等,请改用 `Authorization` 自定义标头。 - */ - auth: { - username: 'name1', - password: '123456' - } - }); - -const App = () => { - const { loading, data } = useRequest(list); - - return ( - { loading ?
加载中...
: null } -
请求数据为:{ JSON.stringify(data) }
- ) -}; -``` - -
- - -```html - - -{#if $loading} -
加载中...
-{/if} -
请求数据为:{ data }
-``` - -
-
- -### 上传 - -使用`FormData`上传文件,这个`FormData`实例会通过`xhr.send`发送到服务端。上传时将自动设置`Content-Type`,不需要自定义设置为`multipart/form-data`。 - -```javascript -const uploadFile = imageFile => { - const formData = new FormData(); - formData.append('file', imageFile); - return alovaInst.Post('/uploadImg', formData, { - // 开启上传进度 - enableUpload: true - }); -}; - -const { - loading, - data, - uploading, - send: sendUpload -} = useRequest(uploadFile, { - immediate: false -}); - -// 图片选择事件回调 -const handleImageChoose = ({ target }) => { - sendUpload(target.files[0]); -}; -``` - -### 下载 - -将请求 url 指向文件地址即可下载,你也可以通过将`enableDownload`设置为 true 来开启下载进度。 - -```javascript -const downloadFile = () => - alovaInst.Get('/bigImage.jpg', { - // 开启下载进度 - enableDownload: true, - responseType: 'blob' - }); - -const { loading, data, downloading, send, onSuccess } = useRequest(downloadFile, { - immediate: false -}); -onSuccess(({ data: imageBlob }) => { - // 下载图片 - const anchor = document.createElement('a'); - anchor.href = URL.createObjectURL(blob); - anchor.download = 'image.jpg'; - anchor.click(); - URL.revokeObjectURL(anchor.href); -}); -const handleImageDownload = () => { - send(); -}; -``` - -## 模拟请求适配器兼容 - -在开发应用时,我们仍然可能需要用到模拟请求。只是默认情况下,[模拟请求适配器(@alova/mock)](/tutorial/request-adapter/alova-mock)的响应数据是一个`Response`实例,即默认兼容`GlobalFetch`请求适配器,当使用 XMLHttpRequest 适配器时,我们需要让模拟请求适配器的响应数据适配 XMLHttpRequest 适配器,此时你需要使用**@alova/adapter-xhr**包中导出的`xhrMockResponse`作为响应适配器。 - -```javascript -import { defineMock, createAlovaMockAdapter } from '@alova/mock'; -import { xhrRequestAdapter, xhrMockResponse } from '@alova/adapter-xhr'; - -const mocks = defineMock({ - // ... -}); - -// 模拟数据请求适配器 -export default createAlovaMockAdapter([mocks], { - // 指定请求适配器后,未匹配模拟接口的请求将使用这个适配器发送请求 - httpAdapter: xhrRequestAdapter(), - - // 使用xhrMockResponse,让模拟数据适配XMLHttpRequest适配器 - onMockResponse: xhrMockResponse -}); - -export const alovaInst = createAlova({ - // ... - // 通过环境变量控制是否使用模拟请求适配器 - requestAdapter: process.env.NODE_ENV === 'development' ? mockAdapter : xhrRequestAdapter() -}); -``` - -## Typescript - -XMLHttpRequest 请求适配器 提供了完整的类型适配。 - -### method 配置 - -在创建 method 实例时,除了 method 中通用的配置项外,你还可以使用`AlovaXHRRequestConfig`中的配置项。 - -```typescript -/** - * xhr请求配置参数 - */ -interface AlovaXHRRequestConfig { - /** - * 设置响应数据类型。 - * - * 可以设置更改响应类型。 值为:“arraybuffer”、“blob”、“document”、“json”和“text”。 - * 设置1:如果当前全局对象不是 Window 对象,则忽略设置为“文档”。 - * 设置2:如果状态正在加载或完成,则抛出“InvalidStateError”DOMException。 - * 设置3:如果设置了同步标志且当前全局对象是 Window 对象,则抛出“InvalidAccessError”DOMException。 - * @default "json" - */ - responseType?: XMLHttpRequestResponseType; - - /** - * 当凭证要包含在跨源请求中时为true。 当它们被排除在跨源请求中以及当 cookie 在其响应中被忽略时为 false。 默认为false。 - * 如果状态未发送或未打开,或者设置了send() 标志,则抛出“InvalidStateError”DOMException。 - * @default false - */ - withCredentials?: boolean; - - /** - * 设置响应数据的mimeType - */ - mimeType?: string; - - /** - * `auth` 表示应该使用 HTTP Basic 身份验证,并提供凭据。 - * 这将设置一个 `Authorization` 标头,覆盖任何现有的 - * 使用 `headers` 设置的 `Authorization` 自定义标头。 - * 请注意,只有 HTTP Basic 身份验证可以通过此参数进行配置。 - * 对于 Bearer 令牌等,请改用 `Authorization` 自定义标头。 - */ - auth?: { - username: string; - password: string; - }; -} -``` - -### 响应数据 - -XMLHttpRequest 适配器响应数据如下: - -```typescript -interface AlovaXHRResponseHeaders { - [x: string]: any; -} -interface AlovaXHRResponse { - status: number; - statusText: string; - data: T; - headers: AlovaXHRResponseHeaders; -} -``` +--- +title: XMLHttpRequest适配器 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## 安装 + + + + +```bash +npm install @alova/adapter-xhr --save +``` + + + + +```bash +yarn add @alova/adapter-xhr +``` + + + + +## 使用方法 + +### 创建 alova + +使用 **xhrRequestAdapter** 作为 alova 的请求适配器。 + +```javascript +import { createAlova } from 'alova'; +import { xhrRequestAdapter } from '@alova/adapter-xhr'; + +const alovaInst = createAlova({ + // ... + requestAdapter: xhrResponseAdapter() + // ... +}); +``` + +### 请求 + +XMLHttpRequest 适配器提供了基本的配置参数,包含`responseType`、`withCredentials`、`mimeType`、`auth`,具体如下: + + + + +```html + +
加载中...
+
请求数据为:{{ data }}
+
+ + +``` + +
+ + +```jsx +const list = () => + alovaInst.Get('/list', { + /** + * 设置响应数据类型 + * 可以设置更改响应类型。 值为:“arraybuffer”、“blob”、“document”、“json”和“text” + * 默认为“json” + */ + responseType: 'text', + + /** + * 当凭证要包含在跨源请求中时为true。 当它们被排除在跨源请求中以及当 cookie 在其响应中被忽略时为 false。 默认为false + */ + withCredentials: true, + + /** + * 设置响应数据的mimeType + */ + mimeType: 'text/plain; charset=x-user-defined', + + /** + * auth 表示使用 HTTP Basic 身份验证,并提供凭据。 + * 这将设置一个 `Authorization` 标头,覆盖任何现有的 + * 使用 `headers` 设置的 `Authorization` 自定义标头。 + * 请注意,只有 HTTP Basic 身份验证可以通过此参数进行配置。 + * 对于 Bearer 令牌等,请改用 `Authorization` 自定义标头。 + */ + auth: { + username: 'name1', + password: '123456' + } + }); + +const App = () => { + const { loading, data } = useRequest(list); + + return ( + { loading ?
加载中...
: null } +
请求数据为:{ JSON.stringify(data) }
+ ) +}; +``` + +
+ + +```html + + +{#if $loading} +
加载中...
+{/if} +
请求数据为:{ data }
+``` + +
+
+ +### 上传 + +使用`FormData`上传文件,这个`FormData`实例会通过`xhr.send`发送到服务端。上传时将自动设置`Content-Type`,不需要自定义设置为`multipart/form-data`。 + +```javascript +const uploadFile = imageFile => { + const formData = new FormData(); + formData.append('file', imageFile); + return alovaInst.Post('/uploadImg', formData, { + // 开启上传进度 + enableUpload: true + }); +}; + +const { + loading, + data, + uploading, + send: sendUpload +} = useRequest(uploadFile, { + immediate: false +}); + +// 图片选择事件回调 +const handleImageChoose = ({ target }) => { + sendUpload(target.files[0]); +}; +``` + +### 下载 + +将请求 url 指向文件地址即可下载,你也可以通过将`enableDownload`设置为 true 来开启下载进度。 + +```javascript +const downloadFile = () => + alovaInst.Get('/bigImage.jpg', { + // 开启下载进度 + enableDownload: true, + responseType: 'blob' + }); + +const { loading, data, downloading, send, onSuccess } = useRequest(downloadFile, { + immediate: false +}); +onSuccess(({ data: imageBlob }) => { + // 下载图片 + const anchor = document.createElement('a'); + anchor.href = URL.createObjectURL(blob); + anchor.download = 'image.jpg'; + anchor.click(); + URL.revokeObjectURL(anchor.href); +}); +const handleImageDownload = () => { + send(); +}; +``` + +## 模拟请求适配器兼容 + +在开发应用时,我们仍然可能需要用到模拟请求。只是默认情况下,[模拟请求适配器(@alova/mock)](/next/resource/request-adapter/alova-mock)的响应数据是一个`Response`实例,即默认兼容`alova/fetch`请求适配器,当使用 XMLHttpRequest 适配器时,我们需要让模拟请求适配器的响应数据适配 XMLHttpRequest 适配器,此时你需要使用**@alova/adapter-xhr**包中导出的`xhrMockResponse`作为响应适配器。 + +```javascript +import { defineMock, createAlovaMockAdapter } from '@alova/mock'; +import { xhrRequestAdapter, xhrMockResponse } from '@alova/adapter-xhr'; + +const mocks = defineMock({ + // ... +}); + +// 模拟数据请求适配器 +export default createAlovaMockAdapter([mocks], { + // 指定请求适配器后,未匹配模拟接口的请求将使用这个适配器发送请求 + httpAdapter: xhrRequestAdapter(), + + // 使用xhrMockResponse,让模拟数据适配XMLHttpRequest适配器 + onMockResponse: xhrMockResponse +}); + +export const alovaInst = createAlova({ + // ... + // 通过环境变量控制是否使用模拟请求适配器 + requestAdapter: process.env.NODE_ENV === 'development' ? mockAdapter : xhrRequestAdapter() +}); +``` + +## Typescript + +XMLHttpRequest 请求适配器 提供了完整的类型适配。 + +### method 配置 + +在创建 method 实例时,除了 method 中通用的配置项外,你还可以使用`AlovaXHRRequestConfig`中的配置项。 + +```typescript +/** + * xhr请求配置参数 + */ +interface AlovaXHRRequestConfig { + /** + * 设置响应数据类型。 + * + * 可以设置更改响应类型。 值为:“arraybuffer”、“blob”、“document”、“json”和“text”。 + * 设置1:如果当前全局对象不是 Window 对象,则忽略设置为“文档”。 + * 设置2:如果状态正在加载或完成,则抛出“InvalidStateError”DOMException。 + * 设置3:如果设置了同步标志且当前全局对象是 Window 对象,则抛出“InvalidAccessError”DOMException。 + * @default "json" + */ + responseType?: XMLHttpRequestResponseType; + + /** + * 当凭证要包含在跨源请求中时为true。 当它们被排除在跨源请求中以及当 cookie 在其响应中被忽略时为 false。 默认为false。 + * 如果状态未发送或未打开,或者设置了send() 标志,则抛出“InvalidStateError”DOMException。 + * @default false + */ + withCredentials?: boolean; + + /** + * 设置响应数据的mimeType + */ + mimeType?: string; + + /** + * `auth` 表示应该使用 HTTP Basic 身份验证,并提供凭据。 + * 这将设置一个 `Authorization` 标头,覆盖任何现有的 + * 使用 `headers` 设置的 `Authorization` 自定义标头。 + * 请注意,只有 HTTP Basic 身份验证可以通过此参数进行配置。 + * 对于 Bearer 令牌等,请改用 `Authorization` 自定义标头。 + */ + auth?: { + username: string; + password: string; + }; +} +``` + +### 响应数据 + +XMLHttpRequest 适配器响应数据如下: + +```typescript +interface AlovaXHRResponseHeaders { + [x: string]: any; +} +interface AlovaXHRResponse { + status: number; + statusText: string; + data: T; + headers: AlovaXHRResponseHeaders; +} +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/03-alova-adapter-axios.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/03-alova-adapter-axios.md index 337bbab35..446ea05e3 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/03-alova-adapter-axios.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/03-alova-adapter-axios.md @@ -1,291 +1,290 @@ ---- -title: axios适配器 -sidebar_position: 30 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -## 安装 - - - - -```bash -npm install @alova/adapter-axios --save -``` - - - - -```bash -yarn add @alova/adapter-axios -``` - - - - -## 使用方法 - -### 创建 alova - -使用 **axiosRequestAdapter** 作为 alova 的请求适配器。 - -```javascript -import { createAlova } from 'alova'; -import { axiosRequestAdapter } from '@alova/adapter-axios'; - -const alovaInst = createAlova({ - // ... - requestAdapter: axiosRequestAdapter() - // ... -}); -``` - -适配器内部将会使用默认的 axios 实例进行请求,如果你为 axios 设置了一些全局参数,你可能需要注意以下两点: - -1. 优先使用 axios 实例内的`baseURL`和`timeout`参数,因此如果你在 axios 实例上设置了这些参数,那么就可以不需要在`createAlova`时设置了; -2. alova 实例的`beforeRequest`钩子将会早于 axios 的`interceptor.request`触发,alova 实例的`responded`钩子将会晚于 axios 实例的`interceptor.response`触发; - -> 你也可以[使用自定义的 axios 实例](#使用自定义的-axios-实例) - -### 请求 - -请求的使用方法与 web 环境中使用完全一致。已经完全兼容**axios**,你可以在创建 method 实例的*config*中指定`axios`支持的[全部配置项](https://axios-http.com/docs/req_config) - - - - -```html - -
加载中...
-
请求数据为:{{ data }}
-
- - -``` - -
- - -```jsx -const list = () => - alovaInst.Get('/list', { - // 设置的参数将传递给axios - paramsSerializer: params => { - return Qs.stringify(params, {arrayFormat: 'brackets'}) - }, - }); - -const App = () => { - const { loading, data } = useRequest(list); - - return ( - { loading ?
加载中...
: null } -
请求数据为:{ JSON.stringify(data) }
- ) -}; -``` - -
- - -```html - - -{#if $loading} -
加载中...
-{/if} -
请求数据为:{ data }
-``` - -
-
- -### 上传 - -使用`FormData`上传文件,这个`FormData`实例会被传递到 axios 中,与 axios 上传文件用法保持了一致。 - -```javascript -const uploadFile = imageFile => { - const formData = new FormData(); - formData.append('file', imageFile); - return alovaInst.Post('/uploadImg', formData, { - // 开启上传进度 - enableUpload: true - }); -}; - -const { - loading, - data, - uploading, - send: sendUpload -} = useRequest(uploadFile, { - immediate: false -}); - -// 图片选择事件回调 -const handleImageChoose = ({ target }) => { - sendUpload(target.files[0]); -}; -``` - -### 下载 - -将请求 url 指向文件地址即可下载,你也可以通过将`enableDownload`设置为 true 来开启下载进度。 - -```javascript -const downloadFile = () => - alovaInst.Get('/bigImage.jpg', { - // 开启下载进度 - enableDownload: true, - responseType: 'blob' - }); - -const { loading, data, downloading, send, onSuccess } = useRequest(downloadFile, { - immediate: false -}); -onSuccess(({ data: imageBlob }) => { - // 下载图片 - const anchor = document.createElement('a'); - anchor.href = URL.createObjectURL(blob); - anchor.download = 'image.jpg'; - anchor.click(); - URL.revokeObjectURL(anchor.href); -}); -const handleImageDownload = () => { - send(); -}; -``` - -## 使用自定义的 axios 实例 - -在默认情况下,此适配器会使用默认的 axios 实例进行请求,但在一些情况下你需要使用自定义创建的 axios 实例,你可以这么做: - -```javascript -const customAxios = axios.create({ - // ... -}); - -const alovaInst = createAlova({ - // ... - // highlight-start - requestAdapter: axiosRequestAdapter({ - axios: customAxios - }) - // highlight-end -}); -``` - -## 模拟请求适配器兼容 - -在开发应用时,我们仍然可能需要用到模拟请求。只是默认情况下,[模拟请求适配器(@alova/mock)](/tutorial/request-adapter/alova-mock)的响应数据是一个`Response`实例,即默认兼容`GlobalFetch`请求适配器,当使用 axios 适配器时,我们需要让模拟请求适配器的响应数据是**AxiosResponse**兼容的,错误实例是**AxiosError**,因此你需要使用**@alova/adapter-axios**包中导出的`axiosMockResponse`作为响应适配器。 - -```javascript -import { defineMock, createAlovaMockAdapter } from '@alova/mock'; -import { axiosRequestAdapter, axiosMockResponse } from '@alova/adapter-axios'; - -const mocks = defineMock({ - // ... -}); - -// 模拟数据请求适配器 -export default createAlovaMockAdapter([mocks], { - // 指定axios请求适配器后,未匹配模拟接口的请求将使用这个适配器发送请求 - httpAdapter: axiosRequestAdapter(), - - // axiosMockResponse中包含了onMockResponse和onMockError - // 用于将模拟数据转换为AxiosResponse和AxiosError兼容的格式 - ...axiosMockResponse -}); - -export const alovaInst = createAlova({ - // ... - // 通过环境变量控制是否使用模拟请求适配器 - requestAdapter: process.env.NODE_ENV === 'development' ? mockAdapter : axiosRequestAdapter() -}); -``` - -## Typescript - -axios 请求适配器 提供了完整的类型适配,method 配置、响应数据的类型将与 axios 的类型完全匹配。 - -### method 配置 - -在创建 method 实例时,除了 method 中通用的配置项外,你还可以使用`AxiosRequestConfig`中的配置项,我们已经在类型中去除了和 method 实例通用配置冲突的项。 - -```typescript -/** - * axios请求配置参数 - * 去掉了与method冲突的属性 - */ -export type AlovaAxiosRequestConfig = Omit< - AxiosRequestConfig, - | 'url' - | 'method' - | 'baseURL' - | 'headers' - | 'params' - | 'data' - | 'timeout' - | 'cancelToken' - | 'signal' - | 'onUploadProgress' - | 'onDownloadProgress' ->; -``` - -### 响应数据 - -axios 的响应数据类型是`AxiosResponse`,当你使用 axios 适配器时,也将获得相同格式的响应数据。在实际使用中,我们通常需要在全局处理响应数据,一个简单的实例如下: - -```typescript -const alovaInst = createAlova( - baseURL: 'https://api.alovajs.org', - requestAdapter: axiosRequestAdapter(), - responded(response) { - // response自动被推断为AxiosResponse类型 - return response.data; - } -}); -``` - -### 错误 - -当 axios 遇到非 20x 和 30x 的响应状态码时将会抛出错误,为了包含更多信息,axios 将错误实例自定义设计成了一个`AxiosError`实例,而不是普通的 Error 实例,因此当遇到服务端错误或网络错误时都将抛出一个错误,你可以在全局的错误回调中捕获它。 - -```typescript -const alovaInst = createAlova( - baseURL: 'https://api.alovajs.org', - requestAdapter: axiosRequestAdapter(), - responded: { - onSuccess(response) { - // response自动被推断为AxiosResponse类型 - return response.data; - }, - onError(err: AxiosError) { - // err默认为any,你可以强制转换为AxiosError处理 - // ... - } - } -}); -``` +--- +title: axios适配器 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## 安装 + + + + +```bash +npm install @alova/adapter-axios --save +``` + + + + +```bash +yarn add @alova/adapter-axios +``` + + + + +## 使用方法 + +### 创建 alova + +使用 **axiosRequestAdapter** 作为 alova 的请求适配器。 + +```javascript +import { createAlova } from 'alova'; +import { axiosRequestAdapter } from '@alova/adapter-axios'; + +const alovaInst = createAlova({ + // ... + requestAdapter: axiosRequestAdapter() + // ... +}); +``` + +适配器内部将会使用默认的 axios 实例进行请求,如果你为 axios 设置了一些全局参数,你可能需要注意以下两点: + +1. 优先使用 axios 实例内的`baseURL`和`timeout`参数,因此如果你在 axios 实例上设置了这些参数,那么就可以不需要在`createAlova`时设置了; +2. alova 实例的`beforeRequest`钩子将会早于 axios 的`interceptor.request`触发,alova 实例的`responded`钩子将会晚于 axios 实例的`interceptor.response`触发; + +> 你也可以[使用自定义的 axios 实例](#使用自定义的-axios-实例) + +### 请求 + +请求的使用方法与 web 环境中使用完全一致。已经完全兼容**axios**,你可以在创建 method 实例的*config*中指定`axios`支持的[全部配置项](https://axios-http.com/docs/req_config) + + + + +```html + +
加载中...
+
请求数据为:{{ data }}
+
+ + +``` + +
+ + +```jsx +const list = () => + alovaInst.Get('/list', { + // 设置的参数将传递给axios + paramsSerializer: params => { + return Qs.stringify(params, {arrayFormat: 'brackets'}) + }, + }); + +const App = () => { + const { loading, data } = useRequest(list); + + return ( + { loading ?
加载中...
: null } +
请求数据为:{ JSON.stringify(data) }
+ ) +}; +``` + +
+ + +```html + + +{#if $loading} +
加载中...
+{/if} +
请求数据为:{ data }
+``` + +
+
+ +### 上传 + +使用`FormData`上传文件,这个`FormData`实例会被传递到 axios 中,与 axios 上传文件用法保持了一致。 + +```javascript +const uploadFile = imageFile => { + const formData = new FormData(); + formData.append('file', imageFile); + return alovaInst.Post('/uploadImg', formData, { + // 开启上传进度 + enableUpload: true + }); +}; + +const { + loading, + data, + uploading, + send: sendUpload +} = useRequest(uploadFile, { + immediate: false +}); + +// 图片选择事件回调 +const handleImageChoose = ({ target }) => { + sendUpload(target.files[0]); +}; +``` + +### 下载 + +将请求 url 指向文件地址即可下载,你也可以通过将`enableDownload`设置为 true 来开启下载进度。 + +```javascript +const downloadFile = () => + alovaInst.Get('/bigImage.jpg', { + // 开启下载进度 + enableDownload: true, + responseType: 'blob' + }); + +const { loading, data, downloading, send, onSuccess } = useRequest(downloadFile, { + immediate: false +}); +onSuccess(({ data: imageBlob }) => { + // 下载图片 + const anchor = document.createElement('a'); + anchor.href = URL.createObjectURL(blob); + anchor.download = 'image.jpg'; + anchor.click(); + URL.revokeObjectURL(anchor.href); +}); +const handleImageDownload = () => { + send(); +}; +``` + +## 使用自定义的 axios 实例 + +在默认情况下,此适配器会使用默认的 axios 实例进行请求,但在一些情况下你需要使用自定义创建的 axios 实例,你可以这么做: + +```javascript +const customAxios = axios.create({ + // ... +}); + +const alovaInst = createAlova({ + // ... + // highlight-start + requestAdapter: axiosRequestAdapter({ + axios: customAxios + }) + // highlight-end +}); +``` + +## 模拟请求适配器兼容 + +在开发应用时,我们仍然可能需要用到模拟请求。只是默认情况下,[模拟请求适配器(@alova/mock)](/next/resource/request-adapter/alova-mock)的响应数据是一个`Response`实例,即默认兼容`alova/fetch`请求适配器,当使用 axios 适配器时,我们需要让模拟请求适配器的响应数据是**AxiosResponse**兼容的,错误实例是**AxiosError**,因此你需要使用**@alova/adapter-axios**包中导出的`axiosMockResponse`作为响应适配器。 + +```javascript +import { defineMock, createAlovaMockAdapter } from '@alova/mock'; +import { axiosRequestAdapter, axiosMockResponse } from '@alova/adapter-axios'; + +const mocks = defineMock({ + // ... +}); + +// 模拟数据请求适配器 +export default createAlovaMockAdapter([mocks], { + // 指定axios请求适配器后,未匹配模拟接口的请求将使用这个适配器发送请求 + httpAdapter: axiosRequestAdapter(), + + // axiosMockResponse中包含了onMockResponse和onMockError + // 用于将模拟数据转换为AxiosResponse和AxiosError兼容的格式 + ...axiosMockResponse +}); + +export const alovaInst = createAlova({ + // ... + // 通过环境变量控制是否使用模拟请求适配器 + requestAdapter: process.env.NODE_ENV === 'development' ? mockAdapter : axiosRequestAdapter() +}); +``` + +## Typescript + +axios 请求适配器 提供了完整的类型适配,method 配置、响应数据的类型将与 axios 的类型完全匹配。 + +### method 配置 + +在创建 method 实例时,除了 method 中通用的配置项外,你还可以使用`AxiosRequestConfig`中的配置项,我们已经在类型中去除了和 method 实例通用配置冲突的项。 + +```typescript +/** + * axios请求配置参数 + * 去掉了与method冲突的属性 + */ +export type AlovaAxiosRequestConfig = Omit< + AxiosRequestConfig, + | 'url' + | 'method' + | 'baseURL' + | 'headers' + | 'params' + | 'data' + | 'timeout' + | 'cancelToken' + | 'signal' + | 'onUploadProgress' + | 'onDownloadProgress' +>; +``` + +### 响应数据 + +axios 的响应数据类型是`AxiosResponse`,当你使用 axios 适配器时,也将获得相同格式的响应数据。在实际使用中,我们通常需要在全局处理响应数据,一个简单的实例如下: + +```typescript +const alovaInst = createAlova( + baseURL: 'https://api.alovajs.org', + requestAdapter: axiosRequestAdapter(), + responded(response) { + // response自动被推断为AxiosResponse类型 + return response.data; + } +}); +``` + +### 错误 + +当 axios 遇到非 20x 和 30x 的响应状态码时将会抛出错误,为了包含更多信息,axios 将错误实例自定义设计成了一个`AxiosError`实例,而不是普通的 Error 实例,因此当遇到服务端错误或网络错误时都将抛出一个错误,你可以在全局的错误回调中捕获它。 + +```typescript +const alovaInst = createAlova( + baseURL: 'https://api.alovajs.org', + requestAdapter: axiosRequestAdapter(), + responded: { + onSuccess(response) { + // response自动被推断为AxiosResponse类型 + return response.data; + }, + onError(err: AxiosError) { + // err默认为any,你可以强制转换为AxiosError处理 + // ... + } + } +}); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/04-alova-adapter-taro.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/04-alova-adapter-taro.md index 23d98ed45..0d5078460 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/04-alova-adapter-taro.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/04-alova-adapter-taro.md @@ -1,436 +1,443 @@ ---- -title: Taro适配器 -sidebar_position: 40 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -:::info 提示 - -此插件只支持 react 16.8+、vue3 版本的 taro 应用。 - -::: - -## 安装 - - - - -```bash -npm install @alova/adapter-taro --save -``` - - - - -```bash -yarn add @alova/adapter-taro -``` - - - - -:::warning React-Native 应用 - -如果你正在使用 Taro 开发 React-Native 应用,请确保`metro >= 0.76.0`,并在`metro.config.js`中开启`resolver.unstable_enablePackageExports` - -[关于 metro 的 unstable_enablePackageExports 参数](https://facebook.github.io/metro/docs/configuration/#unstable_enablepackageexports-experimental) - -::: - -:::warning 依赖预编译问题 - -在 Taro v3.5 beta 中新增了[依赖预编译功能](https://docs.taro.zone/blog/2022/05/19/Taro-3.5-beta#2-%E4%BE%9D%E8%B5%96%E9%A2%84%E7%BC%96%E8%AF%91)并在开发模式下默认开启,当你正在 Taro 中同时使用`alova`库和`@alova/scene-react(vue)`时可能导致报 `` [alova]can not call useHooks until set the `statesHook` at alova instance.``的错误,这是由于 prebundle 功能重复打包了两份不同的`alova`包导致,此时关闭 prebundle 功能即可解决此问题。 - -```js -// config/dev.ts -export default { - // ... - compiler: { - type: 'webpack5', - prebundle: { - // 关闭prebundle - enable: false - } - } -} satisfies UserConfigExport - -``` - -感谢[LBinin 的 issue](https://github.com/alovajs/scene/issues/63)。 - -此问题已向 Taro 团队提交[issue](https://github.com/NervJS/taro/issues/15728),期待解决此问题。 - -::: - -## 使用方法 - -### 创建 alova - -调用 **AdapterTaro** 将返回*请求适配器*、_存储适配器_,以及*ReactHook*,因此你不再需要设置这三个项,使用方法完全一致。 - - - - -```javascript -import { createAlova } from 'alova'; -import AdapterTaro from '@alova/adapter-taro'; - -const alovaInst = createAlova({ - baseURL: 'https://api.alovajs.org', - ...AdapterTaro() -}); -``` - - - - - -```javascript -import { createAlova } from 'alova'; -import AdapterTaroVue from '@alova/adapter-taro/vue'; - -const alovaInst = createAlova({ - baseURL: 'https://api.alovajs.org', - ...AdapterTaroVue() -}); -``` - - - - -### 请求 - -请求的使用方法与 web 环境中使用完全一致。已经完全兼容`Taro.request`,你可以在创建 method 实例的*config*中指定`Taro.request`支持的[全部配置项](https://taro-docs.jd.com/docs/apis/network/request/) - - - - -```jsx -const list = () => - alovaInst.Get('/list', { - // 设置的参数将传递给Taro.request - enableHttp2: true - }); - -const App = () => { - const { loading, data } = useRequest(list); - - return ( - { loading ? 加载中... : null } - 请求数据为:{ JSON.stringify(data) } - ) -}; -``` - - - - -```html - - 加载中... - 请求数据为:{{ data }} - - - -``` - - - - -### 上传 - -在 method 实例的*config*中设置`requestType: 'upload'`时表示上传文件,请求适配器将会调用`Taro.uploadFile`,上传的文件数据放在 method 实例的 data 中,你需要在 data 中指定`name`和`filePath`,这 2 个参数将传到`Taro.uploadFile`中,同时,你还可以在 data 中指定这 2 个参数外的其他参数,请求适配器会将它们传入到`formData`参数中。 - -同样的,已经完全兼容`Taro.uploadFile`,你可以在创建 method 实例的*config*中指定`Taro.uploadFile`支持的[全部配置项](https://taro-docs.jd.com/docs/apis/network/upload/uploadFile),如果还有额外的参数需要设置,请在 method 实例的*config*中指定。 - - - - -```jsx -const uploadFile = (name, filePath, formData) => - alovaInst.Post( - '/uploadImg', - { - name, - filePath, - - // 额外数据将传入uni.uploadFile的formData中 - ...formData - }, - { - // 设置请求方式为上传,适配器内将调用uni.uploadFile - requestType: 'upload', - - // 开启上传进度 - enableUpload: true - } - ); - -const App = () => { - const { loading, data, uploading, send } = useRequest(uploadFile, { - immediate: false - }); - - const handleImageChoose = () => { - Taro.chooseImage({ - success: chooseImageRes => { - const tempFilePaths = chooseImageRes.tempFilePaths; - send('fileName', tempFilePaths[0], { - extra1: 'a', - extra2: 'b' - }); - } - }); - }; - - return ( - { loading ? 上传中... : null } - 上传进度:{ uploading.loaded }/{ uploading.total } - - {/* ... */} - ) -} -``` - - - - -```html - - 上传中... - 上传进度:{{ uploading.loaded }}/{{ uploading.total }} - - - - - -``` - - - - -### 下载 - -在 method 实例的*config*中设置`requestType: 'download'`时表示下载文件,请求适配器将会调用`Taro.downloadFile`。 - -同样的,已经完全兼容`Taro.downloadFile`,你可以在创建 method 实例的*config*中指定`Taro.downloadFile`支持的[全部配置项](https://taro-docs.jd.com/docs/apis/network/download/downloadFile),如果还有额外的参数需要设置,请在 method 实例的*config*中指定。 - - - - -```jsx -const downloadFile = filePath => - alovaInst.Get('/bigImage.jpg', { - // 设置请求方式为下载,适配器内将调用uni.downloadFile - requestType: 'download', - filePath, - - // 开启下载进度 - enableDownload: true - }); - -const App = () => { - const { loading, data, downloading, send } = useRequest(downloadFile, { - immediate: false - }); - const handleImageDownload = () => { - send('file_save_path'); - }; - - return ( - { loading ? 下载中... : null } - 下载进度:{ downloading.loaded }/{ downloading.total } - - {/* ... */} - ); -} -``` - - - - -```html - - 下载中... - 下载进度:{{ downloading.loaded }}/{{ downloading.total }} - - - - - -``` - - - - -## 模拟请求适配器兼容 - -在使用 Taro 开发应用时,我们仍然可能需要用到模拟请求,只是默认情况下,[模拟请求适配器(@alova/mock)](/tutorial/request-adapter/alova-mock)的响应数据是一个`Response`实例,即默认兼容`GlobalFetch`请求适配器,当在 Taro 环境下使用时,我们需要让模拟请求适配器的响应数据是兼容 Taro 适配器的,因此你需要使用**@alova/adapter-taro**包中导出的`taroMockResponse`作为响应适配器。 - -```javascript -import { defineMock, createAlovaMockAdapter } from '@alova/mock'; -import AdapterTaro, { taroRequestAdapter, taroMockResponse } from '@alova/adapter-taro'; - -const mocks = defineMock({ - // ... -}); - -// 模拟数据请求适配器 -export default createAlovaMockAdapter([mocks], { - // 指定taro请求适配器后,未匹配模拟接口的请求将使用这个适配器发送请求 - httpAdapter: taroRequestAdapter, - - // 模拟响应适配器,指定后响应数据将转换为taro兼容的数据格式 - onMockResponse: taroMockResponse -}); - -export const alovaInst = createAlova({ - baseURL: 'https://api.alovajs.org', - timeout: 5000, - ...AdapterTaro({ - // 通过环境变量控制是否使用模拟请求适配器 - mockRequest: process.env.NODE_ENV === 'development' ? mockAdapter : undefined - }) - // ... -}); -``` - -## Typescript - -taro 请求适配器 提供了完整的类型适配,method 配置、响应数据的类型将与 taro 的类型完全匹配。 - -### method 配置 - -在创建 method 实例时,除了 method 中通用的配置项外,你还可以使用`Taro.request`、`Taro.uploadFile`和`Taro.downloadFile`中的配置项,我们已经在类型中去除了和 method 实例通用配置冲突的项。 - -```typescript -/** - * Taro.request请求额外参数 - */ -export type TaroRequestConfig = Omit< - Taro.request.Option, - 'url' | 'data' | 'header' | 'method' | 'timeout' | 'success' | 'fail' | 'complete' ->; - -/** - * Taro.uploadFile额外参数 - */ -export type TaroUploadConfig = Omit< - Taro.uploadFile.Option, - 'url' | 'filePath' | 'name' | 'header' | 'formData' | 'timeout' | 'success' | 'fail' | 'complete' ->; - -/** - * Taro.downloadFile额外参数 - */ -export type TaroDownloadConfig = Omit< - Taro.downloadFile.Option, - 'url' | 'header' | 'timeout' | 'success' | 'fail' | 'complete' ->; - -/** - * 合并的请求配置参数 - */ -export type TaroConfig = { - /** - * 请求类型,upload表示上传,download表示下载,不填写表示正常请求 - */ - requestType?: 'upload' | 'download'; -} & TaroRequestConfig & - TaroUploadConfig & - TaroDownloadConfig; -``` - -### 响应数据 - -因为 taro 请求适配器同时兼容了`Taro.request`、`Taro.uploadFile`和`Taro.downloadFile`,但它们的响应值类型稍有不同,所以响应数据类型是这样的: - -```typescript -type TaroResponse = - // Taro.request的响应类型 - | Taro.request.SuccessCallbackResult - - // Taro.uploadFile的响应类型 - | Taro.uploadFile.SuccessCallbackResult - - // Taro.downloadFile的响应类型 - | Taro.downloadFile.FileSuccessCallbackResult; -``` - -在实际使用中,我们通常需要在全局处理响应数据,建议分开场景判断返回数据,一个简单的实例如下: - -```typescript -const alovaInst = createAlova( - baseURL: 'https://api.alovajs.org', - ...AdapterTaro(), - responded(response) { - const { statusCode, data } = response as Taro.request.SuccessCallbackResult; - if (statusCode >= 400) { - throw new Error('请求错误'); - } - return data || null; - } -}); -``` +--- +title: Taro适配器 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info 提示 + +此插件只支持 react 16.8+、vue3 版本的 taro 应用。 + +::: + +## 安装 + + + + +```bash +npm install @alova/adapter-taro --save +``` + + + + +```bash +yarn add @alova/adapter-taro +``` + + + + +:::warning React-Native 应用 + +如果你正在使用 Taro 开发 React-Native 应用,请确保`metro >= 0.76.0`,并在`metro.config.js`中开启`resolver.unstable_enablePackageExports` + +[关于 metro 的 unstable_enablePackageExports 参数](https://facebook.github.io/metro/docs/configuration/#unstable_enablepackageexports-experimental) + +::: + +:::warning 依赖预编译问题 + +在 Taro v3.5 beta 中新增了[依赖预编译功能](https://docs.taro.zone/blog/2022/05/19/Taro-3.5-beta#2-%E4%BE%9D%E8%B5%96%E9%A2%84%E7%BC%96%E8%AF%91)并在开发模式下默认开启,当你正在 Taro 中同时使用`alova`库和`@alova/scene-react(vue)`时可能导致报 `` [alova]can not call useHooks until set the `statesHook` at alova instance.``的错误,这是由于 prebundle 功能重复打包了两份不同的`alova`包导致,此时关闭 prebundle 功能即可解决此问题。 + +```js +// config/dev.ts +export default { + // ... + compiler: { + type: 'webpack5', + prebundle: { + // 关闭prebundle + enable: false + } + } +} satisfies UserConfigExport + +``` + +感谢[LBinin 的 issue](https://github.com/alovajs/scene/issues/63)。 + +此问题已向 Taro 团队提交[issue](https://github.com/NervJS/taro/issues/15728),期待解决此问题。 + +::: + +## 使用方法 + +### 创建 alova + +调用 **AdapterTaro** 将返回*请求适配器*、_存储适配器_,以及*ReactHook*,因此你不再需要设置这三个项,使用方法完全一致。 + + + + +```javascript +import { createAlova } from 'alova'; +import AdapterTaro from '@alova/adapter-taro'; + +const alovaInst = createAlova({ + baseURL: 'https://api.alovajs.org', + ...AdapterTaro() +}); +``` + + + + + +```javascript +import { createAlova } from 'alova'; +import AdapterTaroVue from '@alova/adapter-taro/vue'; + +const alovaInst = createAlova({ + baseURL: 'https://api.alovajs.org', + ...AdapterTaroVue() +}); +``` + + + + +### 请求 + +请求的使用方法与 web 环境中使用完全一致。已经完全兼容`Taro.request`,你可以在创建 method 实例的*config*中指定`Taro.request`支持的[全部配置项](https://taro-docs.jd.com/docs/apis/network/request/) + + + + +```jsx +const list = () => + alovaInst.Get('/list', { + // 设置的参数将传递给Taro.request + enableHttp2: true + }); + +const App = () => { + const { loading, data } = useRequest(list); + + return ( + { loading ? 加载中... : null } + 请求数据为:{ JSON.stringify(data) } + ) +}; +``` + + + + +```html + + 加载中... + 请求数据为:{{ data }} + + + +``` + + + + +### 上传 + +在 method 实例的*config*中设置`requestType: 'upload'`时表示上传文件,请求适配器将会调用`Taro.uploadFile`,上传的文件数据放在 method 实例的 data 中,你需要在 data 中指定`name`和`filePath`,这 2 个参数将传到`Taro.uploadFile`中,同时,你还可以在 data 中指定这 2 个参数外的其他参数,请求适配器会将它们传入到`formData`参数中。 + +同样的,已经完全兼容`Taro.uploadFile`,你可以在创建 method 实例的*config*中指定`Taro.uploadFile`支持的[全部配置项](https://taro-docs.jd.com/docs/apis/network/upload/uploadFile),如果还有额外的参数需要设置,请在 method 实例的*config*中指定。 + + + + +```jsx +const uploadFile = (name, filePath, formData) => + alovaInst.Post( + '/uploadImg', + { + name, + filePath, + + // 额外数据将传入uni.uploadFile的formData中 + ...formData + }, + { + // 设置请求方式为上传,适配器内将调用uni.uploadFile + requestType: 'upload', + + // 开启上传进度 + enableUpload: true + } + ); + +const App = () => { + const { loading, data, uploading, send } = useRequest(uploadFile, { + immediate: false + }); + + const handleImageChoose = () => { + Taro.chooseImage({ + success: chooseImageRes => { + const tempFilePaths = chooseImageRes.tempFilePaths; + send('fileName', tempFilePaths[0], { + extra1: 'a', + extra2: 'b' + }); + } + }); + }; + + return ( + { loading ? 上传中... : null } + 上传进度:{ uploading.loaded }/{ uploading.total } + + {/* ... */} + ) +} +``` + + + + +```html + + 上传中... + 上传进度:{{ uploading.loaded }}/{{ uploading.total }} + + + + + +``` + + + + +### 下载 + +在 method 实例的*config*中设置`requestType: 'download'`时表示下载文件,请求适配器将会调用`Taro.downloadFile`。 + +同样的,已经完全兼容`Taro.downloadFile`,你可以在创建 method 实例的*config*中指定`Taro.downloadFile`支持的[全部配置项](https://taro-docs.jd.com/docs/apis/network/download/downloadFile),如果还有额外的参数需要设置,请在 method 实例的*config*中指定。 + + + + +```jsx +const downloadFile = filePath => + alovaInst.Get('/bigImage.jpg', { + // 设置请求方式为下载,适配器内将调用uni.downloadFile + requestType: 'download', + filePath, + + // 开启下载进度 + enableDownload: true + }); + +const App = () => { + const { loading, data, downloading, send } = useRequest(downloadFile, { + immediate: false + }); + const handleImageDownload = () => { + send('file_save_path'); + }; + + return ( + { loading ? 下载中... : null } + 下载进度:{ downloading.loaded }/{ downloading.total } + + {/* ... */} + ); +} +``` + + + + +```html + + 下载中... + 下载进度:{{ downloading.loaded }}/{{ downloading.total }} + + + + + +``` + + + + +## 模拟请求适配器兼容 + +在使用 Taro 开发应用时,我们仍然可能需要用到模拟请求,只是默认情况下,[模拟请求适配器(@alova/mock)](/next/resource/request-adapter/alova-mock)的响应数据是一个`Response`实例,即默认兼容`alova/fetch`请求适配器,当在 Taro 环境下使用时,我们需要让模拟请求适配器的响应数据是兼容 Taro 适配器的,因此你需要使用**@alova/adapter-taro**包中导出的`taroMockResponse`作为响应适配器。 + +```javascript +import { defineMock, createAlovaMockAdapter } from '@alova/mock'; +import AdapterTaro, { taroRequestAdapter, taroMockResponse } from '@alova/adapter-taro'; + +const mocks = defineMock({ + // ... +}); + +// 模拟数据请求适配器 +export default createAlovaMockAdapter([mocks], { + // 指定taro请求适配器后,未匹配模拟接口的请求将使用这个适配器发送请求 + httpAdapter: taroRequestAdapter, + + // 模拟响应适配器,指定后响应数据将转换为taro兼容的数据格式 + onMockResponse: taroMockResponse +}); + +export const alovaInst = createAlova({ + baseURL: 'https://api.alovajs.org', + timeout: 5000, + ...AdapterTaro({ + // 通过环境变量控制是否使用模拟请求适配器 + mockRequest: process.env.NODE_ENV === 'development' ? mockAdapter : undefined + }) + // ... +}); +``` + +## Typescript + +taro 请求适配器 提供了完整的类型适配,method 配置、响应数据的类型将与 taro 的类型完全匹配。 + +### method 配置 + +在创建 method 实例时,除了 method 中通用的配置项外,你还可以使用`Taro.request`、`Taro.uploadFile`和`Taro.downloadFile`中的配置项,我们已经在类型中去除了和 method 实例通用配置冲突的项。 + +```typescript +/** + * Taro.request请求额外参数 + */ +export type TaroRequestConfig = Omit< + Taro.request.Option, + 'url' | 'data' | 'header' | 'method' | 'timeout' | 'success' | 'fail' | 'complete' +>; + +/** + * Taro.uploadFile额外参数 + */ +export type TaroUploadConfig = Omit< + Taro.uploadFile.Option, + | 'url' + | 'filePath' + | 'name' + | 'header' + | 'formData' + | 'timeout' + | 'success' + | 'fail' + | 'complete' +>; + +/** + * Taro.downloadFile额外参数 + */ +export type TaroDownloadConfig = Omit< + Taro.downloadFile.Option, + 'url' | 'header' | 'timeout' | 'success' | 'fail' | 'complete' +>; + +/** + * 合并的请求配置参数 + */ +export type TaroConfig = { + /** + * 请求类型,upload表示上传,download表示下载,不填写表示正常请求 + */ + requestType?: 'upload' | 'download'; +} & TaroRequestConfig & + TaroUploadConfig & + TaroDownloadConfig; +``` + +### 响应数据 + +因为 taro 请求适配器同时兼容了`Taro.request`、`Taro.uploadFile`和`Taro.downloadFile`,但它们的响应值类型稍有不同,所以响应数据类型是这样的: + +```typescript +type TaroResponse = + // Taro.request的响应类型 + | Taro.request.SuccessCallbackResult + + // Taro.uploadFile的响应类型 + | Taro.uploadFile.SuccessCallbackResult + + // Taro.downloadFile的响应类型 + | Taro.downloadFile.FileSuccessCallbackResult; +``` + +在实际使用中,我们通常需要在全局处理响应数据,建议分开场景判断返回数据,一个简单的实例如下: + +```typescript +const alovaInst = createAlova( + baseURL: 'https://api.alovajs.org', + ...AdapterTaro(), + responded(response) { + const { statusCode, data } = response as Taro.request.SuccessCallbackResult; + if (statusCode >= 400) { + throw new Error('请求错误'); + } + return data || null; + } +}); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/05-alova-adapter-uniapp.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/05-alova-adapter-uniapp.md index 18ea8079f..40e14801a 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/05-alova-adapter-uniapp.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/05-alova-adapter-uniapp.md @@ -1,279 +1,278 @@ ---- -title: Uniapp适配器 -sidebar_position: 50 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -:::info 提示 - -此插件只支持 vue3 版本的 uniapp 应用。 - -::: - -## 安装 - - - - -```bash -npm install @alova/adapter-uniapp --save -``` - - - - -```bash -yarn add @alova/adapter-uniapp -``` - - - - -## 使用方法 - -### 创建 alova - -调用 **AdapterUniapp** 将返回*请求适配器*、_存储适配器_,以及*VueHook*,因此你不再需要设置这三个项,使用方法完全一致。 - -```javascript -import { createAlova } from 'alova'; -import AdapterUniapp from '@alova/adapter-uniapp'; - -const alovaInst = createAlova({ - baseURL: 'https://api.alovajs.org', - ...AdapterUniapp() -}); -``` - -### 请求 - -请求的使用方法与 web 环境中使用完全一致。已经完全兼容`uni.request`,你可以在创建 method 实例的*config*中指定`uni.request`支持的[全部配置项](https://uniapp.dcloud.net.cn/api/request/request.html) - -```html - - 加载中... - 请求数据为:{{ data }} - - - -``` - -当使用`useRequest/useWatcher`立即发送请求时,它将会在`onLoad`钩子中异步执行,因此你可以在`methodHandler`中访问 options 数据,访问方式如下: - -```javascript -import { onLoad } from '@dcloudio/uni-app'; - -let options = {}; -onLoad(opt => { - options = opt; -}); -const { loading, data } = useRequest(() => getDetail(options.id)); -``` - -### 上传 - -在 method 实例的*config*中设置`requestType: 'upload'`时表示上传文件,请求适配器将会调用`uni.uploadFile`,上传的文件数据放在 method 实例的 data 中,你需要在 data 中指定`name`、`filePath或files`,以及`file`(如果需要),这 4 个参数将传到`uni.uploadFile`中,同时,你还可以在 data 中指定这 4 个参数外的其他参数,请求适配器会将它们传入到`formData`参数中。 - -同样的,已经完全兼容`uni.uploadFile`,你可以在创建 method 实例的*config*中指定`uni.uploadFile`支持的[全部配置项](https://uniapp.dcloud.net.cn/api/request/network-file.html#uploadfile),如果还有额外的参数需要设置,请在 method 实例的*config*中指定。 - -```html - - 上传中... - 上传进度:{{ uploading.loaded }}/{{ uploading.total }} - - - - - -``` - -### 下载 - -在 method 实例的*config*中设置`requestType: 'download'`时表示下载文件,请求适配器将会调用`uni.downloadFile`。 - -同样的,已经完全兼容`uni.downloadFile`,你可以在创建 method 实例的*config*中指定`uni.downloadFile`支持的[全部配置项](https://uniapp.dcloud.net.cn/api/request/network-file.html#downloadfile),如果还有额外的参数需要设置,请在 method 实例的*config*中指定。 - -```html - - 下载中... - 下载进度:{{ downloading.loaded }}/{{ downloading.total }} - - - - - -``` - -## 模拟请求适配器兼容 - -在使用 uniapp 开发应用时,我们仍然可能需要用到模拟请求,只是默认情况下,[模拟请求适配器(@alova/mock)](/tutorial/request-adapter/alova-mock)的响应数据是一个`Response`实例,即默认兼容`GlobalFetch`请求适配器,当在 uniapp 环境下使用时,我们需要让模拟请求适配器的响应数据是兼容 uniapp 适配器的,因此你需要使用**@alova/adapter-uniapp**包中导出的`uniappMockResponse`作为响应适配器。 - -```javascript -import { defineMock, createAlovaMockAdapter } from '@alova/mock'; -import AdapterUniapp, { uniappRequestAdapter, uniappMockResponse } from '@alova/adapter-uniapp'; - -const mocks = defineMock({ - // ... -}); - -// 模拟数据请求适配器 -export default createAlovaMockAdapter([mocks], { - // 指定uniapp请求适配器后,未匹配模拟接口的请求将使用这个适配器发送请求 - httpAdapter: uniappRequestAdapter, - - // 模拟响应适配器,指定后响应数据将转换为uniapp兼容的数据格式 - onMockResponse: uniappMockResponse -}); - -export const alovaInst = createAlova({ - baseURL: 'https://api.alovajs.org', - timeout: 5000, - ...AdapterUniapp({ - // 通过环境变量控制是否使用模拟请求适配器 - mockRequest: process.env.NODE_ENV === 'development' ? mockAdapter : undefined - }) - // ... -}); -``` - -## Typescript - -uniapp 请求适配器提供了完整的类型适配,method 配置、响应数据的类型将与 uniapp 的类型完全匹配。 - -### method 配置 - -在创建 method 实例时,除了 method 中通用的配置项外,你还可以使用`uniapp.request`、`uniapp.uploadFile`和`uniapp.downloadFile`中的配置项,我们已经在类型中去除了和 method 实例通用配置冲突的项。 - -```typescript -/** - * uni.request请求额外参数 - */ -export type UniappRequestConfig = Omit< - UniNamespace.RequestOptions, - 'url' | 'data' | 'header' | 'method' | 'timeout' | 'success' | 'fail' | 'complete' ->; - -/** - * uni.uploadFile额外参数 - */ -export type UniappUploadConfig = Omit< - UniNamespace.UploadFileOption, - 'url' | 'name' | 'header' | 'formData' | 'timeout' | 'success' | 'fail' | 'complete' ->; - -/** - * uni.downloadFile额外参数 - */ -export type UniappDownloadConfig = Omit< - UniNamespace.DownloadFileOption, - 'url' | 'header' | 'timeout' | 'success' | 'fail' | 'complete' ->; - -/** - * 合并的请求配置参数 - */ -export type UniappConfig = { - /** - * 请求类型,upload表示上传,download表示下载,不填写表示正常请求 - */ - requestType?: 'upload' | 'download'; -} & UniappRequestConfig & - UniappUploadConfig & - UniappDownloadConfig; -``` - -### 响应数据 - -因为 uniapp 请求适配器同时兼容了`uniapp.request`、`uniapp.uploadFile`和`uniapp.downloadFile`,但它们的响应值类型稍有不同,所以响应数据类型是这样的: - -```typescript -type UniappResponse = - // uni.request的响应类型 - | UniNamespace.RequestSuccessCallbackResult - - // uni.uploadFile的响应类型 - | UniNamespace.UploadFileSuccessCallbackResult - - // uni.downloadFile的响应类型 - | UniNamespace.DownloadSuccessData; -``` - -在实际使用中,我们通常需要在全局处理响应数据,建议分开场景判断返回数据,一个简单的实例如下: - -```typescript -const alovaInst = createAlova( - baseURL: 'https://api.alovajs.org', - ...AdapterUniapp(), - responded(response) { - const { statusCode, data } = response as UniNamespace.RequestSuccessCallbackResult; - if (statusCode >= 400) { - throw new Error('请求错误'); - } - return data || null; - } -}); -``` +--- +title: Uniapp适配器 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info 提示 + +此插件只支持 vue3 版本的 uniapp 应用。 + +::: + +## 安装 + + + + +```bash +npm install @alova/adapter-uniapp --save +``` + + + + +```bash +yarn add @alova/adapter-uniapp +``` + + + + +## 使用方法 + +### 创建 alova + +调用 **AdapterUniapp** 将返回*请求适配器*、_存储适配器_,以及*VueHook*,因此你不再需要设置这三个项,使用方法完全一致。 + +```javascript +import { createAlova } from 'alova'; +import AdapterUniapp from '@alova/adapter-uniapp'; + +const alovaInst = createAlova({ + baseURL: 'https://api.alovajs.org', + ...AdapterUniapp() +}); +``` + +### 请求 + +请求的使用方法与 web 环境中使用完全一致。已经完全兼容`uni.request`,你可以在创建 method 实例的*config*中指定`uni.request`支持的[全部配置项](https://uniapp.dcloud.net.cn/api/request/request.html) + +```html + + 加载中... + 请求数据为:{{ data }} + + + +``` + +当使用`useRequest/useWatcher`立即发送请求时,它将会在`onLoad`钩子中异步执行,因此你可以在`methodHandler`中访问 options 数据,访问方式如下: + +```javascript +import { onLoad } from '@dcloudio/uni-app'; + +let options = {}; +onLoad(opt => { + options = opt; +}); +const { loading, data } = useRequest(() => getDetail(options.id)); +``` + +### 上传 + +在 method 实例的*config*中设置`requestType: 'upload'`时表示上传文件,请求适配器将会调用`uni.uploadFile`,上传的文件数据放在 method 实例的 data 中,你需要在 data 中指定`name`、`filePath或files`,以及`file`(如果需要),这 4 个参数将传到`uni.uploadFile`中,同时,你还可以在 data 中指定这 4 个参数外的其他参数,请求适配器会将它们传入到`formData`参数中。 + +同样的,已经完全兼容`uni.uploadFile`,你可以在创建 method 实例的*config*中指定`uni.uploadFile`支持的[全部配置项](https://uniapp.dcloud.net.cn/api/request/network-file.html#uploadfile),如果还有额外的参数需要设置,请在 method 实例的*config*中指定。 + +```html + + 上传中... + 上传进度:{{ uploading.loaded }}/{{ uploading.total }} + + + + + +``` + +### 下载 + +在 method 实例的*config*中设置`requestType: 'download'`时表示下载文件,请求适配器将会调用`uni.downloadFile`。 + +同样的,已经完全兼容`uni.downloadFile`,你可以在创建 method 实例的*config*中指定`uni.downloadFile`支持的[全部配置项](https://uniapp.dcloud.net.cn/api/request/network-file.html#downloadfile),如果还有额外的参数需要设置,请在 method 实例的*config*中指定。 + +```html + + 下载中... + 下载进度:{{ downloading.loaded }}/{{ downloading.total }} + + + + + +``` + +## 模拟请求适配器兼容 + +在使用 uniapp 开发应用时,我们仍然可能需要用到模拟请求,只是默认情况下,[模拟请求适配器(@alova/mock)](/next/resource/request-adapter/alova-mock)的响应数据是一个`Response`实例,即默认兼容`alova/fetch`请求适配器,当在 uniapp 环境下使用时,我们需要让模拟请求适配器的响应数据是兼容 uniapp 适配器的,因此你需要使用**@alova/adapter-uniapp**包中导出的`uniappMockResponse`作为响应适配器。 + +```javascript +import { defineMock, createAlovaMockAdapter } from '@alova/mock'; +import AdapterUniapp, { uniappRequestAdapter, uniappMockResponse } from '@alova/adapter-uniapp'; + +const mocks = defineMock({ + // ... +}); + +// 模拟数据请求适配器 +export default createAlovaMockAdapter([mocks], { + // 指定uniapp请求适配器后,未匹配模拟接口的请求将使用这个适配器发送请求 + httpAdapter: uniappRequestAdapter, + + // 模拟响应适配器,指定后响应数据将转换为uniapp兼容的数据格式 + onMockResponse: uniappMockResponse +}); + +export const alovaInst = createAlova({ + baseURL: 'https://api.alovajs.org', + timeout: 5000, + ...AdapterUniapp({ + // 通过环境变量控制是否使用模拟请求适配器 + mockRequest: process.env.NODE_ENV === 'development' ? mockAdapter : undefined + }) + // ... +}); +``` + +## Typescript + +uniapp 请求适配器提供了完整的类型适配,method 配置、响应数据的类型将与 uniapp 的类型完全匹配。 + +### method 配置 + +在创建 method 实例时,除了 method 中通用的配置项外,你还可以使用`uniapp.request`、`uniapp.uploadFile`和`uniapp.downloadFile`中的配置项,我们已经在类型中去除了和 method 实例通用配置冲突的项。 + +```typescript +/** + * uni.request请求额外参数 + */ +export type UniappRequestConfig = Omit< + UniNamespace.RequestOptions, + 'url' | 'data' | 'header' | 'method' | 'timeout' | 'success' | 'fail' | 'complete' +>; + +/** + * uni.uploadFile额外参数 + */ +export type UniappUploadConfig = Omit< + UniNamespace.UploadFileOption, + 'url' | 'name' | 'header' | 'formData' | 'timeout' | 'success' | 'fail' | 'complete' +>; + +/** + * uni.downloadFile额外参数 + */ +export type UniappDownloadConfig = Omit< + UniNamespace.DownloadFileOption, + 'url' | 'header' | 'timeout' | 'success' | 'fail' | 'complete' +>; + +/** + * 合并的请求配置参数 + */ +export type UniappConfig = { + /** + * 请求类型,upload表示上传,download表示下载,不填写表示正常请求 + */ + requestType?: 'upload' | 'download'; +} & UniappRequestConfig & + UniappUploadConfig & + UniappDownloadConfig; +``` + +### 响应数据 + +因为 uniapp 请求适配器同时兼容了`uniapp.request`、`uniapp.uploadFile`和`uniapp.downloadFile`,但它们的响应值类型稍有不同,所以响应数据类型是这样的: + +```typescript +type UniappResponse = + // uni.request的响应类型 + | UniNamespace.RequestSuccessCallbackResult + + // uni.uploadFile的响应类型 + | UniNamespace.UploadFileSuccessCallbackResult + + // uni.downloadFile的响应类型 + | UniNamespace.DownloadSuccessData; +``` + +在实际使用中,我们通常需要在全局处理响应数据,建议分开场景判断返回数据,一个简单的实例如下: + +```typescript +const alovaInst = createAlova( + baseURL: 'https://api.alovajs.org', + ...AdapterUniapp(), + responded(response) { + const { statusCode, data } = response as UniNamespace.RequestSuccessCallbackResult; + if (statusCode >= 400) { + throw new Error('请求错误'); + } + return data || null; + } +}); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/02-storage-adapter/01-psc.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/02-storage-adapter/01-psc.md index 65fc8505a..6492b427f 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/02-storage-adapter/01-psc.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/02-storage-adapter/01-psc.md @@ -1,6 +1,5 @@ --- title: 进程共享适配器 -sidebar_position: 10 --- 进程共享存储适配器可以在多进程环境中共享 Alova 的缓存。 @@ -64,16 +63,17 @@ createAlova({ 当然,同时共享 `l1Cache` 和 `l2Cache` 也完全没有问题!使用 `scope` 选项,并创建不同的共享储存适配器即可。 ```javascript -const createScopedPSCAdapter = (scope: string) => createPSCAdapter( - NodeSyncAdapter(stopIPC => { - process.on('SIGTERM', stopIPC); - }), - // 这个参数用于指定储存适配器,我们稍后会介绍 - undefined, - // highlight-start - { scope } - // highlight-end -); +const createScopedPSCAdapter = (scope: string) => + createPSCAdapter( + NodeSyncAdapter(stopIPC => { + process.on('SIGTERM', stopIPC); + }), + // 这个参数用于指定储存适配器,我们稍后会介绍 + undefined, + // highlight-start + { scope } + // highlight-end + ); createAlova({ // ... @@ -149,19 +149,22 @@ declare global { 通过传入 `createPSCAdapter` 的第二个参数,可指定使用的储存适配器。 ```typescript -const pscAdapter = createPSCAdapter(ElectronSyncAdapter( - ipcRenderer, - // highlight-start - // 用法与 createAlova 时传入到 l1Cache 一致。如果传入 undefined,那么将使用默认实现 - MyStorageAdapter() - // highlight-end -)); +const pscAdapter = createPSCAdapter( + ElectronSyncAdapter( + ipcRenderer, + // highlight-start + // 用法与 createAlova 时传入到 l1Cache 一致。如果传入 undefined,那么将使用默认实现 + MyStorageAdapter() + // highlight-end + ) +); createAlova({ // ... l1Cache: pscAdapter }); ``` + > 你还可以使用 [lru-cache](https://www.npmjs.com/package/lru-cache) 作为缓存适配器。 ## 自定义共享适配器 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/01-vue-options.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/01-vue-options.md index 16007b3da..120decd4f 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/01-vue-options.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/01-vue-options.md @@ -1,6 +1,5 @@ --- title: vue2/3 options -sidebar_position: 10 --- import Tabs from '@theme/Tabs'; @@ -49,14 +48,14 @@ alova 版本 >= 2.13.2 ```javascript import { createAlova, Method } from 'alova'; -import GlobalFetch from 'alova/GlobalFetch'; +import adapterFetch from 'alova/fetch'; import { VueOptionsHook } from '@alova/vue-options'; // api.js const alovaInst = createAlova({ baseURL: 'http://example.com', statesHook: VueOptionsHook, - requestAdapter: GlobalFetch(), + requestAdapter: adapterFetch(), responded: response => response.json() }); @@ -326,9 +325,9 @@ export const getData = () => alovaInst.Get('/todolist'); **typescript** -在 typescript 中添加响应数据类型,请阅读 [alova 文档 typescript 章节](/tutorial/combine-framework/typescript) +在 typescript 中添加响应数据类型,请阅读 [alova 文档 typescript 章节](/next/tutorial/advanced/in-depth/typescript) ## 限制 -1. 暂不支持[管理额外的状态](/tutorial/advanced/manage-extra-states)。 +1. 暂不支持[管理额外的状态](/next/tutorial/client/in-depth/manage-extra-states)。 2. 目前只支持 alova 的`useRequest/useWatcher/useFetcher`三个核心 useHook,以及你在自己项目中基于核心 useHook 的封装,暂不支持[@alova/scene](https://github.com/alovajs/scene)内的扩展 useHook。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/02-solid.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/02-solid.md index f204dd737..bb13ca954 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/02-solid.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/02-solid.md @@ -1,6 +1,5 @@ --- title: solid -sidebar_position: 20 --- 敬请期待... diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/03-angular.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/03-angular.md index 49a755cb1..9897e4695 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/03-angular.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/03-angular.md @@ -1,6 +1,5 @@ --- title: angular -sidebar_position: 30 --- 敬请期待... diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/04-native-mp.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/04-native-mp.md index b04786d56..92ac7538e 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/04-native-mp.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/04-native-mp.md @@ -1,6 +1,5 @@ --- title: 原生小程序 -sidebar_position: 40 --- 敬请期待... diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/05-preact.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/05-preact.md index 9fd875bd7..50451c180 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/05-preact.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/05-preact.md @@ -1,6 +1,5 @@ --- title: preact -sidebar_position: 50 --- 敬请期待... diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/06-qwik.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/06-qwik.md index 893e62274..f75e5460d 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/06-qwik.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/06-qwik.md @@ -1,6 +1,5 @@ --- title: qwik -sidebar_position: 60 --- 敬请期待... diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/07-lit.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/07-lit.md index 106cd17b5..4671412ea 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/07-lit.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/07-lit.md @@ -1,6 +1,5 @@ --- title: lit -sidebar_position: 70 --- 敬请期待... diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/08-stencil.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/08-stencil.md index 07a070572..acef20ed4 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/08-stencil.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/08-stencil.md @@ -1,6 +1,5 @@ --- title: stencil -sidebar_position: 80 --- 敬请期待... diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/01-init-page.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/01-vue/01-init-page.md similarity index 70% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/01-init-page.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/01-vue/01-init-page.md index aff0d9759..8a7d833b6 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/01-init-page.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/01-vue/01-init-page.md @@ -1,16 +1,15 @@ ---- -title: (vue)页面初始化请求 -sidebar_position: 10 ---- - -import InitPage from '@site/example-links/InitPage'; - -> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); - - - -:::info 示例说明 - -本示例主要展示请求的简单性,它将返回状态化的请求状态、数据等,这些状态可直接在视图中使用,并且直接由 alova 管理 - -::: +--- +title: 页面初始化请求 +--- + +import InitPage from '@site/example-links/InitPage'; + +> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/next/tutorial/getting-started/introduce); + + + +:::info 示例说明 + +本示例主要展示请求的简单性,它将返回状态化的请求状态、数据等,这些状态可直接在视图中使用,并且直接由 alova 管理 + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/02-submit-form.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/01-vue/02-submit-form.md similarity index 64% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/02-submit-form.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/01-vue/02-submit-form.md index fccbbb3b7..1f461cc78 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/02-submit-form.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/01-vue/02-submit-form.md @@ -1,16 +1,15 @@ ---- -title: (vue)表单提交 -sidebar_position: 20 ---- - -import SubmitForm from '@site/example-links/SubmitForm'; - -> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); - - - -:::info 示例说明 - -本示例主要展示基础的数据提交编码方式 - -::: +--- +title: 表单提交 +--- + +import SubmitForm from '@site/example-links/SubmitForm'; + +> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/next/tutorial/getting-started/introduce); + + + +:::info 示例说明 + +本示例主要展示基础的数据提交编码方式 + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/03-condition-search.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/01-vue/03-condition-search.md similarity index 74% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/03-condition-search.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/01-vue/03-condition-search.md index 02ea0abeb..80ac15fcc 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/03-condition-search.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/01-vue/03-condition-search.md @@ -1,14 +1,13 @@ ---- -title: (vue)条件搜索 -sidebar_position: 30 ---- - -import ConditionSearch from '@site/example-links/ConditionSearch'; - -> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); - - - -:::info 示例说明 -本示例主要展示数据搜索的简单性,无需开发者自己维护请求状态、数据等状态,也无需手动触发请求发送,只需绑定好搜索条件的 state 即可 -::: +--- +title: 条件搜索 +--- + +import ConditionSearch from '@site/example-links/ConditionSearch'; + +> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/next/tutorial/getting-started/introduce); + + + +:::info 示例说明 +本示例主要展示数据搜索的简单性,无需开发者自己维护请求状态、数据等状态,也无需手动触发请求发送,只需绑定好搜索条件的 state 即可 +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/04-memory-cache.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/01-vue/04-memory-cache.md similarity index 78% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/04-memory-cache.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/01-vue/04-memory-cache.md index c6bc2dcc5..79ec1a434 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/04-memory-cache.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/01-vue/04-memory-cache.md @@ -1,21 +1,20 @@ ---- -title: (vue)响应缓存-内存模式 -sidebar_position: 40 ---- - -import MemoryCache from '@site/example-links/MemoryCache'; - -> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); - - - -:::info 示例说明 - -内存缓存模式是将响应数据存放在内存中,缓存在刷新页面即失效。 - -_操作引导:_ - -1. 点击以下的学生列表项,将会发送请求学生详细信息,此时模态框显示 Loading 状态; -2. 点击遮罩关闭弹框,并重新打开它,此时将会命中缓存并立即显示学生详细信息,Request Records 中不再打印请求记录; - -::: +--- +title: 响应缓存-内存模式 +--- + +import MemoryCache from '@site/example-links/MemoryCache'; + +> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/next/tutorial/getting-started/introduce); + + + +:::info 示例说明 + +内存缓存模式是将响应数据存放在内存中,缓存在刷新页面即失效。 + +_操作引导:_ + +1. 点击以下的学生列表项,将会发送请求学生详细信息,此时模态框显示 Loading 状态; +2. 点击遮罩关闭弹框,并重新打开它,此时将会命中缓存并立即显示学生详细信息,Request Records 中不再打印请求记录; + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/05-storage-placeholder.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/01-vue/05-storage-placeholder.md similarity index 81% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/05-storage-placeholder.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/01-vue/05-storage-placeholder.md index 5a63eafae..60542a98b 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/05-storage-placeholder.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/01-vue/05-storage-placeholder.md @@ -1,21 +1,20 @@ ---- -title: (vue)响应缓存-缓存占位模式 -sidebar_position: 50 ---- - -import StoragePlaceholder from '@site/example-links/StoragePlaceholder'; - -> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); - - - -:::info 示例说明 - -缓存占位模式是将响应数据持久化,它将在刷新页面后立即更新到 data state 中作为占位数据,同时发送请求,开发者可以在响应前使用占位数据替代 Loading 状态。 - -_操作引导:_ - -1. 点击`Reload page`刷新页面,你不再看到 Loading 状态,而是旧数据被渲染出来了,且当请求响应后被替换为新数据; -2. 点击`Invalidate the data of placeholder`让缓存数据失效,此时你将重新看到 Loading 状态; - -::: +--- +title: 响应缓存-缓存占位模式 +--- + +import StoragePlaceholder from '@site/example-links/StoragePlaceholder'; + +> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/next/tutorial/getting-started/introduce); + + + +:::info 示例说明 + +缓存占位模式是将响应数据持久化,它将在刷新页面后立即更新到 data state 中作为占位数据,同时发送请求,开发者可以在响应前使用占位数据替代 Loading 状态。 + +_操作引导:_ + +1. 点击`Reload page`刷新页面,你不再看到 Loading 状态,而是旧数据被渲染出来了,且当请求响应后被替换为新数据; +2. 点击`Invalidate the data of placeholder`让缓存数据失效,此时你将重新看到 Loading 状态; + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/06-storage-restore.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/01-vue/06-storage-restore.md similarity index 82% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/06-storage-restore.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/01-vue/06-storage-restore.md index 3115eaa6b..7ab6801ff 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/06-storage-restore.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/01-vue/06-storage-restore.md @@ -1,21 +1,20 @@ ---- -title: (vue)响应缓存-恢复模式 -sidebar_position: 60 ---- - -import StorageRestore from '@site/example-links/StorageRestore'; - -> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); - - - -:::info 示例说明 - -缓存恢复模式是将响应数据持久化,当请求命中缓存时将持久化缓存数据返回,不再发出请求。它一般用于一些需要服务端管理,但在一定时间内不变的数据,以下是节假日信息的恢复模式示例。 - -_操作引导:_ - -1. 点击`Reload page`刷新页面,你不再看到 Loading 状态,而会使用缓存数据并立即渲染到页面中,也不再发送请求; -2. 点击`Invalidate the data of placeholder`让缓存数据失效,此时你将重新看到 Loading 状态; - -::: +--- +title: 响应缓存-恢复模式 +--- + +import StorageRestore from '@site/example-links/StorageRestore'; + +> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/next/tutorial/getting-started/introduce); + + + +:::info 示例说明 + +缓存恢复模式是将响应数据持久化,当请求命中缓存时将持久化缓存数据返回,不再发出请求。它一般用于一些需要服务端管理,但在一定时间内不变的数据,以下是节假日信息的恢复模式示例。 + +_操作引导:_ + +1. 点击`Reload page`刷新页面,你不再看到 Loading 状态,而会使用缓存数据并立即渲染到页面中,也不再发送请求; +2. 点击`Invalidate the data of placeholder`让缓存数据失效,此时你将重新看到 Loading 状态; + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/07-update-state.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/01-vue/07-update-state.md similarity index 74% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/07-update-state.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/01-vue/07-update-state.md index 5258b992e..6e9debd5f 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/07-update-state.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/01-vue/07-update-state.md @@ -1,20 +1,19 @@ ---- -title: (vue)跨组件/页面更新状态 -sidebar_position: 70 ---- - -import UpdateState from '@site/example-links/UpdateState'; - -> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); - - - -:::info 示例说明 - -当需要跨组件或页面更新状态时,常常会受组件层级的限制,这里有一个可以打破这个限制的方式。 - -_操作引导:_ - -在编辑框或编辑页中,新增或编辑列表数据后,列表数据被跨越组件层级的方式更新了。 - -::: +--- +title: 跨组件/页面更新状态 +--- + +import UpdateState from '@site/example-links/UpdateState'; + +> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/next/tutorial/getting-started/introduce); + + + +:::info 示例说明 + +当需要跨组件或页面更新状态时,常常会受组件层级的限制,这里有一个可以打破这个限制的方式。 + +_操作引导:_ + +在编辑框或编辑页中,新增或编辑列表数据后,列表数据被跨越组件层级的方式更新了。 + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/08-prefetch.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/01-vue/08-prefetch.md similarity index 77% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/08-prefetch.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/01-vue/08-prefetch.md index 152c1352c..b9b7094f6 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/08-prefetch.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/01-vue/08-prefetch.md @@ -1,21 +1,20 @@ ---- -title: (vue)数据预拉取 -sidebar_position: 90 ---- - -import Prefetch from '@site/example-links/Prefetch'; - -> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); - - - -:::info 示例说明 - -数据预拉取是一种预测用户操作行为的一种方式,来达到内容更快展示在用户面前的策略。 - -_操作引导:_ - -1. 鼠标移动到任意列表项,并停留 0.2 秒,将会在底部面板上看到详情数据的请求被发送; -2. 单击点开这个列表项,可以立即看到详情数据; - -::: +--- +title: 数据预拉取 +--- + +import Prefetch from '@site/example-links/Prefetch'; + +> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/next/tutorial/getting-started/introduce); + + + +:::info 示例说明 + +数据预拉取是一种预测用户操作行为的一种方式,来达到内容更快展示在用户面前的策略。 + +_操作引导:_ + +1. 鼠标移动到任意列表项,并停留 0.2 秒,将会在底部面板上看到详情数据的请求被发送; +2. 单击点开这个列表项,可以立即看到详情数据; + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/09-load-more.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/01-vue/09-load-more.md similarity index 74% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/09-load-more.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/01-vue/09-load-more.md index fa7d8ccdb..456a57bb1 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/09-load-more.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/01-vue/09-load-more.md @@ -1,23 +1,22 @@ ---- -title: (vue)下拉加载更多 -sidebar_position: 80 ---- - -import LoadMore from '@site/example-links/LoadMore'; - -> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); - - - -:::info 示例说明 - -使用分页策略,实现更加高性能易用的分页功能,分页相关状态自动管理、前后一页预加载、自动维护数据的新增/编辑/替换/移除,以及请求级的防抖功能。 - -_操作引导:_ - -1. 初始化完成后会预加载下一页数据,下拉翻页无需等待; -2. 添加、删除、修改列表项无需重置列表,它将自动处理成和重新请求一致的效果; - -[usePagination 文档](/tutorial/strategy/usePagination) - -::: +--- +title: 下拉加载更多 +--- + +import LoadMore from '@site/example-links/LoadMore'; + +> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/next/tutorial/getting-started/introduce); + + + +:::info 示例说明 + +使用分页策略,实现更加高性能易用的分页功能,分页相关状态自动管理、前后一页预加载、自动维护数据的新增/编辑/替换/移除,以及请求级的防抖功能。 + +_操作引导:_ + +1. 初始化完成后会预加载下一页数据,下拉翻页无需等待; +2. 添加、删除、修改列表项无需重置列表,它将自动处理成和重新请求一致的效果; + +[usePagination 文档](/next/tutorial/client/strategy/use-pagination) + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/10-paginated-list.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/01-vue/10-paginated-list.md similarity index 75% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/10-paginated-list.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/01-vue/10-paginated-list.md index 3b5b350cc..3b9cae7eb 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/10-paginated-list.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/01-vue/10-paginated-list.md @@ -1,23 +1,22 @@ ---- -title: (vue)页码列表 -sidebar_position: 100 ---- - -import Pagination from '@site/example-links/Pagination'; - -> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); - - - -:::info 示例说明 - -使用分页策略,实现更加高性能易用的分页功能,分页相关状态自动管理、前后一页预加载、自动维护数据的新增/编辑/替换/移除,以及请求级的防抖功能。 - -_操作引导:_ - -1. 初始化完成后会预加载下一页数据,翻页到下一页时无需等待; -2. 添加、删除、修改列表项无需重置列表,它将自动处理成和重新请求一致的效果; - -[usePagination 文档](/tutorial/strategy/usePagination) - -::: +--- +title: 页码列表 +--- + +import Pagination from '@site/example-links/Pagination'; + +> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/next/tutorial/getting-started/introduce); + + + +:::info 示例说明 + +使用分页策略,实现更加高性能易用的分页功能,分页相关状态自动管理、前后一页预加载、自动维护数据的新增/编辑/替换/移除,以及请求级的防抖功能。 + +_操作引导:_ + +1. 初始化完成后会预加载下一页数据,翻页到下一页时无需等待; +2. 添加、删除、修改列表项无需重置列表,它将自动处理成和重新请求一致的效果; + +[usePagination 文档](/next/tutorial/client/strategy/use-pagination) + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/11-controlled-cache-by-indexeddb.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/01-vue/11-controlled-cache-by-indexeddb.md similarity index 74% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/11-controlled-cache-by-indexeddb.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/01-vue/11-controlled-cache-by-indexeddb.md index 58b630c8d..79a6db4bd 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/11-controlled-cache-by-indexeddb.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/01-vue/11-controlled-cache-by-indexeddb.md @@ -1,23 +1,22 @@ ---- -title: (vue)使用IndexedDB管理缓存 -sidebar_position: 110 ---- - -import ControlledCacheByIndexedDB from '@site/example-links/ControlledCacheByIndexedDB'; - -> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); - - - -:::info 示例说明 - -使用受控缓存让开发者自定义管理缓存,在大文件缓存下,可以配合 IndexedDB 管理本地缓存。 - -_操作引导:_ - -1. 选择其中一张图片,图片会先请求网络加载,图片数据将会保存在本地 IndexedDB 中; -2. 刷新页面,再次选择相同的图片,图片将在 IndexedDB 中获取数据,而不再发起网络请求; - -[受控缓存文档](/tutorial/cache/controlled-cache) - -::: +--- +title: 使用IndexedDB管理缓存 +--- + +import ControlledCacheByIndexedDB from '@site/example-links/ControlledCacheByIndexedDB'; + +> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/next/tutorial/getting-started/introduce); + + + +:::info 示例说明 + +使用受控缓存让开发者自定义管理缓存,在大文件缓存下,可以配合 IndexedDB 管理本地缓存。 + +_操作引导:_ + +1. 选择其中一张图片,图片会先请求网络加载,图片数据将会保存在本地 IndexedDB 中; +2. 刷新页面,再次选择相同的图片,图片将在 IndexedDB 中获取数据,而不再发起网络请求; + +[受控缓存文档](/next/tutorial/cache/controlled-cache) + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/13-silent-submit-simple-list.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/01-vue/13-silent-submit-simple-list.md similarity index 72% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/13-silent-submit-simple-list.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/01-vue/13-silent-submit-simple-list.md index d9df67255..3c92e1a7c 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/13-silent-submit-simple-list.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/01-vue/13-silent-submit-simple-list.md @@ -1,23 +1,22 @@ ---- -title: (vue)静默提交-简单列表 -sidebar_position: 130 ---- - -import SimpleListSilentVue from '@site/example-links/SimpleListSilentVue'; - -> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); - - - -:::info 示例说明 - -使用静默提交策略实现的简单列表,提交即响应,大幅降低网络波动造成的影响,让你的应用在网络不稳定,甚至断网状态下依然非常流畅。 - -_操作引导:_ - -1. 新增、编辑、删除列表项,它将立即产生反馈,而不需要等待服务端响应; -2. 切换请求模式和网络状态,体验它们的区别; - -[静默提交策略文档](/tutorial/strategy/sensorless-data-interaction) - -::: +--- +title: 静默提交-简单列表 +--- + +import SimpleListSilentVue from '@site/example-links/SimpleListSilentVue'; + +> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/next/tutorial/getting-started/introduce); + + + +:::info 示例说明 + +使用静默提交策略实现的简单列表,提交即响应,大幅降低网络波动造成的影响,让你的应用在网络不稳定,甚至断网状态下依然非常流畅。 + +_操作引导:_ + +1. 新增、编辑、删除列表项,它将立即产生反馈,而不需要等待服务端响应; +2. 切换请求模式和网络状态,体验它们的区别; + +[静默提交策略文档](/next/tutorial/client/strategy/sensorless-data-interaction) + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/19-serial-request.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/01-vue/19-serial-request.md similarity index 52% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/19-serial-request.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/01-vue/19-serial-request.md index cd05f8509..4740f1e2b 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/19-serial-request.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/01-vue/19-serial-request.md @@ -1,19 +1,18 @@ ---- -title: (vue)串行请求hook -sidebar_position: 190 ---- - -import SerialRequest from '@site/example-links/SerialRequest'; - -> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); - - - -:::info 示例说明 - -本示例主要通过`useSerialRequest`展示更优雅地进行串行请求。 - -[useSerialRequest](/tutorial/strategy/useSerialRequest) -[useSerialWatcher](/tutorial/strategy/useSerialWatcher) - -::: +--- +title: 串行请求hook +--- + +import SerialRequest from '@site/example-links/SerialRequest'; + +> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/next/tutorial/getting-started/introduce); + + + +:::info 示例说明 + +本示例主要通过`useSerialRequest`展示更优雅地进行串行请求。 + +[useSerialRequest](/next/tutorial/client/strategy/use-serial-request) +[useSerialWatcher](/next/tutorial/client/strategy/use-serial-watcher) + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/14-silent-submit-notes.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/02-react/14-silent-submit-notes.md similarity index 71% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/14-silent-submit-notes.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/02-react/14-silent-submit-notes.md index ec3130abe..360a3a29d 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/14-silent-submit-notes.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/02-react/14-silent-submit-notes.md @@ -1,23 +1,22 @@ ---- -title: (react)静默提交-笔记本 -sidebar_position: 140 ---- - -import NoteSilentReact from '@site/example-links/NoteSilentReact'; - -> 示例以 react 为例,但你还可以在 vue3、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); - - - -:::info 示例说明 - -使用静默提交策略实现的简单列表,提交即响应,大幅降低网络波动造成的影响,让你的应用在网络不稳定,甚至断网状态下依然非常流畅。 - -_操作引导:_ - -1. 新增、编辑、删除笔记,它将立即产生反馈,而不需要等待服务端响应; -2. 切换请求模式和网络状态,体验它们的区别; - -[静默提交策略文档](/tutorial/strategy/sensorless-data-interaction) - -::: +--- +title: 静默提交-笔记本 +--- + +import NoteSilentReact from '@site/example-links/NoteSilentReact'; + +> 示例以 react 为例,但你还可以在 vue3、svelte 中使用 alova,详细请阅读 [入门指南](/next/tutorial/getting-started/introduce); + + + +:::info 示例说明 + +使用静默提交策略实现的简单列表,提交即响应,大幅降低网络波动造成的影响,让你的应用在网络不稳定,甚至断网状态下依然非常流畅。 + +_操作引导:_ + +1. 新增、编辑、删除笔记,它将立即产生反馈,而不需要等待服务端响应; +2. 切换请求模式和网络状态,体验它们的区别; + +[静默提交策略文档](/next/tutorial/client/strategy/sensorless-data-interaction) + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/15-form-hook.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/02-react/15-form-hook.md similarity index 61% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/15-form-hook.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/02-react/15-form-hook.md index 253e94e36..e3b9180bf 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/15-form-hook.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/02-react/15-form-hook.md @@ -1,18 +1,17 @@ ---- -title: (react)表单提交策略 -sidebar_position: 150 ---- - -import FormHook from '@site/example-links/FormHook'; - -> 示例以 react 为例,但你还可以在 vue3、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); - - - -:::info 示例说明 - -本示例分别提供了表单数据持久化、表单编辑、多模块表单、表单筛选数据 4 个部分,可分别尝试体验。 - -[表单提交策略文档](/tutorial/strategy/useForm) - -::: +--- +title: 表单提交策略 +--- + +import FormHook from '@site/example-links/FormHook'; + +> 示例以 react 为例,但你还可以在 vue3、svelte 中使用 alova,详细请阅读 [入门指南](/next/tutorial/getting-started/introduce); + + + +:::info 示例说明 + +本示例分别提供了表单数据持久化、表单编辑、多模块表单、表单筛选数据 4 个部分,可分别尝试体验。 + +[表单提交策略文档](/next/tutorial/client/strategy/use-form) + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/16-captcha-send.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/02-react/16-captcha-send.md similarity index 54% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/16-captcha-send.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/02-react/16-captcha-send.md index 47e28292b..708f8c887 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/16-captcha-send.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/02-react/16-captcha-send.md @@ -1,18 +1,17 @@ ---- -title: (react)发送验证码 -sidebar_position: 160 ---- - -import CaptchaSend from '@site/example-links/CaptchaSend'; - -> 示例以 react 为例,但你还可以在 vue3、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); - - - -:::info 示例说明 - -本示例展示便捷地实现验证码发送功能。 - -[验证码发送策略文档](/tutorial/strategy/useCaptcha) - -::: +--- +title: 发送验证码 +--- + +import CaptchaSend from '@site/example-links/CaptchaSend'; + +> 示例以 react 为例,但你还可以在 vue3、svelte 中使用 alova,详细请阅读 [入门指南](/next/tutorial/getting-started/introduce); + + + +:::info 示例说明 + +本示例展示便捷地实现验证码发送功能。 + +[验证码发送策略文档](/next/tutorial/client/strategy/use-captcha) + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/17-retriable-hook.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/02-react/17-retriable-hook.md similarity index 60% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/17-retriable-hook.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/02-react/17-retriable-hook.md index 7f5145279..32cedf7dc 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/17-retriable-hook.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/02-react/17-retriable-hook.md @@ -1,18 +1,17 @@ ---- -title: (react)请求重试/轮询请求 -sidebar_position: 170 ---- - -import RetriableHook from '@site/example-links/RetriableHook'; - -> 示例以 react 为例,但你还可以在 vue3、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); - - - -:::info 示例说明 - -本示例分别提供了表单数据持久化、表单编辑、多模块表单、表单筛选数据 4 个部分,可分别尝试体验。 - -[请求重试策略文档](/tutorial/strategy/useRetriableRequest) - -::: +--- +title: 请求重试/轮询请求 +--- + +import RetriableHook from '@site/example-links/RetriableHook'; + +> 示例以 react 为例,但你还可以在 vue3、svelte 中使用 alova,详细请阅读 [入门指南](/next/tutorial/getting-started/introduce); + + + +:::info 示例说明 + +本示例分别提供了表单数据持久化、表单编辑、多模块表单、表单筛选数据 4 个部分,可分别尝试体验。 + +[请求重试策略文档](/next/tutorial/client/strategy/use-retriable-request) + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/18.action-delegation-middleware.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/02-react/18.action-delegation-middleware.md similarity index 64% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/18.action-delegation-middleware.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/02-react/18.action-delegation-middleware.md index e8226402d..1c04253a5 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/18.action-delegation-middleware.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/02-react/18.action-delegation-middleware.md @@ -1,18 +1,17 @@ ---- -title: (react)跨组件触发请求 -sidebar_position: 180 ---- - -import ActionDelegationMiddleware from '@site/example-links/ActionDelegationMiddleware'; - -> 示例以 react 为例,但你还可以在 vue3、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); - - - -:::info 示例说明 - -本示例主要通过`actionDelegationMiddleware`中间件展示,在不使用全局状态管理的情况下,跨越任意层级组件服务端数据的刷新。 - -[操作函数委托中间件文档](/tutorial/strategy/actionDelegationMiddleware) - -::: +--- +title: 跨组件触发请求 +--- + +import ActionDelegationMiddleware from '@site/example-links/ActionDelegationMiddleware'; + +> 示例以 react 为例,但你还可以在 vue3、svelte 中使用 alova,详细请阅读 [入门指南](/next/tutorial/getting-started/introduce); + + + +:::info 示例说明 + +本示例主要通过`actionDelegationMiddleware`中间件展示,在不使用全局状态管理的情况下,跨越任意层级组件服务端数据的刷新。 + +[操作函数委托中间件文档](/next/tutorial/client/strategy/action-delegation-middleware) + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/12-silent-submit-setting.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/03-svelte/12-silent-submit-setting.md similarity index 70% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/12-silent-submit-setting.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/03-svelte/12-silent-submit-setting.md index e3f4af9c8..c2c96bcd7 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/12-silent-submit-setting.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/03-svelte/12-silent-submit-setting.md @@ -1,23 +1,22 @@ ---- -title: (svelte)静默提交-设置页 -sidebar_position: 120 ---- - -import SettingSilentSvelte from '@site/example-links/SettingSilentSvelte'; - -> 示例以 svelte 为例,但你还可以在 react、vue 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); - - - -:::info 示例说明 - -使用静默提交策略,提交即响应,大幅降低网络波动造成的影响,让你的应用在网络不稳定,甚至断网状态下依然非常流畅。 - -_操作引导:_ - -1. 操作设置项,它将立即产生反馈,而不需要等待服务端响应; -2. 切换请求模式和网络状态,体验它们的区别; - -[静默提交策略文档](/tutorial/strategy/sensorless-data-interaction) - -::: +--- +title: 静默提交-设置页 +--- + +import SettingSilentSvelte from '@site/example-links/SettingSilentSvelte'; + +> 示例以 svelte 为例,但你还可以在 react、vue 中使用 alova,详细请阅读 [入门指南](/next/tutorial/getting-started/introduce); + + + +:::info 示例说明 + +使用静默提交策略,提交即响应,大幅降低网络波动造成的影响,让你的应用在网络不稳定,甚至断网状态下依然非常流畅。 + +_操作引导:_ + +1. 操作设置项,它将立即产生反馈,而不需要等待服务端响应; +2. 切换请求模式和网络状态,体验它们的区别; + +[静默提交策略文档](/next/tutorial/client/strategy/sensorless-data-interaction) + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/_category_.json b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/_category_.json deleted file mode 100644 index a03cd40f2..000000000 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/01-example/_category_.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "label": "示例", - "link": { - "type": "generated-index", - "description": "这里有丰富的示例,来展示alova在不同请求场景下的表现,示例运行前会先安装依赖,要稍微等待一会儿!" - } -} diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/README.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/01-introduce.md similarity index 92% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/README.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/01-introduce.md index 2acc89ac9..c833acff3 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/README.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/01-introduce.md @@ -1,11 +1,13 @@ --- -title: alova 是什么 +title: 介绍alova --- import Link from '@docusaurus/Link'; import NavCard from '@site/src/components/NavCard'; import SupportList from '@site/src/components/SupportList'; +## alova 是什么? + alova 是一个创新的下一代请求工具,从前后端协作和 API 消费作为出发点,将 API 的消费从 7 步简化为只有 1 步,帮你在请求方面省去大部分的工作,让网络请求变得非常简单。我们来看看 alova 是如何帮你的简化工作的。 ![](/img/overview_flow_cn.png) @@ -32,7 +34,7 @@ const { loading, error, data, page, pageSize, total } = usePagination((page, siz ); ``` -alova 提供了 10+个基于[RSM](/tutorial/others/RSM)规范的请求策略模块,它们以 useHook 的形式实现。 +alova 提供了 10+个基于[RSM](/about/RSM)规范的请求策略模块,它们以 useHook 的形式实现。 ### alova 编辑器扩展 @@ -40,7 +42,7 @@ alova 提供了 10+个基于[RSM](/tutorial/others/RSM)规范的请求策略模 这个扩展也优化了 API 的消费流程,感受不一样的 API 使用体验,在过去,你需要先查询 API 文档,并不断地在 API 文档与编辑器切换来编写请求代码,使用 alova 插件后,你可以不再需要离开编辑器,直接在编辑器中边查边使用 API。 -> 关于 alova 插件的详细介绍,请参考 [集成编辑器扩展](/tutorial/getting-started/extension-integration)。 +> 关于 alova 插件的详细介绍,请参考 [集成编辑器扩展](/next/tutorial/getting-started/extension-integration)。 ## 有什么不同吗? @@ -58,7 +60,7 @@ alova 提供了 10+个基于[RSM](/tutorial/others/RSM)规范的请求策略模 :::info 对比 -你还可以查看请[与其他请求库比较](/tutorial/others/comparison)详细了解 alova 的不同之处。 +你还可以查看请[与其他请求库比较](/about/comparison)详细了解 alova 的不同之处。 ::: @@ -117,7 +119,7 @@ target: '__blank' ## 欢迎参与贡献 -在参与贡献前,请务必详细阅读 [贡献指南](/contributing/overview),以保证你的有效贡献。 +在参与贡献前,请务必详细阅读 [贡献指南](/next/contributing/overview),以保证你的有效贡献。 ## 开始 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/02-quick-start.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/02-quick-start.md index cd1deeacc..39fa57b50 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/02-quick-start.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/02-quick-start.md @@ -1,6 +1,5 @@ --- title: 快速开始 -sidebar_position: 20 --- import Tabs from '@theme/Tabs'; @@ -12,7 +11,7 @@ import quickStartPOST from '!!raw-loader!@site/codesandbox/01-getting-started/02 :::tip 示例提示 -如果你还未了解 alova,推荐你先阅读 [alova 概述](/tutorial/getting-started)。 +如果你还未了解 alova,推荐你先阅读 [alova 介绍](/next/tutorial/getting-started/introduce)。 ::: @@ -49,8 +48,6 @@ bun add alova
-> 你也可以[通过 CDN 使用 alova](/tutorial/others/use-in-static) - ## 创建 alova 实例 在 alova 中需要通过 alova 实例发起请求,我们先创建一个。在创建 alova 实例时需要指定请求适配器,在这里推荐使用`alova/fetch`请求适配器,它是基于`fetch API`的封装,非常简洁。 @@ -79,7 +76,7 @@ const alova = createAlova({ }); ``` -> 在 nodejs 中使用 fetchAdapter 时,nodejs 版本要求`v17.5`,或者你可以使用[axios 请求适配器](/tutorial/request-adapter/alova-adapter-axios/)。 +> 在 nodejs 中使用 fetchAdapter 时,nodejs 版本要求`v17.5`,或者你可以使用[axios 请求适配器](/next/resource/request-adapter/alova-adapter-axios/)。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/03-method.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/03-basic/03-method.md similarity index 92% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/03-method.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/03-basic/03-method.md index 746cc8158..859078b18 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/03-method.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/03-basic/03-method.md @@ -1,315 +1,314 @@ ---- -title: method详解 -sidebar_position: 30 ---- - -在上一个章节中我们尝试发送了请求,获取响应数据。实际上,`alovaInstance.Get(...)`并不是一个发起请求的函数,而是创建了一个 method 实例,它是一个 PromiseLike 实例,你可以通过`then、catch、finally`方法或`await`发送请求,就像 Promise 对象一样。 - -```javascript -const userMethodInstance = alovaInstance.Get('/api/user'); - -userMethodInstance.then(response => { - // ... -}); - -userMethodInstance.catch(error => { - // ... -}); - -userMethodInstance.finally(() => { - // ... -}); - -try { - await userMethodInstance; -} catch (error) { - // ... -} finally { - // ... -} -``` - -简便写法: - -```javascript -const response = await alovaInstance.Get('/api/user'); -``` - -每个 method 实例描述了每个请求的类型、请求 url、请求头、请求参数等。此外,你还可以在 method 实例上定义请求行为,来控制 method 以什么方式处理请求。 - -## 请求类型 - -alova 共提供了 GET、POST、PUT、DELETE、HEAD、OPTIONS、PATCH 7 种请求类型。 - -| 实例创建函数 | 参数 | -| ------------ | --------------------------------------------- | -| GET | `alovaInstance.Get(url[, config])` | -| POST | `alovaInstance.Post(url[, data[, config]])` | -| PUT | `alovaInstance.Put(url[, data[, config]])` | -| DELETE | `alovaInstance.Delete(url[, data[, config]])` | -| HEAD | `alovaInstance.Head(url[, config])` | -| OPTIONS | `alovaInstance.Options(url[, config])` | -| PATCH | `alovaInstance.Patch(url[, data[, config]])` | - -参数说明: - -- `url`是请求路径; -- `data`为请求体数据; -- `config`为请求配置对象,其中包含了请求头、params 参数等、请求行为参数等配置; - -你也可以自定义创建 method 实例,这在动态指定请求类型时很有用。 - -```javascript -import { Method } from 'alova'; - -const method = new Method('GET', alovaInstance, '/api/users', { - params: { - ID: 1 - } -}); -``` - -接下来我们先来看下如何定义请求参数,你应该会觉得很熟悉。 - -## 请求参数 - -### URL 参数 - -通过 params 传入 URL 参数,params 参数会在 url 后面以?的形式拼接。 - -```javascript -alovaInstance.Get('/todo/list', { - params: { - userId: 1 - } -}); -``` - -当然,你也可以直接拼接在 url 后面,效果是相同的。 - -```javascript -alovaInstance.Get('/todo/list?userId=1'); -``` - -### 请求体 - -当发送 **POST、PUT、DELETE、PATCH 请求** 时可以通过请求体发送数据,此时第二个参数传入的是请求体,值得注意的是,POST 请求也可以传入 params 参数。 - -```javascript -alovaInstance.Post( - '/todo', - // 第二个参数是请求体 - { - title: 'test todo', - time: '12:00' - }, - // 第三个参数是配置 - { - params: { - userId: 1 - } - } -); -``` - -### 请求头 - -通过 headers 指定请求头。 - -```javascript -alovaInstance.Get('/user', { - headers: { - 'Content-Type': 'application/json;charset=UTF-8' - } -}); -``` - -### 其他请求适配器支持的参数 - -除了请求头、params 参数等外,还支持配置对应请求适配器支持的参数,当使用`alova/fetch`作为 alova 的请求适配器时,就可以在 method 实例上配置任何`fetch API`支持的参数,这些参数会在请求时传给`fetch`函数。 - -```javascript -alovaInstance.Get('/todo/list', { - // ... - // highlight-start - credentials: 'same-origin', - referrerPolicy: 'no-referrer', - mode: 'cors' - // highlight-end -}); -``` - -以上 method 实例在通过`fetch`发送请求时,将会以以下参数请求。 - -```javascript -fetch('/todo/list', { - // ... - // highlight-start - credentials: 'same-origin', - referrerPolicy: 'no-referrer', - mode: 'cors' - // highlight-end -}); -``` - -> 请求体除了可以传递 Object,还能传递请求适配器支持的请求体参数,例如 `alova/fetch` 支持传递`string | FormData | Blob | ArrayBuffer | URLSearchParams | ReadableStream`参数。 - -如果你使用了其他的请求适配器,也可以传递它们支持的参数。 - -## 请求行为 - -在[RSM](/tutorial/others/RSM)中,请求行为用于描述将以怎样的方式处理请求。 - -### 超时时间 - -设置请求超时时间。 - -```javascript -// 请求级别的请求超时时间 -alovaInstance.Get('/todo/list', { - // ... - // highlight-start - timeout: 10000 - // highlight-end -}); -``` - -### 请求共享 - -我们总会遇到这种情况,当一个请求发出但还未响应时,又发起了相同请求,就造成了请求浪费,或者重复提交问题,例如以下三种场景: - -1. 一个组件在创建时会获取初始化数据,当一个页面同时渲染多个此组件时,将会同时发出多次相同请求; -2. 提交按钮未被禁用,用户点击了多次提交按钮; -3. 当预加载还未完成时进入了预加载页面,将会发起多次相同请求; -4. 在 react 的 StrictMode 下防止重复发送请求; - -共享请求就是用来解决这些问题的,它不仅可以提升应用流畅性,还能降低服务端压力。 - -```mermaid -flowchart LR - classDef response fill:#a8bcff - R1[请求1] --> S1[发送请求] --> W1[等待响应]:::response --> RE1[接收数据1] - R2[与请求1相同的请求] --> W1[等待响应]:::response --> RE2[接收数据1] -``` - -请求共享默认开启,如果你希望在特定请求上关闭共享请求,可以这样做: - -```javascript -alovaInst.Get('/todo', { - // ... - // highlight-start - shareRequest: false - // highlight-end -}); -``` - -:::warning 如何识别相同请求 - -通过 method 实例的请求方法、请求 url、请求头、url 参数、请求体组合作为唯一标识,标识相同即表示为相同请求,而不是对比 method 实例的引用地址。 - -::: - -### 转换响应数据 - -有时候我们需要统一转换响应数据,我们可以为 method 实例设置`transformData`函数将响应数据转换成需要的结构。 - -```javascript -alovaInstance.Get('/todo/list', { - // 函数接受响应数据和响应头数据,并要求将转换后的数据返回。 - transformData(rawData, headers) { - return rawData.list.map(item => { - return { - ...item, - statusText: item.done ? '已完成' : '进行中' - }; - }); - } -}); -``` - -### 响应缓存 - -响应缓存让你可以更好地多次利用服务端数据,而不需要每次请求时都发送请求获取数据。GET 请求将默认设置 5 分钟的内存缓存时间,如果你不需要可以通过以下方式关闭当前请求的缓存。 - -```ts -alovaInstance.Get('/todo/list', { - // 设置为0或者null关闭默认的响应缓存 - cacheFor: 0 -}); -``` - -详细内容可参考[响应缓存](/tutorial/cache/mode) - -## 中断请求 - -调用 method 实例的`abort`中断请求。 - -```javascript -const userMethod = alovaInstance.Get('/api/user'); -userMethod.then(res => { - // ... -}); - -const handleCancel = () => { - // highlight-start - userMethod.abort(); - // highlight-end -}; -``` - -## 监听上传下载进度 - -通过 method 实例的`onUpload`绑定上传进度事件,`onDownload`绑定下载进度事件,它将返回解绑函数。 - -```javascript -const uploadMethod = alovaInstance.Post('/todo/uploadfile', formData); -const offUploadEvent = uploadMethod.onUpload(event => { - console.log('文件大小:',event.total); - console.log('已上传:',event.loaded); -}); - -uploadMethod.then(res => { - // ... -}); - -// 解绑上传回调 -const handleOffEvent = () => { - offUploadEvent(); -}; -``` - -```javascript -const downloadMethod = alovaInstance.Get('/todo/downloadfile'); -const offDownloadEvent = downloadMethod.onDownload(event => { - console.log('文件大小:',event.total); - console.log('已下载:',event.loaded); -}); - -downloadMethod.then(res => { - // ... -}); - -// 解绑下载回调 -const handleOffEvent = () => { - offDownloadEvent(); -}; -``` - -:::warning 使用`alova/fetch`适配器需注意 - -因 fetch api 限制,alova 提供的 `alova/fetch` 适配器不支持上传进度,如果需要上传进度,请使用[XMLHttpRequest 适配器](/tutorial/request-adapter/alova-adapter-xhr)或[axios 适配器](/tutorial/request-adapter/alova-adapter-axios)。 - -也可以自行编写请求适配器,详见 [编写请求适配器](/tutorial/custom/custom-http-adapter)。 - -::: - -**上传/下载状态类型** - -```typescript -type Progress = { - /** 上传或下载的数据总数据量 */ - total: number; - /** 已完成的数据 */ - loaded: number; -}; -``` +--- +title: method详解 +--- + +在上一个章节中我们尝试发送了请求,获取响应数据。实际上,`alovaInstance.Get(...)`并不是一个发起请求的函数,而是创建了一个 method 实例,它是一个 PromiseLike 实例,你可以通过`then、catch、finally`方法或`await`发送请求,就像 Promise 对象一样。 + +```javascript +const userMethodInstance = alovaInstance.Get('/api/user'); + +userMethodInstance.then(response => { + // ... +}); + +userMethodInstance.catch(error => { + // ... +}); + +userMethodInstance.finally(() => { + // ... +}); + +try { + await userMethodInstance; +} catch (error) { + // ... +} finally { + // ... +} +``` + +简便写法: + +```javascript +const response = await alovaInstance.Get('/api/user'); +``` + +每个 method 实例描述了每个请求的类型、请求 url、请求头、请求参数等。此外,你还可以在 method 实例上定义请求行为,来控制 method 以什么方式处理请求。 + +## 请求类型 + +alova 共提供了 GET、POST、PUT、DELETE、HEAD、OPTIONS、PATCH 7 种请求类型。 + +| 实例创建函数 | 参数 | +| ------------ | --------------------------------------------- | +| GET | `alovaInstance.Get(url[, config])` | +| POST | `alovaInstance.Post(url[, data[, config]])` | +| PUT | `alovaInstance.Put(url[, data[, config]])` | +| DELETE | `alovaInstance.Delete(url[, data[, config]])` | +| HEAD | `alovaInstance.Head(url[, config])` | +| OPTIONS | `alovaInstance.Options(url[, config])` | +| PATCH | `alovaInstance.Patch(url[, data[, config]])` | + +参数说明: + +- `url`是请求路径; +- `data`为请求体数据; +- `config`为请求配置对象,其中包含了请求头、params 参数等、请求行为参数等配置; + +你也可以自定义创建 method 实例,这在动态指定请求类型时很有用。 + +```javascript +import { Method } from 'alova'; + +const method = new Method('GET', alovaInstance, '/api/users', { + params: { + ID: 1 + } +}); +``` + +接下来我们先来看下如何定义请求参数,你应该会觉得很熟悉。 + +## 请求参数 + +### URL 参数 + +通过 params 传入 URL 参数,params 参数会在 url 后面以?的形式拼接。 + +```javascript +alovaInstance.Get('/todo/list', { + params: { + userId: 1 + } +}); +``` + +当然,你也可以直接拼接在 url 后面,效果是相同的。 + +```javascript +alovaInstance.Get('/todo/list?userId=1'); +``` + +### 请求体 + +当发送 **POST、PUT、DELETE、PATCH 请求** 时可以通过请求体发送数据,此时第二个参数传入的是请求体,值得注意的是,POST 请求也可以传入 params 参数。 + +```javascript +alovaInstance.Post( + '/todo', + // 第二个参数是请求体 + { + title: 'test todo', + time: '12:00' + }, + // 第三个参数是配置 + { + params: { + userId: 1 + } + } +); +``` + +### 请求头 + +通过 headers 指定请求头。 + +```javascript +alovaInstance.Get('/user', { + headers: { + 'Content-Type': 'application/json;charset=UTF-8' + } +}); +``` + +### 其他请求适配器支持的参数 + +除了请求头、params 参数等外,还支持配置对应请求适配器支持的参数,当使用`alova/fetch`作为 alova 的请求适配器时,就可以在 method 实例上配置任何`fetch API`支持的参数,这些参数会在请求时传给`fetch`函数。 + +```javascript +alovaInstance.Get('/todo/list', { + // ... + // highlight-start + credentials: 'same-origin', + referrerPolicy: 'no-referrer', + mode: 'cors' + // highlight-end +}); +``` + +以上 method 实例在通过`fetch`发送请求时,将会以以下参数请求。 + +```javascript +fetch('/todo/list', { + // ... + // highlight-start + credentials: 'same-origin', + referrerPolicy: 'no-referrer', + mode: 'cors' + // highlight-end +}); +``` + +> 请求体除了可以传递 Object,还能传递请求适配器支持的请求体参数,例如 `alova/fetch` 支持传递`string | FormData | Blob | ArrayBuffer | URLSearchParams | ReadableStream`参数。 + +如果你使用了其他的请求适配器,也可以传递它们支持的参数。 + +## 请求行为 + +在[RSM](/about/RSM)中,请求行为用于描述将以怎样的方式处理请求。 + +### 超时时间 + +设置请求超时时间。 + +```javascript +// 请求级别的请求超时时间 +alovaInstance.Get('/todo/list', { + // ... + // highlight-start + timeout: 10000 + // highlight-end +}); +``` + +### 请求共享 + +我们总会遇到这种情况,当一个请求发出但还未响应时,又发起了相同请求,就造成了请求浪费,或者重复提交问题,例如以下三种场景: + +1. 一个组件在创建时会获取初始化数据,当一个页面同时渲染多个此组件时,将会同时发出多次相同请求; +2. 提交按钮未被禁用,用户点击了多次提交按钮; +3. 当预加载还未完成时进入了预加载页面,将会发起多次相同请求; +4. 在 react 的 StrictMode 下防止重复发送请求; + +共享请求就是用来解决这些问题的,它不仅可以提升应用流畅性,还能降低服务端压力。 + +```mermaid +flowchart LR + classDef response fill:#a8bcff + R1[请求1] --> S1[发送请求] --> W1[等待响应]:::response --> RE1[接收数据1] + R2[与请求1相同的请求] --> W1[等待响应]:::response --> RE2[接收数据1] +``` + +请求共享默认开启,如果你希望在特定请求上关闭共享请求,可以这样做: + +```javascript +alovaInst.Get('/todo', { + // ... + // highlight-start + shareRequest: false + // highlight-end +}); +``` + +:::warning 如何识别相同请求 + +通过 method 实例的请求方法、请求 url、请求头、url 参数、请求体组合作为唯一标识,标识相同即表示为相同请求,而不是对比 method 实例的引用地址。 + +::: + +### 转换响应数据 + +有时候我们需要统一转换响应数据,我们可以为 method 实例设置`transformData`函数将响应数据转换成需要的结构。 + +```javascript +alovaInstance.Get('/todo/list', { + // 函数接受响应数据和响应头数据,并要求将转换后的数据返回。 + transformData(rawData, headers) { + return rawData.list.map(item => { + return { + ...item, + statusText: item.done ? '已完成' : '进行中' + }; + }); + } +}); +``` + +### 响应缓存 + +响应缓存让你可以更好地多次利用服务端数据,而不需要每次请求时都发送请求获取数据。GET 请求将默认设置 5 分钟的内存缓存时间,如果你不需要可以通过以下方式关闭当前请求的缓存。 + +```ts +alovaInstance.Get('/todo/list', { + // 设置为0或者null关闭默认的响应缓存 + cacheFor: 0 +}); +``` + +详细内容可参考[响应缓存](/next/tutorial/cache/mode) + +## 中断请求 + +调用 method 实例的`abort`中断请求。 + +```javascript +const userMethod = alovaInstance.Get('/api/user'); +userMethod.then(res => { + // ... +}); + +const handleCancel = () => { + // highlight-start + userMethod.abort(); + // highlight-end +}; +``` + +## 监听上传下载进度 + +通过 method 实例的`onUpload`绑定上传进度事件,`onDownload`绑定下载进度事件,它将返回解绑函数。 + +```javascript +const uploadMethod = alovaInstance.Post('/todo/uploadfile', formData); +const offUploadEvent = uploadMethod.onUpload(event => { + console.log('文件大小:',event.total); + console.log('已上传:',event.loaded); +}); + +uploadMethod.then(res => { + // ... +}); + +// 解绑上传回调 +const handleOffEvent = () => { + offUploadEvent(); +}; +``` + +```javascript +const downloadMethod = alovaInstance.Get('/todo/downloadfile'); +const offDownloadEvent = downloadMethod.onDownload(event => { + console.log('文件大小:',event.total); + console.log('已下载:',event.loaded); +}); + +downloadMethod.then(res => { + // ... +}); + +// 解绑下载回调 +const handleOffEvent = () => { + offDownloadEvent(); +}; +``` + +:::warning 使用`alova/fetch`适配器需注意 + +因 fetch api 限制,alova 提供的 `alova/fetch` 适配器不支持上传进度,如果需要上传进度,请使用[XMLHttpRequest 适配器](/next/resource/request-adapter/alova-adapter-xhr)或[axios 适配器](/next/resource/request-adapter/alova-adapter-axios)。 + +也可以自行编写请求适配器,详见 [编写请求适配器](/next/tutorial/advanced/custom/http-adapter)。 + +::: + +**上传/下载状态类型** + +```typescript +type Progress = { + /** 上传或下载的数据总数据量 */ + total: number; + /** 已完成的数据 */ + loaded: number; +}; +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/04-alova.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/03-basic/04-alova.md similarity index 77% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/04-alova.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/03-basic/04-alova.md index b3cc5285b..e289b3b33 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/04-alova.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/03-basic/04-alova.md @@ -1,79 +1,78 @@ ---- -title: alova详解 -sidebar_position: 40 ---- - -alova 实例不但可以创建不同类型的 method 实例,还可以设置全局参数,创建的 method 实例都会继承这个 alova 实例的参数。当 alova 实例的参数与 method 实例设置了相同的参数时,例如 `timeout`、`shareRequest`等,将优先使用 method 实例的参数。 - -接下来我们看下 alova 的全局参数。 - -## baseURL - -设置 baseURL 后,你可以不再需要为每个请求都添加相同的 url 前缀。 - -```javascript -const alovaInstance = createAlova({ - baseURL: 'https://api.alovajs.dev' - // ... -}); -``` - -此时,创建 method 实例时只需要指定 pathname 即可。 - -```javascript -alovaInstance.Get('/todo/list'); -``` - -## 全局的超时时间 - -以下为设置全局的请求超时时间。 - -```javascript -// 全局设置请求超时时间 -const alovaInstance = createAlova({ - // ... - // highlight-start - // 请求超时时间,单位为毫秒,默认为0,表示永不超时 - timeout: 50000 - // highlight-end -}); -``` - -## 全局的共享请求 - -在全局设置共享请求。 - -```javascript -const alovaInstance = createAlova({ - // ... - // highlight-start - shareRequest: false - // highlight-end -}); -``` - -## 请求适配器 - -在之前的章节中我们已经配置了`alova/fetch`请求适配器,由这个 alova 实例发起的请求都将使用它发送请求。实际上,我们针对不同的 JS 环境,还提供了各种请求适配器。 - -- [模拟请求适配器](/tutorial/request-adapter/alova-mock) -- [XMLHttpRequest 适配器](/tutorial/request-adapter/alova-adapter-xhr) -- [axios 适配器](/tutorial/request-adapter/alova-adapter-axios) -- [uniapp 适配器](/tutorial/request-adapter/alova-adapter-uniapp) -- [taro 适配器](/tutorial/request-adapter/alova-adapter-taro) - -## 全局的响应缓存 - -你还可以全局设置响应缓存: - -```ts -const alovaInstance = createAlova({ - // ... - cacheFor: { - GET: 0, // 关闭所有GET缓存 - POST: 60 * 60 * 1000 // 设置所有POST缓存1小时 - } -}); -``` - -详细内容可参考[响应缓存](/tutorial/cache/mode) +--- +title: alova详解 +--- + +alova 实例不但可以创建不同类型的 method 实例,还可以设置全局参数,创建的 method 实例都会继承这个 alova 实例的参数。当 alova 实例的参数与 method 实例设置了相同的参数时,例如 `timeout`、`shareRequest`等,将优先使用 method 实例的参数。 + +接下来我们看下 alova 的全局参数。 + +## baseURL + +设置 baseURL 后,你可以不再需要为每个请求都添加相同的 url 前缀。 + +```javascript +const alovaInstance = createAlova({ + baseURL: 'https://api.alovajs.dev' + // ... +}); +``` + +此时,创建 method 实例时只需要指定 pathname 即可。 + +```javascript +alovaInstance.Get('/todo/list'); +``` + +## 全局的超时时间 + +以下为设置全局的请求超时时间。 + +```javascript +// 全局设置请求超时时间 +const alovaInstance = createAlova({ + // ... + // highlight-start + // 请求超时时间,单位为毫秒,默认为0,表示永不超时 + timeout: 50000 + // highlight-end +}); +``` + +## 全局的共享请求 + +在全局设置共享请求。 + +```javascript +const alovaInstance = createAlova({ + // ... + // highlight-start + shareRequest: false + // highlight-end +}); +``` + +## 请求适配器 + +在之前的章节中我们已经配置了`alova/fetch`请求适配器,由这个 alova 实例发起的请求都将使用它发送请求。实际上,我们针对不同的 JS 环境,还提供了各种请求适配器。 + +- [模拟请求适配器](/next/resource/request-adapter/alova-mock) +- [XMLHttpRequest 适配器](/next/resource/request-adapter/alova-adapter-xhr) +- [axios 适配器](/next/resource/request-adapter/alova-adapter-axios) +- [uniapp 适配器](/next/resource/request-adapter/alova-adapter-uniapp) +- [taro 适配器](/next/resource/request-adapter/alova-adapter-taro) + +## 全局的响应缓存 + +你还可以全局设置响应缓存: + +```ts +const alovaInstance = createAlova({ + // ... + cacheFor: { + GET: 0, // 关闭所有GET缓存 + POST: 60 * 60 * 1000 // 设置所有POST缓存1小时 + } +}); +``` + +详细内容可参考[响应缓存](/next/tutorial/cache/mode) diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/05-global-interceptor.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/03-basic/05-global-interceptor.md similarity index 97% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/05-global-interceptor.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/03-basic/05-global-interceptor.md index 98462ae54..357252daf 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/05-global-interceptor.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/03-basic/05-global-interceptor.md @@ -1,6 +1,5 @@ --- title: 全局拦截器 -sidebar_position: 50 --- import Tabs from '@theme/Tabs'; @@ -71,7 +70,7 @@ const alovaInstance = createAlova({ responded: { // highlight-start // 请求成功的拦截器 - // 当使用GlobalFetch请求适配器时,第一个参数接收Response对象 + // 当使用 `alova/fetch` 请求适配器时,第一个参数接收Response对象 // 第二个参数为当前请求的method实例,你可以用它同步请求前后的配置信息 onSuccess: async (response, method) => { if (response.status >= 400) { diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/06-method-metadata.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/03-basic/06-method-metadata.md similarity index 95% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/06-method-metadata.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/03-basic/06-method-metadata.md index e2249288a..750e71860 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/06-method-metadata.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/03-basic/06-method-metadata.md @@ -1,151 +1,150 @@ ---- -title: method元数据 -sidebar_position: 60 ---- - -method 实例是贯穿 alova 的整个请求生命周期的,并且,在项目中会大量存在不同的 method 实例,有时候我们需要对特定的 method 实例添加附加信息,以便于对它们进行身份标识或额外的信息传递等,此时,我们就需要使用 method 元数据。 - -## 使用元数据标识身份 - -### 在请求前使用身份标识 - -例如,你的项目中大部分接口在每次请求时需附带`token`,但还存在一些接口无需验证的,可能你会在全局的`beforeRequest`函数中统一处理它们。 - -```javascript -const nonvalidateRequiredApi = [ - '/api/url1', - '/api/url2', - '/api/url3' - // ... -]; - -createAlova({ - beforeRequest(method) { - if (!nonvalidateRequiredApi.includes(method.url)) { - method.config.headers.token = '...'; - } - } -}); -``` - -这将导致以下两个问题: - -1. 信息没有与 method 实例聚合,可维护性更差; -2. 编码更麻烦; - -为解决这两个问题,我们将使用元数据的方式,在创建特定的 method 实例时对它进行标识。 - -**第一步:在创建 method 实例时定义元数据** - -```javascript -const loginAPI = (username, password) => { - const methodInstance = alovaInst.Post('/login', { - username, - password - }); - methodInstance.meta = { - ignoreToken: true - }; - return methodInstance; -}; -``` - -你也可以直接在 config 中传入 meta 数据 - -```javascript -const loginAPI = (username, password) => { - return alovaInst.Post( - '/login', - { - username, - password - }, - { - meta: { - ignoreToken: true - } - } - ); -}; -``` - -**第二步:在`beforeRequest`中通过元数据作为判断依据** - -```javascript -createAlova({ - // ... - beforeRequest(method) { - if (!method.meta?.ignoreToken) { - method.config.headers.token = '...'; - } - } -}); -``` - -### 在响应后使用标识身份 - -这种方式还可以用于在全局的`responded`中,例如,在绝大部分情况下,请求 api 都将返回 json 数据,但可能存在文件下载接口,它将返回二进制数据流,在这种情况下,你可以在`responded`中使用不同的元数据分别处理不同的响应。 - -**第一步:创建 method 实例时同样需要分配一个元数据** - -```javascript -const downloadAPI = filePath => { - const methodInstance = alovaInst.Post('/download_file', { - filePath - }); - methodInstance.meta = { - isDownload: true - }; - return methodInstance; -}; -``` - -**第二步:在`responded`中通过元数据作为判断依据** - -```javascript -createAlova({ - // ... - responded: - onSuccess: (response, method) => method.meta?.isDownload ? response.blob() : response.json() - onError: (error, method) => { - // 在响应错误时也可以访问method实例的元数据 - } - } -}); -``` - -## 使用元数据传递信息 - -在某种情况下,如果你希望为不同的 method 实例添加附加信息,以便在其他地方使用,也可以使用元数据进行保存。以统一生成不同的 method 实例 id 为例。 - -```javascript -createAlova({ - beforeRequest(method) { - if (!method.meta.generateId) { - method.meta.uid = generateUUID(); - } - }, - - responded: { - onSuccess(response, method) { - // 在请求成功中访问当前method生成的meta数据 - const currentMethodUID = method.meta.uid; - }, - onError(error, method) { - // 在请求失败中访问当前method生成的meta数据 - const currentMethodUID = method.meta.uid; - } - } -}); -``` - -## 非 typescript 项目提示 - -在非 typescript 环境下,你可以使用任意一个属性作为信息载体,而不局限于`meta`属性。 - -```javascript -methodInstance.showResponseMsg = true; -methodInstance.others = 'abc'; -``` - -只是在 typescript 环境下,任意的属性名将会报`不存在属性“$0”。ts(2339)`,因此在类型中我们指定了`meta`属性作为信息载体。 +--- +title: method元数据 +--- + +method 实例是贯穿 alova 的整个请求生命周期的,并且,在项目中会大量存在不同的 method 实例,有时候我们需要对特定的 method 实例添加附加信息,以便于对它们进行身份标识或额外的信息传递等,此时,我们就需要使用 method 元数据。 + +## 使用元数据标识身份 + +### 在请求前使用身份标识 + +例如,你的项目中大部分接口在每次请求时需附带`token`,但还存在一些接口无需验证的,可能你会在全局的`beforeRequest`函数中统一处理它们。 + +```javascript +const nonvalidateRequiredApi = [ + '/api/url1', + '/api/url2', + '/api/url3' + // ... +]; + +createAlova({ + beforeRequest(method) { + if (!nonvalidateRequiredApi.includes(method.url)) { + method.config.headers.token = '...'; + } + } +}); +``` + +这将导致以下两个问题: + +1. 信息没有与 method 实例聚合,可维护性更差; +2. 编码更麻烦; + +为解决这两个问题,我们将使用元数据的方式,在创建特定的 method 实例时对它进行标识。 + +**第一步:在创建 method 实例时定义元数据** + +```javascript +const loginAPI = (username, password) => { + const methodInstance = alovaInst.Post('/login', { + username, + password + }); + methodInstance.meta = { + ignoreToken: true + }; + return methodInstance; +}; +``` + +你也可以直接在 config 中传入 meta 数据 + +```javascript +const loginAPI = (username, password) => { + return alovaInst.Post( + '/login', + { + username, + password + }, + { + meta: { + ignoreToken: true + } + } + ); +}; +``` + +**第二步:在`beforeRequest`中通过元数据作为判断依据** + +```javascript +createAlova({ + // ... + beforeRequest(method) { + if (!method.meta?.ignoreToken) { + method.config.headers.token = '...'; + } + } +}); +``` + +### 在响应后使用标识身份 + +这种方式还可以用于在全局的`responded`中,例如,在绝大部分情况下,请求 api 都将返回 json 数据,但可能存在文件下载接口,它将返回二进制数据流,在这种情况下,你可以在`responded`中使用不同的元数据分别处理不同的响应。 + +**第一步:创建 method 实例时同样需要分配一个元数据** + +```javascript +const downloadAPI = filePath => { + const methodInstance = alovaInst.Post('/download_file', { + filePath + }); + methodInstance.meta = { + isDownload: true + }; + return methodInstance; +}; +``` + +**第二步:在`responded`中通过元数据作为判断依据** + +```javascript +createAlova({ + // ... + responded: + onSuccess: (response, method) => method.meta?.isDownload ? response.blob() : response.json() + onError: (error, method) => { + // 在响应错误时也可以访问method实例的元数据 + } + } +}); +``` + +## 使用元数据传递信息 + +在某种情况下,如果你希望为不同的 method 实例添加附加信息,以便在其他地方使用,也可以使用元数据进行保存。以统一生成不同的 method 实例 id 为例。 + +```javascript +createAlova({ + beforeRequest(method) { + if (!method.meta.generateId) { + method.meta.uid = generateUUID(); + } + }, + + responded: { + onSuccess(response, method) { + // 在请求成功中访问当前method生成的meta数据 + const currentMethodUID = method.meta.uid; + }, + onError(error, method) { + // 在请求失败中访问当前method生成的meta数据 + const currentMethodUID = method.meta.uid; + } + } +}); +``` + +## 非 typescript 项目提示 + +在非 typescript 环境下,你可以使用任意一个属性作为信息载体,而不局限于`meta`属性。 + +```javascript +methodInstance.showResponseMsg = true; +methodInstance.others = 'abc'; +``` + +只是在 typescript 环境下,任意的属性名将会报`不存在属性“$0”。ts(2339)`,因此在类型中我们指定了`meta`属性作为信息载体。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/07-combine-framework.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/03-basic/07-combine-framework.md similarity index 95% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/07-combine-framework.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/03-basic/07-combine-framework.md index e7e7252c8..f924ac77a 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/07-combine-framework.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/03-basic/07-combine-framework.md @@ -1,6 +1,5 @@ --- title: 结合UI框架 -sidebar_position: 70 --- import Tabs from '@theme/Tabs'; @@ -92,7 +91,7 @@ useRequest 表示一次请求的发送,调用时默认将发送一次请求。 -[何时使用 useRequest ,何时通过`await alovaInstance.Get` 发送请求](/tutorial/best-practice/skills)。 +[何时使用 useRequest ,何时通过`await alovaInstance.Get` 发送请求](/next/tutorial/project/best-practice/skills)。 :::warning useHook 使用规范 @@ -190,7 +189,7 @@ flowchart LR ### 转换响应数据 -在[method 详解](/tutorial/getting-started/method)中,我们已经了解过`transformData`了,这在 useHook 中使用也非常有用,它可以让 useHook 的 data 接收到转换后的数据,而不用再转换。 +在[method 详解](/next/tutorial/getting-started/basic/method)中,我们已经了解过`transformData`了,这在 useHook 中使用也非常有用,它可以让 useHook 的 data 接收到转换后的数据,而不用再转换。 ```javascript const todoListGetter = alovaInstance.Get('/todo/list', { @@ -277,4 +276,4 @@ onComplete(event => { 3. useAutoRequest: 按定时轮询、浏览器聚焦、网络重连等规则自动请求 4. ... -了解完整使用或其他客户端请求策略,请移步[客户端策略](/xxx)查看 alova 提供的所有客户端请求策略。 +了解完整使用或其他客户端请求策略,请移步[客户端策略](/next/tutorial/client/strategy)查看 alova 提供的所有客户端请求策略。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/08-server.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/03-basic/08-server.md similarity index 88% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/08-server.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/03-basic/08-server.md index db20b5c83..8aa2c0457 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/08-server.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/03-basic/08-server.md @@ -1,9 +1,8 @@ --- title: 在服务端使用 -sidebar_position: 80 --- -在前面的[快速开始](/tutorial/getting-started/quick-start)中已经介绍过,alova 可以在`nodejs/deno/bun`等服务端环境中使用,你可以在服务端使用除客户端请求策略外的所有功能,此外,alova 还针对服务端环境提供了很好的支持。 +在前面的[快速开始](/next/tutorial/getting-started/quick-start)中已经介绍过,alova 可以在`nodejs/deno/bun`等服务端环境中使用,你可以在服务端使用除客户端请求策略外的所有功能,此外,alova 还针对服务端环境提供了很好的支持。 ```js import { createAlova } from 'alova'; @@ -68,7 +67,7 @@ const result = await sendCaptcha( ); ``` -> 了解更多`Server hooks`请移步[服务端策略](/xxx)。 +> 了解更多`Server hooks`请移步[服务端策略](/next/tutorial/server/strategy)。 ## 多级缓存 @@ -147,4 +146,4 @@ const alovaInstance = createAlova({ }); ``` -> 深入了解响应缓存,请移步[缓存详解](/turorial/cache/mode)。 +> 深入了解响应缓存,请移步[缓存详解](/next/tutorial/cache/mode)。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/09-extension-integration.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/09-extension-integration.md index 895f9190a..a1194a24b 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/09-extension-integration.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/09-extension-integration.md @@ -1,6 +1,5 @@ --- title: 编辑器扩展集成 -sidebar_position: 90 --- 集成 alova 的编辑器扩展可以让它展现出它更强大的力量。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/10-congratulations.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/10-congratulations.md index 844e96aa5..005ea08eb 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/10-congratulations.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/10-congratulations.md @@ -1,6 +1,5 @@ --- title: 结语 -sidebar_position: 100 --- import NavCard from '@site/src/components/NavCard'; @@ -11,16 +10,16 @@ import NavCard from '@site/src/components/NavCard'; { title: '客户端策略', desc: '选择你需要的策略模块,快速实现复杂请求', -link: '/category/request-adapter' +link: '/next/tutorial/client/strategy' }, { title: '服务端策略', desc: '选择你需要的策略模块,快速实现复杂请求', -link: '/category/request-adapter' +link: '/next/tutorial/server/strategy' }, { title: '最佳实践', desc: '在项目中使用实践总结的 alova 技巧', -link: '/category/best-practice' +link: '/next/tutorial/project/best-practice' } ]}> diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-use-request.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/01-use-request.md similarity index 92% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-use-request.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/01-use-request.md index 9bb44c6d8..60b0adbf1 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-use-request.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/01-use-request.md @@ -1,6 +1,5 @@ --- title: 自动管理请求状态 -sidebar_position: 10 --- import Tabs from '@theme/Tabs'; @@ -16,7 +15,7 @@ useRequest 表示一次请求的发送,调用时默认将发送一次请求, ## 使用 -它的基础用法已在[开始-结合 UI 框架](/tutorial/getting-started/combine-framework)中详细介绍。 +它的基础用法已在[基础-结合 UI 框架](/next/tutorial/getting-started/basic/combine-framework)中详细介绍。 ### 设置初始数据 @@ -157,7 +156,7 @@ send(1); #### 在 force 函数中接收 -force 用于指定是否需要穿透响应缓存,关于响应缓存的内容将在后面的[缓存模式](/tutorial/cache/mode)中讲解。 +force 用于指定是否需要穿透响应缓存,关于响应缓存的内容将在后面的[缓存模式](/next/tutorial/cache/mode)中讲解。 ```javascript const { send } = useRequest(alovaInstance.Get('/todo'), { @@ -204,7 +203,7 @@ abort(); ### 额外的管理状态 -设置额外的管理状态,这些状态可以实现跨组件更新,具体请查看[额外的管理状态](/tutorial/managed-state)。 +设置额外的管理状态,这些状态可以实现跨组件更新,具体请查看[额外的管理状态](/next/tutorial/client/in-depth/manage-extra-states)。 ### 中间件 @@ -222,8 +221,8 @@ useRequest(todoListGetter, { }); ``` -具体请查看[深入客户端-中间件](/tutorial/middleware)。 +具体请查看[深入客户端-中间件](/next/tutorial/client/in-depth/middleware)。 ## API -请查看[API-useRequest](/api/core-hooks#userequest)。 +请查看[API-useRequest](/next/api/core-hooks#userequest)。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/02-use-watcher.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/02-use-watcher.md similarity index 96% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/02-use-watcher.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/02-use-watcher.md index f2270cf26..b1b4497eb 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/02-use-watcher.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/02-use-watcher.md @@ -1,6 +1,5 @@ --- title: 监听请求 -sidebar_position: 20 --- import Tabs from '@theme/Tabs'; @@ -44,7 +43,7 @@ use hook :::tip 用法提示 -useWatcher 支持 useRequest 的所有功能,详情请查看 [useRequest](/tutorial/client/use-request)。以下是 useWatcher 特有的用法。 +useWatcher 支持 useRequest 的所有功能,详情请查看 [useRequest](/next/tutorial/client/strategy/use-request)。以下是 useWatcher 特有的用法。 ::: @@ -145,4 +144,4 @@ useWatcher( ## API -请查看[API-useWatcher](/api/core-hooks#usewatcher)。 +请查看[API-useWatcher](/next/api/core-hooks#usewatcher)。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/03-use-fetcher.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/03-use-fetcher.md similarity index 96% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/03-use-fetcher.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/03-use-fetcher.md index e83727ee9..ebb353857 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/03-use-fetcher.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/03-use-fetcher.md @@ -1,6 +1,5 @@ --- title: 数据拉取 -sidebar_position: 30 --- import Tabs from '@theme/Tabs'; @@ -185,7 +184,7 @@ const App = () => { ## 跨模块/组件更新视图 -下面我们来实现修改一条 todo 数据,并重新拉取最新的 todo 列表数据,让视图更新。我们可能并不知道 todo 列表当前位于第几页,此时在使用`fetch`函数时可以使用[method 快照匹配器](/tutorial/advanced/method-matcher)来动态拉取当前页的数据。 +下面我们来实现修改一条 todo 数据,并重新拉取最新的 todo 列表数据,让视图更新。我们可能并不知道 todo 列表当前位于第几页,此时在使用`fetch`函数时可以使用[method 快照匹配器](/next/tutorial/client/in-depth/method-matcher)来动态拉取当前页的数据。 > method 快照匹配器可以在已请求过的 method 实例中,查找符合条件的 method 实例。 @@ -234,8 +233,8 @@ useFetcher 只会在请求完成后更新缓存,并且如果此方法发现该 ::: -> 更多 method 快照匹配器的使用方法见 [method 快照匹配器](/tutorial/advanced/method-matcher)。 +> 更多 method 快照匹配器的使用方法见 [method 快照匹配器](/next/tutorial/client/in-depth/method-matcher)。 ## 强制发送请求 -和`useRequest`和`useWatcher`相同,更多请阅读[强制请求](/tutorial/cache/force-request)。 +和`useRequest`和`useWatcher`相同,更多请阅读[强制请求](/next/tutorial/cache/force-request)。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/04-use-pagination.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/04-use-pagination.md similarity index 96% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/04-use-pagination.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/04-use-pagination.md index d80142489..b9584d804 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/04-use-pagination.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/04-use-pagination.md @@ -1,6 +1,5 @@ --- title: 分页请求策略 -sidebar_position: 40 --- import Tabs from '@theme/Tabs'; @@ -16,11 +15,11 @@ use hook 为分页场景下设计的 hook,它可以帮助你自动管理分页数据,数据预加载,减少不必要的数据刷新,**流畅性提高 300%,编码难度降低 50%**。你可以在下拉加载和页码翻页两种分页场景下使用它,此 hook 提供了丰富的特性,助你的应用打造性能更好,使用更便捷的分页功能。 -## 示例 + ## 特性 @@ -483,7 +482,7 @@ const App = () => { -与`useWatcher`相同,你也可以通过指定`debounce`来实现请求防抖,具体可参考[useWatcher 的 debounce 参数设置](/api/core-hooks#usewatcher)。 +与`useWatcher`相同,你也可以通过指定`debounce`来实现请求防抖,具体可参考[useWatcher 的 debounce 参数设置](/next/api/core-hooks#usewatcher)。 ```javascript usePagination((page, pageSize) => queryStudents(page, pageSize, studentName, clsName), { @@ -494,7 +493,7 @@ usePagination((page, pageSize) => queryStudents(page, pageSize, studentName, cls }); ``` -需要注意的是,`debounce`是通过 [**useWatcher**](/api/core-hooks#usewatcher) 中的请求防抖实现的。**监听状态末尾分别还有 page 和 pageSize 两个隐藏的监听状态,也可以通过 debounce 来设置。** +需要注意的是,`debounce`是通过 [**useWatcher**](/next/api/core-hooks#usewatcher) 中的请求防抖实现的。**监听状态末尾分别还有 page 和 pageSize 两个隐藏的监听状态,也可以通过 debounce 来设置。** 举例来说,当`watchingStates`设置了`[studentName, clsName]`,内部将会监听`[studentName, clsName, page, pageSize]`,因此如果需要对 page 和 pageSize 设置防抖时,可以指定为`[0, 0, 500, 500]`。 @@ -623,7 +622,7 @@ declare function refresh(pageOrItemPage?: number | LD[number]): void; ### 手动更新列表数据 -使用`update`函数更新响应式数据,这与[useRequest 的 update](/tutorial/combine-framework/use-request)相似,唯一不同的是,在调用`update`更新`data`时,更新的是列表数据,而非响应数据。这在手动清除列表数据,而不重新发起请求时很有用。 +使用`update`函数更新响应式数据,这与[useRequest 的 update](/next/tutorial/client/strategy/use-request)相似,唯一不同的是,在调用`update`更新`data`时,更新的是列表数据,而非响应数据。这在手动清除列表数据,而不重新发起请求时很有用。 ```typescript // 情况列表数据 @@ -647,7 +646,7 @@ declare function reload(): void; ### Hook 配置 -继承[**useWatcher**](/api/core-hooks#usewatcher)所有配置。 +继承[**useWatcher**](/next/api/core-hooks#usewatcher)所有配置。 | 名称 | 描述 | 类型 | 默认值 | 版本 | | ------------------- | ---------------------------------------- | ------------------------- | -------------------------- | ---- | @@ -663,7 +662,7 @@ declare function reload(): void; ### 响应式数据 -继承[**useWatcher**](/api/core-hooks#usewatcher)所有响应式数据。 +继承[**useWatcher**](/next/api/core-hooks#usewatcher)所有响应式数据。 | 名称 | 描述 | 类型 | 版本 | | ---------- | ------------------------------------------------------------------------------------------------------------------ | ------- | ---- | @@ -677,7 +676,7 @@ declare function reload(): void; ### 操作函数 -继承[**useWatcher**](/api/core-hooks#usewatcher)所有操作函数。 +继承[**useWatcher**](/next/api/core-hooks#usewatcher)所有操作函数。 | 名称 | 描述 | 函数参数 | 返回值 | 版本 | | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------- | ------ | ---- | @@ -690,7 +689,7 @@ declare function reload(): void; ### 事件 -继承[**useWatcher**](/api/core-hooks#usewatcher)所有事件。 +继承[**useWatcher**](/next/api/core-hooks#usewatcher)所有事件。 | 名称 | 描述 | 回调参数 | 版本 | | --------------- | ------------------------ | ------------------------- | ---- | diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/05-use-form.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/05-use-form.md similarity index 96% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/05-use-form.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/05-use-form.md index 14987e5ef..c30f97f0a 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/05-use-form.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/05-use-form.md @@ -1,6 +1,5 @@ --- title: 表单提交策略 -sidebar_position: 50 --- import Tabs from '@theme/Tabs'; @@ -16,9 +15,9 @@ use hook 为表单提交而设计的 hook,通过此 hook 你可以很方便地实现表单草稿、多页面(多步骤)表单,除此以外还提供了表单重置等常用功能。 -## 示例 + ## 特性 @@ -414,7 +413,7 @@ const returnStates = useForm(submitData, { 这样无论先渲染哪个组件都可以对 id 为 testForm 的表单初始化,后面的组件在遇到 id 为 testForm 时将优先使用已初始化的表单数据,而不会再次初始化。这样你就可以在任意组件内初始化表单数据。 -> 更详细的多步骤表单也可以在[表单提交 Demo](/tutorial/example/form-hook)中体验和查看。 + ### 条件筛选 @@ -431,7 +430,7 @@ const { send: searchData } = useForm(queryCity, { :::warning 条件限制 -在条件筛选场景下,`useForm`更适用于非分页的列表条件查询,如果你需要在分页列表中进行条件查询,建议使用 [分页请求策略(usePagination)](/tutorial/strategy/usePagination)。 +在条件筛选场景下,`useForm`更适用于非分页的列表条件查询,如果你需要在分页列表中进行条件查询,建议使用 [分页请求策略(usePagination)](/next/tutorial/client/strategy/use-pagination)。 ::: @@ -439,7 +438,7 @@ const { send: searchData } = useForm(queryCity, { ### Hook 配置 -继承[**useRequest**](/api/core-hooks#userequest)所有配置。 +继承[**useRequest**](/next/api/core-hooks#userequest)所有配置。 | 名称 | 描述 | 类型 | 默认值 | 版本 | | ------------------- | -------------------------------------------------------------------------------------------------------- | -------------------------------------------------- | ------ | ---- | @@ -450,7 +449,7 @@ const { send: searchData } = useForm(queryCity, { ### 响应式数据 -继承[**useRequest**](/api/core-hooks#userequest)所有响应式数据。 +继承[**useRequest**](/next/api/core-hooks#userequest)所有响应式数据。 | 名称 | 描述 | 类型 | 版本 | | ---- | ----------------------------- | ---- | ---- | @@ -472,7 +471,7 @@ const { send: searchData } = useForm(queryCity, { ### 操作函数 -继承[**useRequest**](/api/core-hooks#userequest)所有操作函数。 +继承[**useRequest**](/next/api/core-hooks#userequest)所有操作函数。 | 名称 | 描述 | 函数参数 | 返回值 | 版本 | | ---------- | ------------------------------------------ | ---------------------------------------------------------------------- | ------ | ---- | @@ -481,7 +480,7 @@ const { send: searchData } = useForm(queryCity, { ### 事件 -继承[**useRequest**](/api/core-hooks#userequest)所有事件。 +继承[**useRequest**](/next/api/core-hooks#userequest)所有事件。 | 名称 | 描述 | 回调参数 | 版本 | | --------- | -------------------- | -------- | ---- | diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/06-token-authentication.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/06-token-authentication.md similarity index 97% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/06-token-authentication.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/06-token-authentication.md index a5e286e16..dcfd751bf 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/06-token-authentication.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/06-token-authentication.md @@ -1,6 +1,5 @@ --- title: Token认证拦截器 -sidebar_position: 60 --- import Tabs from '@theme/Tabs'; @@ -112,7 +111,7 @@ const alovaInstance = createAlova({ :::warning -当你使用`GlobalFetch`适配器时,你可能会遇到`TypeError: Failed to execute 'json' on 'Response': body stream already read`这个问题,这是因为`Response`的`body stream`只能访问一次,你可以`response.clone().json()`来解决它。 +当你使用`alova/fetch`适配器时,你可能会遇到`TypeError: Failed to execute 'json' on 'Response': body stream already read`这个问题,这是因为`Response`的`body stream`只能访问一次,你可以`response.clone().json()`来解决它。 ::: @@ -152,7 +151,7 @@ createClientTokenAuthentication({ ::: -> 了解更多元数据的信息,请前往[method 元数据](/tutorial/getting-started/method-metadata)。 +> 了解更多元数据的信息,请前往[method 元数据](/next/tutorial/getting-started/basic/method-metadata)。 ```javascript export const refreshToken = () => { @@ -170,7 +169,7 @@ export const refreshToken = () => { ### 在请求成功拦截器中处理 -当使用`GlobalFetch`时,只要服务端返回了响应数据,就会触发响应成功拦截器,此时我们需要在响应成功拦截器中处理 token 的刷新。 +当使用`alova/fetch`时,只要服务端返回了响应数据,就会触发响应成功拦截器,此时我们需要在响应成功拦截器中处理 token 的刷新。 ```javascript createServerTokenAuthentication({ @@ -235,7 +234,7 @@ createServerTokenAuthentication({ ::: -> 了解更多元数据的信息,请前往[method 元数据](/tutorial/getting-started/method-metadata)。 +> 了解更多元数据的信息,请前往[method 元数据](/next/tutorial/getting-started/basic/method-metadata)。 ```javascript export const refreshToken = () => { @@ -469,7 +468,7 @@ export const logout = () => { ## Typescript -默认情况下,`createClientServerTokenAuthentication`和`createServerTokenAuthentication`适配了`GlobalFetch`请求适配器,你只需要指定`statesHook`的类型,如下: +默认情况下,`createClientServerTokenAuthentication`和`createServerTokenAuthentication`适配了`alova/fetch`请求适配器,你只需要指定`statesHook`的类型,如下: ```typescript // highlight-start @@ -493,7 +492,7 @@ const alovaInstance = createAlova({ }); ``` -如果你使用的不是`GlobalFetch`请求适配器,你还需要指定请求适配器的类型,这也很简单。 +如果你使用的不是`alova/fetch`请求适配器,你还需要指定请求适配器的类型,这也很简单。 以下为 axios 请求适配器为例,在`createClientTokenAuthentication`中指定请求适配器类型。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/07-use-auto-request.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/07-use-auto-request.md similarity index 90% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/07-use-auto-request.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/07-use-auto-request.md index a72c09412..e9321e6ae 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/07-use-auto-request.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/07-use-auto-request.md @@ -1,6 +1,5 @@ --- title: 自动拉取数据 -sidebar_position: 70 --- import Tabs from '@theme/Tabs'; @@ -32,9 +31,9 @@ import { useAutoRequest } from 'alova/client'; const { loading, data, error } = useAutoRequest(() => method()); ``` -`useAutoRequest`的返回值与[useRequest](/api/core-hooks#userequest)相同。 +`useAutoRequest`的返回值与[useRequest](/next/api/core-hooks#userequest)相同。 -除了支持[useRequest](/api/core-hooks#userequest)的所有配置参数外,还支持自动拉取的配置参数,你可以通过以下配置开启或关闭一些事件,或修改请求节流事件。 +除了支持[useRequest](/next/api/core-hooks#userequest)的所有配置参数外,还支持自动拉取的配置参数,你可以通过以下配置开启或关闭一些事件,或修改请求节流事件。 ```javascript const { loading, data, error, onSuccess, onError, onComplete } = useAutoRequest( @@ -77,7 +76,7 @@ const { loading, data, error, onSuccess, onError, onComplete } = useAutoRequest( :::warning 缓存建议 -建议在使用`useAutoRequest`时关闭对应请求的缓存,因为当设置缓存时,在触发自动请求时也会命中缓存而获取不到最新数据。具体请阅读[缓存模式](/tutorial/cache/mode)。 +建议在使用`useAutoRequest`时关闭对应请求的缓存,因为当设置缓存时,在触发自动请求时也会命中缓存而获取不到最新数据。具体请阅读[缓存模式](/next/tutorial/cache/mode)。 ::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-action-delegation-middleware.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/08-action-delegation-middleware.md similarity index 55% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-action-delegation-middleware.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/08-action-delegation-middleware.md index ec292250f..3dc4a55c4 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/08-action-delegation-middleware.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/08-action-delegation-middleware.md @@ -1,6 +1,5 @@ --- title: 跨组件触发请求 -sidebar_position: 80 --- import Tabs from '@theme/Tabs'; @@ -18,9 +17,9 @@ import TabItem from '@theme/TabItem'; 例如,你可以某个组件中更新了菜单数据后,重新触发侧边菜单栏的重新请求,从而刷新数据。当操作了列表数据后,触发列表更新。 -## 示例 + ## 特性 @@ -139,11 +138,11 @@ accessAction(/^prefix_/, delegatedActions => { ### useRequest -| 名称 | 描述 | 函数参数 | 返回值 | 版本 | -| ------ | ------------------------------------------------------- | -------- | ------ | ---- | -| send | 与 [useRequset](/api/core-hooks#userequest).send 相同 | | | - | -| abort | 与 [useRequset](/api/core-hooks#userequest).abort 相同 | | | - | -| update | 与 [useRequset](/api/core-hooks#userequest).update 相同 | | | - | +| 名称 | 描述 | 函数参数 | 返回值 | 版本 | +| ------ | ------------------------------------------------------------ | -------- | ------ | ---- | +| send | 与 [useRequset](/next/api/core-hooks#userequest).send 相同 | | | - | +| abort | 与 [useRequset](/next/api/core-hooks#userequest).abort 相同 | | | - | +| update | 与 [useRequset](/next/api/core-hooks#userequest).update 相同 | | | - | ### useWatcher @@ -151,23 +150,23 @@ accessAction(/^prefix_/, delegatedActions => { ### useFetcher -| 名称 | 描述 | 函数参数 | 返回值 | 版本 | -| ------ | ------------------------------------------------------- | -------- | ------ | ---- | -| fetch | 与 [useFetcher](/api/core-hooks#usefetcher).fetch 相同 | | | - | -| abort | 与 [useFetcher](/api/core-hooks#usefetcher).abort 相同 | | | - | -| update | 与 [useFetcher](/api/core-hooks#usefetcher).update 相同 | | | - | +| 名称 | 描述 | 函数参数 | 返回值 | 版本 | +| ------ | ------------------------------------------------------------ | -------- | ------ | ---- | +| fetch | 与 [useFetcher](/next/api/core-hooks#usefetcher).fetch 相同 | | | - | +| abort | 与 [useFetcher](/next/api/core-hooks#usefetcher).abort 相同 | | | - | +| update | 与 [useFetcher](/next/api/core-hooks#usefetcher).update 相同 | | | - | ### usePagination -| 名称 | 描述 | 函数参数 | 返回值 | 版本 | -| -------- | ------------------------------------------------------------------ | ---------------------------------------------------------------------------------- | ------------------ | ---- | -| refresh | 详见[usePagination 操作函数](/tutorial/strategy/usePagination#api) | | | - | -| insert | 详见[usePagination 操作函数](/tutorial/strategy/usePagination#api) | | | - | -| remove | 详见[usePagination 操作函数](/tutorial/strategy/usePagination#api) | | | - | -| replace | 详见[usePagination 操作函数](/tutorial/strategy/usePagination#api) | | | - | -| reload | 详见[usePagination 操作函数](/tutorial/strategy/usePagination#api) | | | - | -| update | 详见[usePagination 操作函数](/tutorial/strategy/usePagination#api) | | | - | -| getState | 按名称获取分页相关数据 | stateKey: 'page' \| 'pageSize' \| 'data' \| 'pageCount' \| 'total' \| 'isLastPage' | 对应 statekey 的值 | - | +| 名称 | 描述 | 函数参数 | 返回值 | 版本 | +| -------- | ------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | ------------------ | ---- | +| refresh | 详见[usePagination 操作函数](/next/tutorial/client/strategy/use-pagination#api) | | | - | +| insert | 详见[usePagination 操作函数](/next/tutorial/client/strategy/use-pagination#api) | | | - | +| remove | 详见[usePagination 操作函数](/next/tutorial/client/strategy/use-pagination#api) | | | - | +| replace | 详见[usePagination 操作函数](/next/tutorial/client/strategy/use-pagination#api) | | | - | +| reload | 详见[usePagination 操作函数](/next/tutorial/client/strategy/use-pagination#api) | | | - | +| update | 详见[usePagination 操作函数](/next/tutorial/client/strategy/use-pagination#api) | | | - | +| getState | 按名称获取分页相关数据 | stateKey: 'page' \| 'pageSize' \| 'data' \| 'pageCount' \| 'total' \| 'isLastPage' | 对应 statekey 的值 | - | ### useSQRequest @@ -175,13 +174,13 @@ accessAction(/^prefix_/, delegatedActions => { ### useForm -| 名称 | 描述 | 函数参数 | 返回值 | 版本 | -| ---------- | ------------------------------------------------------- | -------- | ------ | ---- | -| updateForm | 详见[useForm 操作函数](/tutorial/strategy/useForm#api) | | | - | -| reset | 详见[useForm 操作函数](/tutorial/strategy/useForm#api) | | | - | -| send | 与 [useRequset](/api/core-hooks#userequest).send 相同 | | | - | -| abort | 与 [useRequset](/api/core-hooks#userequest).abort 相同 | | | - | -| update | 与 [useRequset](/api/core-hooks#userequest).update 相同 | | | - | +| 名称 | 描述 | 函数参数 | 返回值 | 版本 | +| ---------- | ------------------------------------------------------------------- | -------- | ------ | ---- | +| updateForm | 详见[useForm 操作函数](/next/tutorial/client/strategy/use-form#api) | | | - | +| reset | 详见[useForm 操作函数](/next/tutorial/client/strategy/use-form#api) | | | - | +| send | 与 [useRequset](/next/api/core-hooks#userequest).send 相同 | | | - | +| abort | 与 [useRequset](/next/api/core-hooks#userequest).abort 相同 | | | - | +| update | 与 [useRequset](/next/api/core-hooks#userequest).update 相同 | | | - | ### useCaptcha @@ -189,12 +188,12 @@ accessAction(/^prefix_/, delegatedActions => { ### useRetriableRequest -| 名称 | 描述 | 函数参数 | 返回值 | 版本 | -| ------ | ------------------------------------------------------------------------------ | -------- | ------ | ---- | -| stop | 详见[useRetriableRequest 操作函数](/tutorial/strategy/useRetriableRequest#api) | | | - | -| send | 与 [useRequset](/api/core-hooks#userequest).send 相同 | | | - | -| abort | 与 [useRequset](/api/core-hooks#userequest).abort 相同 | | | - | -| update | 与 [useRequset](/api/core-hooks#userequest).update 相同 | | | - | +| 名称 | 描述 | 函数参数 | 返回值 | 版本 | +| ------ | -------------------------------------------------------------------------------------------- | -------- | ------ | ---- | +| stop | 详见[useRetriableRequest 操作函数](/next/tutorial/client/strategy/use-retriable-request#api) | | | - | +| send | 与 [useRequset](/next/api/core-hooks#userequest).send 相同 | | | - | +| abort | 与 [useRequset](/next/api/core-hooks#userequest).abort 相同 | | | - | +| update | 与 [useRequset](/next/api/core-hooks#userequest).update 相同 | | | - | ### useSerialRequest diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/02-virtual-data.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/09-sensorless-data-interaction/02-virtual-data.md similarity index 99% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/02-virtual-data.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/09-sensorless-data-interaction/02-virtual-data.md index 19036ae56..c39faf8fb 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/02-virtual-data.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/09-sensorless-data-interaction/02-virtual-data.md @@ -1,6 +1,5 @@ --- title: 虚拟数据 -sidebar_position: 20 --- 实际上,虚拟数据是一个拥有唯一 id 的引用对象,其追查机制就是通过先生成虚拟数据 id 和响应数据之间的映射,再通过虚拟数据 id 查找并替换为实际值来实现的。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/03-start-silent-factory.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/09-sensorless-data-interaction/03-start-silent-factory.md similarity index 99% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/03-start-silent-factory.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/09-sensorless-data-interaction/03-start-silent-factory.md index 9c1412416..142571653 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/03-start-silent-factory.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/09-sensorless-data-interaction/03-start-silent-factory.md @@ -1,6 +1,5 @@ --- title: 启动静默工厂 -sidebar_position: 30 --- import Tabs from '@theme/Tabs'; diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/04-conservative-request.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/09-sensorless-data-interaction/04-conservative-request.md similarity index 95% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/04-conservative-request.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/09-sensorless-data-interaction/04-conservative-request.md index b6c395468..18a2c6b05 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/04-conservative-request.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/09-sensorless-data-interaction/04-conservative-request.md @@ -1,6 +1,5 @@ --- title: 步骤1-以保守请求实现功能 -sidebar_position: 40 --- import Tabs from '@theme/Tabs'; @@ -8,7 +7,7 @@ import TabItem from '@theme/TabItem'; 以 Todo 管理为示例,来实现 Todo 在无感交互模式下的创建、编辑、删除等功能,在接下来的章节中将提供请求相关的关键代码。 -> 这里的[简单列表页示例](/tutorial/example/silent-submit-simple-list)包含了完整的代码,你可以进入体验。 + 使用 **useSQRequest** 来替代 alova 提供的 **useRequest**,接下来先以最常见的保守请求模式来实现,再一步一步处理无感交互模式的兼容性。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/05-modify-response.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/09-sensorless-data-interaction/05-modify-response.md similarity index 98% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/05-modify-response.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/09-sensorless-data-interaction/05-modify-response.md index 63d69adf9..36e22c108 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/05-modify-response.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/09-sensorless-data-interaction/05-modify-response.md @@ -1,6 +1,5 @@ --- title: 步骤2-调整响应处理 -sidebar_position: 50 --- import Tabs from '@theme/Tabs'; @@ -111,7 +110,7 @@ onSuccess(({ data, silentMethod }) => { // highlight-end ``` -> updateStateEffect 的使用方法与[updateState](/tutorial/advanced/update-across-components)一致 +> updateStateEffect 的使用方法与[updateState](/next/tutorial/client/in-depth/update-across-components)一致 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/06-request-retry.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/09-sensorless-data-interaction/06-request-retry.md similarity index 96% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/06-request-retry.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/09-sensorless-data-interaction/06-request-retry.md index fb09fe811..7d847c4b1 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/06-request-retry.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/09-sensorless-data-interaction/06-request-retry.md @@ -1,144 +1,143 @@ ---- -title: 步骤3-设置请求重试 -sidebar_position: 60 ---- - -当一个请求进入静默队列后是可以为它设置请求重试参数,来保证它的请求成功率,这在行为模式设置为 **queue** 和 **silent** 时都有效,不同的是,**silent** 行为下的请求默认是持久化的,请求成功前即使刷新也将继续发送请求,而 **queue** 行为下的请求不会被持久化,刷新后将被清除。 - -## 最大重试次数 - -设置最大重试次数,默认不重试。 - -```javascript -useSQRequest(createOrEditTodo, { - // ... - // highlight-start - // 重试次数为3次 - maxRetryTimes: 3 - // highlight-end -}); -``` - -## 请求延迟时间 - -默认情况下,每次间隔 1000ms 重试,我们可以在避让策略中自定义设置每次重试的延迟时间。 - -```javascript -useSQRequest(createOrEditTodo, { - // ... - maxRetryTimes: 3, - // highlight-start - // 每次延迟2000ms进行请求 - backoff: { - delay: 2000 - } - // highlight-end -}); -``` - -如果需要按规则增长的延迟时间,可以再为它设置一个增长倍数,延迟时间将按重试次数指数增长。 - -```javascript -useSQRequest(createOrEditTodo, { - // ... - maxRetryTimes: 3, - backoff: { - delay: 2000, - // highlight-start - // multiplier设置为2时,第一次重试延迟为2秒,第二次为4秒,第三次为8秒,以此类推 - multiplier: 2 - // highlight-end - } -}); -``` - -还不够?你甚至还可以为每次延迟时间增加随机抖动值,让它看着不那么规律 - -```javascript -useSQRequest(createOrEditTodo, { - // ... - maxRetryTimes: 3, - backoff: { - delay: 2000, - multiplier: 2, - // highlight-start - /** - * 延迟请求的抖动起始百分比值,范围为0-1 - * 当只设置了startQuiver时,endQuiver默认为1 - * 例如设置为0.5,它将在当前延迟时间上增加50%到100%的随机时间 - * 如果endQuiver有值,则延迟时间将增加startQuiver和endQuiver范围的随机值 - */ - startQuiver: 0.5, - - /** - * 延迟请求的抖动结束百分比值,范围为0-1 - * 当只设置了endQuiver时,startQuiver默认为0 - * 例如设置为0.8,它将在当前延迟时间上增加0%到80%的随机时间 - * 如果startQuiver有值,则延迟时间将增加startQuiver和endQuiver范围的随机值 - */ - endQuiver: 0.8; - // highlight-end - } -}); -``` - -## 重试判定规则 - -默认情况下,只要请求失败都将会重试,请求失败分为以下情况: - -1. 请求错误,并且未被全局的`onError`钩子捕获错误; -2. 请求成功,但在全局的`onSuccess`钩子中抛出了错误; - -但实际情况下,并不是所有的请求都需要重试,例如当遇到服务端错误,或网络断开时不应该重试,此时就需要设置重试判定规则。当请求失败时通常会获得一个 `Error` 实例,我们可以设置正则表达式对 `error.message` 或 `error.name` 进行匹配,匹配通过则不再重试。 - -```javascript -useSQRequest(createOrEditTodo, { - // ... - // highlight-start - // 当抛出的错误name为500,或者错误的message匹配network error时不再重试 - retryError: { - name: /^500$/, - message: /network error/i - } - // highlight-end -}); -``` - -你也可以设置其中一个匹配规则,当只设置 `message` 的匹配规则时,可以直接简写为正则表达式。 - -```javascript -// 只设置匹配错误的name -useSQRequest(createOrEditTodo, { - // ... - retryError: { - name: /^500$/ - } -}); - -// 只设置匹配错误的message -useSQRequest(createOrEditTodo, { - // ... - retryError: /network error/i -}); -``` - -为了不污染错误信息,通常情况下我们将会把服务端返回的错误代码放在 `error.name` 中,当然,你也可以拼接到 `error.message` 中,以 Response 的错误处理示例如下: - -```javascript -const alovaInst = createAlova({ - // ... - responded: { - onSuccess(response) { - // 500错误时抛出错误 - if (response.status === 500) { - const error = new Error(response.statusText); - error.name = response.status; - throw error; - } - return response.json(); - } - } -}); -``` - -下一步,将会使用到保存的操作记录,对列表数据进行数据补偿,来达到最新状态。 +--- +title: 步骤3-设置请求重试 +--- + +当一个请求进入静默队列后是可以为它设置请求重试参数,来保证它的请求成功率,这在行为模式设置为 **queue** 和 **silent** 时都有效,不同的是,**silent** 行为下的请求默认是持久化的,请求成功前即使刷新也将继续发送请求,而 **queue** 行为下的请求不会被持久化,刷新后将被清除。 + +## 最大重试次数 + +设置最大重试次数,默认不重试。 + +```javascript +useSQRequest(createOrEditTodo, { + // ... + // highlight-start + // 重试次数为3次 + maxRetryTimes: 3 + // highlight-end +}); +``` + +## 请求延迟时间 + +默认情况下,每次间隔 1000ms 重试,我们可以在避让策略中自定义设置每次重试的延迟时间。 + +```javascript +useSQRequest(createOrEditTodo, { + // ... + maxRetryTimes: 3, + // highlight-start + // 每次延迟2000ms进行请求 + backoff: { + delay: 2000 + } + // highlight-end +}); +``` + +如果需要按规则增长的延迟时间,可以再为它设置一个增长倍数,延迟时间将按重试次数指数增长。 + +```javascript +useSQRequest(createOrEditTodo, { + // ... + maxRetryTimes: 3, + backoff: { + delay: 2000, + // highlight-start + // multiplier设置为2时,第一次重试延迟为2秒,第二次为4秒,第三次为8秒,以此类推 + multiplier: 2 + // highlight-end + } +}); +``` + +还不够?你甚至还可以为每次延迟时间增加随机抖动值,让它看着不那么规律 + +```javascript +useSQRequest(createOrEditTodo, { + // ... + maxRetryTimes: 3, + backoff: { + delay: 2000, + multiplier: 2, + // highlight-start + /** + * 延迟请求的抖动起始百分比值,范围为0-1 + * 当只设置了startQuiver时,endQuiver默认为1 + * 例如设置为0.5,它将在当前延迟时间上增加50%到100%的随机时间 + * 如果endQuiver有值,则延迟时间将增加startQuiver和endQuiver范围的随机值 + */ + startQuiver: 0.5, + + /** + * 延迟请求的抖动结束百分比值,范围为0-1 + * 当只设置了endQuiver时,startQuiver默认为0 + * 例如设置为0.8,它将在当前延迟时间上增加0%到80%的随机时间 + * 如果startQuiver有值,则延迟时间将增加startQuiver和endQuiver范围的随机值 + */ + endQuiver: 0.8; + // highlight-end + } +}); +``` + +## 重试判定规则 + +默认情况下,只要请求失败都将会重试,请求失败分为以下情况: + +1. 请求错误,并且未被全局的`onError`钩子捕获错误; +2. 请求成功,但在全局的`onSuccess`钩子中抛出了错误; + +但实际情况下,并不是所有的请求都需要重试,例如当遇到服务端错误,或网络断开时不应该重试,此时就需要设置重试判定规则。当请求失败时通常会获得一个 `Error` 实例,我们可以设置正则表达式对 `error.message` 或 `error.name` 进行匹配,匹配通过则不再重试。 + +```javascript +useSQRequest(createOrEditTodo, { + // ... + // highlight-start + // 当抛出的错误name为500,或者错误的message匹配network error时不再重试 + retryError: { + name: /^500$/, + message: /network error/i + } + // highlight-end +}); +``` + +你也可以设置其中一个匹配规则,当只设置 `message` 的匹配规则时,可以直接简写为正则表达式。 + +```javascript +// 只设置匹配错误的name +useSQRequest(createOrEditTodo, { + // ... + retryError: { + name: /^500$/ + } +}); + +// 只设置匹配错误的message +useSQRequest(createOrEditTodo, { + // ... + retryError: /network error/i +}); +``` + +为了不污染错误信息,通常情况下我们将会把服务端返回的错误代码放在 `error.name` 中,当然,你也可以拼接到 `error.message` 中,以 Response 的错误处理示例如下: + +```javascript +const alovaInst = createAlova({ + // ... + responded: { + onSuccess(response) { + // 500错误时抛出错误 + if (response.status === 500) { + const error = new Error(response.statusText); + error.name = response.status; + throw error; + } + return response.json(); + } + } +}); +``` + +下一步,将会使用到保存的操作记录,对列表数据进行数据补偿,来达到最新状态。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/07-data-compensation.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/09-sensorless-data-interaction/07-data-compensation.md similarity index 99% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/07-data-compensation.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/09-sensorless-data-interaction/07-data-compensation.md index b1922a75b..0253e15f4 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/07-data-compensation.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/09-sensorless-data-interaction/07-data-compensation.md @@ -1,6 +1,5 @@ --- title: 步骤4-数据补偿 -sidebar_position: 70 --- 用户可能在断网环境下进行了一些数据操作,此时静默队列内将堆满未提交的请求,当网络恢复后,由于时序机制的限制,完成这些请求需要消耗一点时间,此时加载的列表数据还不包括未提交请求的部分,这会让用户造成一定的困惑: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/08-edit-item.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/09-sensorless-data-interaction/08-edit-item.md similarity index 95% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/08-edit-item.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/09-sensorless-data-interaction/08-edit-item.md index 916a877d8..1ae1a049d 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/08-edit-item.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/09-sensorless-data-interaction/08-edit-item.md @@ -1,6 +1,5 @@ --- title: 步骤5-编辑数据 -sidebar_position: 80 --- > 当用户在断网情况下需要编辑数据,怎么办? @@ -18,7 +17,7 @@ sidebar_position: 80 在之前的章节中我们知道,当新建的数据项还没有成功提交时,将会使用虚拟数据作为 id 的占位符,通常,我们也是通过 id 来获取数据项的,此时我们在`useSQRequeset`上实现了虚拟数据拦截,一个请求如果附带了虚拟数据信息,它将在发送前被拦截并可以指定数据来替代响应数据,并且放弃这次请求。 -还记得在 [步骤 2-调整响应处理](/tutorial/strategy/sensorless-data-interaction/modify-response) 中保存的 **silentMethod.reviewData** 吗? +还记得在 [步骤 2-调整响应处理](/next/tutorial/client/strategy/sensorless-data-interaction/modify-response) 中保存的 **silentMethod.reviewData** 吗? ```javascript onSuccess(({ silentMethod }) => { diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/09-what-more.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/09-sensorless-data-interaction/09-what-more.md similarity index 94% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/09-what-more.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/09-sensorless-data-interaction/09-what-more.md index fce2949f1..78054f5a0 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/09-what-more.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/09-sensorless-data-interaction/09-what-more.md @@ -1,6 +1,5 @@ --- title: 还有什么? -sidebar_position: 90 --- ## 虚拟数据的作用说明 @@ -104,7 +103,7 @@ const defaultSerializers = { }; ``` -[阅读 Date 序列化器源码](https://github.com/alovajs/scene/blob/main/src/hooks/silent/serializer/date.ts) +[阅读 Date 序列化器源码](https://github.com/alovajs/alova/blob/main/packages/client/src/util/serializer/date.ts) ## 操纵静默队列 @@ -140,11 +139,11 @@ const handleClick = () => { ### 查找 silentMethod -在之前的[数据补偿](/tutorial/strategy/sensorless-data-interaction/data-compensation)中,我们使用了 [filterSilentMethods](/tutorial/strategy/sensorless-data-interaction/data-compensation#filtersilentmethods) 查找指定队列的 silentMethod 实例,它将返回所有匹配的 silentMethod 实例,这里再介绍两种查找队列的方式: +在之前的[数据补偿](/next/tutorial/client/strategy/sensorless-data-interaction/data-compensation)中,我们使用了 [filterSilentMethods](/next/tutorial/client/strategy/sensorless-data-interaction/data-compensation#filtersilentmethods) 查找指定队列的 silentMethod 实例,它将返回所有匹配的 silentMethod 实例,这里再介绍两种查找队列的方式: #### 查找个 silentMethod 实例 -使用`getSilentMethod`查询匹配的第一个 silentMethod 实例,用法与 [filterSilentMethods](/tutorial/strategy/sensorless-data-interaction/data-compensation#filtersilentmethods) 相同。 +使用`getSilentMethod`查询匹配的第一个 silentMethod 实例,用法与 [filterSilentMethods](/next/tutorial/client/strategy/sensorless-data-interaction/data-compensation#filtersilentmethods) 相同。 ```typescript function filterSilentMethods( diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/README.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/09-sensorless-data-interaction/README.md similarity index 88% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/README.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/09-sensorless-data-interaction/README.md index ff66e617e..4d46fe38a 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/README.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/09-sensorless-data-interaction/README.md @@ -1,146 +1,146 @@ ---- -title: 无感数据交互 - 概览 ---- - -无感数据交互是指用户在与应用进行交互时,无需等待即可立即展示相关内容,或者提交信息时也无需等待即可展示操作结果,就像和本地数据交互一样,从而大幅提升应用的流畅性,它让用户感知不到数据传输带来的卡顿。 - -这并不是新鲜事,早在 2015 年以前就已经有乐观更新的概念了,它是指在服务端响应前将提交结果展示到界面中,它是建立在大部分的提交都成功的假设上,与之相对的是保守更新,即服务端响应前将展示等待状态,直到请求完成。在处理失败方面,目前的乐观更新方案通常是通过回退来处理,例如下面的示例代码: - -```javascript -const list = []; -const data = {}; -addTodo(data).catch(() => { - list = list.filter(item => item !== data); -}); -list.push(data); -``` - -这可能会导致以下问题: - -1. 回退将增加用户的理解成本和操作成本; -2. 请求时序问题; -3. 如果后续的请求存在对本次提交依赖,则本次失败将导致后续的请求变得没有意义; -4. 可能丢失请求; - -经过数个月的方案设计和不断迭代,alova 已经在这方面走出了一大步,在我们的方案中对以上存在的问题进行了解决,可以更稳定地保证请求成功,虽然还存在技术限制,但在许多场景中得到了应用。在我们的技术方案中,可以更高限度地降低网络波动带来的问题,你的应用在高延迟网络甚至是断网状态下依然可用,在刷新页面后也依然能保持最新状态的数据。 - -## 应用场景 - -无感数据交互虽然不能大规模地使用,但在一定场景下它又是非常合适的,在探索中,我们发现了至少包含但不限于以下几个场景,供你参考。 - -### 编辑器类应用 - -笔记类应用如印象笔记,画布编辑类应用如 MasterGO,它们分别需要实现以下需求: - -1. 进入笔记或图纸列表时将全量拉取列表数据,下次进入将使用本地的缓存数据; -2. 编辑过程中实时同步到服务端,且同步过程发生在后台,不会影响用户正常使用; -3. 网络差或断网状态下也可以继续使用; - -:::info 示例 - -我们提供了一个[笔记应用示例](/tutorial/example/silent-submit-notes),你可以进入体验。 - -::: - -### 设置模块 - -由常用的开关、选择器组成的设置模块,需实现的需求是,用户操作后实时同步到服务端,同时不再展示提交状态,而是直接展示操作后的最新状态。 - -:::info 示例 - -我们提供了一个[设置页示例](/tutorial/example/silent-submit-setting),你可以进入体验。 - -::: - -### 简单的列表管理 - -我们将创建列表项时填写的数据足够用于列表页的展示,称为简单的列表,例如一个学生列表页展示学生的姓名、性别、年级三个数据,这三个数据在创建学生时都需要填写。在简单列表中将实现以下需求: - -1. 在新增、编辑和删除列表项时立即在列表页中展示最新状态,无需在提交完成后才迟迟展示,同时也不受网络波动限制; -2. 刷新页面时,列表页始终保持最新状态; - -:::info 示例 - -我们提供了一个[简单列表页示例](/tutorial/example/silent-submit-simple-list),你可以进入体验。 - -::: - -### 复杂的列表管理 - -复杂列表是指,创建列表项时填写的数据不足以在列表页用于展示,而需要根据服务端的计算产生额外的数据,例如一个 Todo 列表页除了展示基本信息外,还需要列出具体的执行日期,而在创建页只指定了执行日期范围和相关规则,因此执行日期由服务端根据日期范围和规则统一计算生成。 - -在复杂列表中将实现以下需求: - -1. 在新增、编辑和删除列表项时立即在列表页中展示最新状态,并在服务端响应后将服务端计算的数据更新到此列表项中; -2. 刷新页面时,列表页始终保持最新状态; - -:::info 示例 - -复杂列表示例敬请期待... - -::: - -### 自由模式 - -在以上的几种场景下,你可能希望根据一个条件来判断当前是使用无感交互策略还是最常见的保守请求策略,需求如下: - -1. 当网络状态良好时,或付费用户将使用无感交互策略,而网络波动大时,或免费用户则不能享受到无感交互策略; -2. 策略自由切换; - -:::info 示例 - -在以上的示例中你都可以体验自由切换策略 - -::: - -## 不推荐的应用场景 - -### 信息共享类 - -提交的信息需要同步给其他人,例如订单信息,这类信息具有高实时性的要求,我们应该确保提交成功。 - -### 复杂的数据交互类 - -复杂的数据交互是指,数据的编辑和筛选混合进行,例如对某个列表混合进行新增、编辑、删除和筛选,在这种情况下 alova 目前还无法很好地支持,在后续的版本中也将尝试解决这个难题。 - -## 技术方案 - -在无感数据交互的技术方案上,alova 分别实现了数据预拉取和静默提交,接下来我们来了解这两种技术方案。 - -:::info - -阅读前请确保已经掌握了以下章节内容 - -- [基础学习](/tutorial/getting-started) - -::: - -### 数据预拉取 - -在 html 中你可能见过这样的标签``,它告诉浏览器在闲时去预加载样式文件,并放在缓存中,当真正需要使用的时候从缓存中取出即可,alova 也使用了类似的方案,通过[useFetcher](/tutorial/advanced/use-fetcher)来预拉取需要的数据,它将保存在本地缓存中。你可以在任意情况下预判用户的需要阅读的内容,然后预先拉取对应的内容,如在流程类的页面中可以预加载下一页的内容,又或者,用户在某个按钮上停留了 200ms,我们可以预先拉取下一个界面需要的数据,这点类似于**Next.js**的页面预加载。 - -我们提供了一个[预加载的示例](/tutorial/example/prefetch),你可以进入体验。 - -### 静默提交 - -静默提交是一种提交即响应的机制,方案中将保证提交的完成,因此可以将它看作更安全的乐观更新方案。静默提交主要通过**静默队列**来持久化请求信息,以及保证请求时序问题,通过**虚拟数据**来作为服务端响应数据的占位符,当请求完成后替换为实际的响应数据,通过这两项技术实现了本地化的数据创建,并对新建数据进行编辑、删除等操作,即使创建的数据还未真正在服务端提交成功。为了让开发成本降到最低,这些在 alova 中都是自动完成的。 - -### 静默队列 - -静默队列用于保证请求时序问题,我们可以任意创建队列,进入队列的请求都将会以**SilentMethod**实例的形式保存在队列中,每个**SilentMethod**除了包含请求信息外,还包含静默提交的相关配置,如*唯一 id*、*错误重试参数*等。队列内的请求只有在前一个响应后才会发起下一个请求,从而保证队列内的请求时序。你可以将有依赖关系的请求放到同一个队列中,这样也可以保证数据的一致性。 - -![静默队列](https://user-images.githubusercontent.com/29848971/220057005-dd467392-4a43-45a7-90dc-999dd1d95531.png) - -在方案中,还分别提供了`queue`、`silent`、`static`三种行为模式,用于区分一个请求需要以怎样的行为进行请求。 - -- queue:请求将进入静默队列,但不会被持久化,将等待前面的请求完成才发送请求,响应回调将会在响应后触发,一般用于需要依赖前项请求的数据获取; -- silent:请求将进入静默队列,且会被持久化,然后立即触发响应回调,这种行为模式下,onSuccess 将接收到虚拟数据,onError 永远不会被触发,在进行提交即响应的场景下需使用此模式; -- static:请求不会进入静默队列,也不会被持久化,它会立即发出请求,在禁用静默提交时可使用此模式; - -### 虚拟数据 - -在提交即响应的机制中,虚拟数据起到了重要的作用,它表示在服务端真正响应之前,作为响应数据的替代数据进行占位,并通过追查机制,虚拟数据即使分布在应用各个位置,也能在响应后自动替换为实际的响应数据。同时在静默队列中也起到了重要作用,它可以标识队列内请求的依赖关系,并在依赖项响应后将依赖数据替换为实际数据,例如创建一条数据时将返回这条数据的 id,当服务端还未响应时,用户又进行了删除操作,需要将 id 作为删除标识,此时删除请求将依赖创建请求。在创建请求响应前,虚拟数据将作为 id 占位符作为删除的参数,并在创建请求响应后替换虚拟数据 id,至此就可以完成删除的请求。 - -![虚拟数据](https://user-images.githubusercontent.com/29848971/220005505-d30b7ae2-ddd0-4080-81a4-65c9be2cb0bd.png) - -接下来,我们将会具体了解虚拟数据的特性。 +--- +title: 无感数据交互 - 概览 +--- + +无感数据交互是指用户在与应用进行交互时,无需等待即可立即展示相关内容,或者提交信息时也无需等待即可展示操作结果,就像和本地数据交互一样,从而大幅提升应用的流畅性,它让用户感知不到数据传输带来的卡顿。 + +这并不是新鲜事,早在 2015 年以前就已经有乐观更新的概念了,它是指在服务端响应前将提交结果展示到界面中,它是建立在大部分的提交都成功的假设上,与之相对的是保守更新,即服务端响应前将展示等待状态,直到请求完成。在处理失败方面,目前的乐观更新方案通常是通过回退来处理,例如下面的示例代码: + +```javascript +const list = []; +const data = {}; +addTodo(data).catch(() => { + list = list.filter(item => item !== data); +}); +list.push(data); +``` + +这可能会导致以下问题: + +1. 回退将增加用户的理解成本和操作成本; +2. 请求时序问题; +3. 如果后续的请求存在对本次提交依赖,则本次失败将导致后续的请求变得没有意义; +4. 可能丢失请求; + +经过数个月的方案设计和不断迭代,alova 已经在这方面走出了一大步,在我们的方案中对以上存在的问题进行了解决,可以更稳定地保证请求成功,虽然还存在技术限制,但在许多场景中得到了应用。在我们的技术方案中,可以更高限度地降低网络波动带来的问题,你的应用在高延迟网络甚至是断网状态下依然可用,在刷新页面后也依然能保持最新状态的数据。 + +## 应用场景 + +无感数据交互虽然不能大规模地使用,但在一定场景下它又是非常合适的,在探索中,我们发现了至少包含但不限于以下几个场景,供你参考。 + +### 编辑器类应用 + +笔记类应用如印象笔记,画布编辑类应用如 MasterGO,它们分别需要实现以下需求: + +1. 进入笔记或图纸列表时将全量拉取列表数据,下次进入将使用本地的缓存数据; +2. 编辑过程中实时同步到服务端,且同步过程发生在后台,不会影响用户正常使用; +3. 网络差或断网状态下也可以继续使用; + + + +### 设置模块 + +由常用的开关、选择器组成的设置模块,需实现的需求是,用户操作后实时同步到服务端,同时不再展示提交状态,而是直接展示操作后的最新状态。 + + + +### 简单的列表管理 + +我们将创建列表项时填写的数据足够用于列表页的展示,称为简单的列表,例如一个学生列表页展示学生的姓名、性别、年级三个数据,这三个数据在创建学生时都需要填写。在简单列表中将实现以下需求: + +1. 在新增、编辑和删除列表项时立即在列表页中展示最新状态,无需在提交完成后才迟迟展示,同时也不受网络波动限制; +2. 刷新页面时,列表页始终保持最新状态; + + + +### 复杂的列表管理 + +复杂列表是指,创建列表项时填写的数据不足以在列表页用于展示,而需要根据服务端的计算产生额外的数据,例如一个 Todo 列表页除了展示基本信息外,还需要列出具体的执行日期,而在创建页只指定了执行日期范围和相关规则,因此执行日期由服务端根据日期范围和规则统一计算生成。 + +在复杂列表中将实现以下需求: + +1. 在新增、编辑和删除列表项时立即在列表页中展示最新状态,并在服务端响应后将服务端计算的数据更新到此列表项中; +2. 刷新页面时,列表页始终保持最新状态; + +:::info 示例 + +复杂列表示例敬请期待... + +::: + +### 自由模式 + +在以上的几种场景下,你可能希望根据一个条件来判断当前是使用无感交互策略还是最常见的保守请求策略,需求如下: + +1. 当网络状态良好时,或付费用户将使用无感交互策略,而网络波动大时,或免费用户则不能享受到无感交互策略; +2. 策略自由切换; + +:::info 示例 + +在以上的示例中你都可以体验自由切换策略 + +::: + +## 不推荐的应用场景 + +### 信息共享类 + +提交的信息需要同步给其他人,例如订单信息,这类信息具有高实时性的要求,我们应该确保提交成功。 + +### 复杂的数据交互类 + +复杂的数据交互是指,数据的编辑和筛选混合进行,例如对某个列表混合进行新增、编辑、删除和筛选,在这种情况下 alova 目前还无法很好地支持,在后续的版本中也将尝试解决这个难题。 + +## 技术方案 + +在无感数据交互的技术方案上,alova 分别实现了数据预拉取和静默提交,接下来我们来了解这两种技术方案。 + +:::info + +阅读前请确保已经掌握了以下章节内容 + +- [基础学习](/next/tutorial/getting-started/quick-start) + +::: + +### 数据预拉取 + +在 html 中你可能见过这样的标签``,它告诉浏览器在闲时去预加载样式文件,并放在缓存中,当真正需要使用的时候从缓存中取出即可,alova 也使用了类似的方案,通过[useFetcher](/next/tutorial/client/strategy/use-fetcher)来预拉取需要的数据,它将保存在本地缓存中。你可以在任意情况下预判用户的需要阅读的内容,然后预先拉取对应的内容,如在流程类的页面中可以预加载下一页的内容,又或者,用户在某个按钮上停留了 200ms,我们可以预先拉取下一个界面需要的数据,这点类似于**Next.js**的页面预加载。 + + + +### 静默提交 + +静默提交是一种提交即响应的机制,方案中将保证提交的完成,因此可以将它看作更安全的乐观更新方案。静默提交主要通过**静默队列**来持久化请求信息,以及保证请求时序问题,通过**虚拟数据**来作为服务端响应数据的占位符,当请求完成后替换为实际的响应数据,通过这两项技术实现了本地化的数据创建,并对新建数据进行编辑、删除等操作,即使创建的数据还未真正在服务端提交成功。为了让开发成本降到最低,这些在 alova 中都是自动完成的。 + +### 静默队列 + +静默队列用于保证请求时序问题,我们可以任意创建队列,进入队列的请求都将会以**SilentMethod**实例的形式保存在队列中,每个**SilentMethod**除了包含请求信息外,还包含静默提交的相关配置,如*唯一 id*、*错误重试参数*等。队列内的请求只有在前一个响应后才会发起下一个请求,从而保证队列内的请求时序。你可以将有依赖关系的请求放到同一个队列中,这样也可以保证数据的一致性。 + +![静默队列](https://user-images.githubusercontent.com/29848971/220057005-dd467392-4a43-45a7-90dc-999dd1d95531.png) + +在方案中,还分别提供了`queue`、`silent`、`static`三种行为模式,用于区分一个请求需要以怎样的行为进行请求。 + +- queue:请求将进入静默队列,但不会被持久化,将等待前面的请求完成才发送请求,响应回调将会在响应后触发,一般用于需要依赖前项请求的数据获取; +- silent:请求将进入静默队列,且会被持久化,然后立即触发响应回调,这种行为模式下,onSuccess 将接收到虚拟数据,onError 永远不会被触发,在进行提交即响应的场景下需使用此模式; +- static:请求不会进入静默队列,也不会被持久化,它会立即发出请求,在禁用静默提交时可使用此模式; + +### 虚拟数据 + +在提交即响应的机制中,虚拟数据起到了重要的作用,它表示在服务端真正响应之前,作为响应数据的替代数据进行占位,并通过追查机制,虚拟数据即使分布在应用各个位置,也能在响应后自动替换为实际的响应数据。同时在静默队列中也起到了重要作用,它可以标识队列内请求的依赖关系,并在依赖项响应后将依赖数据替换为实际数据,例如创建一条数据时将返回这条数据的 id,当服务端还未响应时,用户又进行了删除操作,需要将 id 作为删除标识,此时删除请求将依赖创建请求。在创建请求响应前,虚拟数据将作为 id 占位符作为删除的参数,并在创建请求响应后替换虚拟数据 id,至此就可以完成删除的请求。 + +![虚拟数据](https://user-images.githubusercontent.com/29848971/220005505-d30b7ae2-ddd0-4080-81a4-65c9be2cb0bd.png) + +接下来,我们将会具体了解虚拟数据的特性。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/_category_.json b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/09-sensorless-data-interaction/_category_.json similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/09-sensorless-data-interaction/_category_.json rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/09-sensorless-data-interaction/_category_.json diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/10-use-captcha.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/10-use-captcha.md similarity index 89% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/10-use-captcha.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/10-use-captcha.md index 3b9b1852f..9b1bd56cd 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/10-use-captcha.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/10-use-captcha.md @@ -1,6 +1,5 @@ --- title: 发送验证码 -sidebar_position: 100 --- import Tabs from '@theme/Tabs'; @@ -16,9 +15,9 @@ use hook 验证码发送 hook,减掉你在开发验证码发送功能时的繁琐。 -## 示例 + ## 特性 @@ -147,7 +146,7 @@ useCaptcha(() => apiSendCaptcha(mobile.value), { ### Hook 配置 -继承[**useRequest**](/api/core-hooks#userequest)除`immediate`外的所有配置,`useCaptcha`中`immediate`已硬编码为 false。 +继承[**useRequest**](/next/api/core-hooks#userequest)除`immediate`外的所有配置,`useCaptcha`中`immediate`已硬编码为 false。 | 名称 | 描述 | 类型 | 默认值 | 版本 | | ---------------- | ------------------------------------------------------ | ------ | ------ | ---- | @@ -155,7 +154,7 @@ useCaptcha(() => apiSendCaptcha(mobile.value), { ### 响应式数据 -继承[**useRequest**](/api/core-hooks#userequest)所有响应式数据。 +继承[**useRequest**](/next/api/core-hooks#userequest)所有响应式数据。 | 名称 | 描述 | 类型 | 版本 | | --------- | ------------------------------------------------------ | ------ | ---- | @@ -163,7 +162,7 @@ useCaptcha(() => apiSendCaptcha(mobile.value), { ### 操作函数 -继承[**useRequest**](/api/core-hooks#userequest)所有操作函数。 +继承[**useRequest**](/next/api/core-hooks#userequest)所有操作函数。 | 名称 | 描述 | 函数参数 | 返回值 | 版本 | | ---- | ---------------------------------------- | ----------------------- | ------------------- | ---- | @@ -171,4 +170,4 @@ useCaptcha(() => apiSendCaptcha(mobile.value), { ### 事件 -继承[**useRequest**](/api/core-hooks#userequest)所有事件。 +继承[**useRequest**](/next/api/core-hooks#userequest)所有事件。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/11-use-serial-request.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/11-use-serial-request.md similarity index 78% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/11-use-serial-request.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/11-use-serial-request.md index 21166fe9b..1c7672dac 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/11-use-serial-request.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/11-use-serial-request.md @@ -1,6 +1,5 @@ --- title: 串行请求的useRequest -sidebar_position: 110 --- import Tabs from '@theme/Tabs'; @@ -14,7 +13,7 @@ use hook > 在使用扩展 hooks 前,确保你已熟悉了 alova 的基本使用。 -这个 use hook 比[在最佳实践中的串行请求](/tutorial/best-practice/skills)更加简洁易用,统一的 loading 状态、error、回调函数。 +这个 use hook 比[在最佳实践中的串行请求](/next/tutorial/project/best-practice/serial-request)更加简洁易用,统一的 loading 状态、error、回调函数。 ## 特性 @@ -22,9 +21,9 @@ use hook - 统一的请求状态和回调函数; - send 函数可触发多个请求串行执行; -## 示例 + ## 使用 @@ -92,16 +91,16 @@ useSerialRequest([ ### Hook 配置 -继承[**useRequest**](/api/core-hooks#userequest)所有配置。 +继承[**useRequest**](/next/api/core-hooks#userequest)所有配置。 ### 响应式数据 -继承[**useRequest**](/api/core-hooks#userequest)所有响应式数据。 +继承[**useRequest**](/next/api/core-hooks#userequest)所有响应式数据。 ### 操作函数 -继承[**useRequest**](/api/core-hooks#userequest)所有操作函数。 +继承[**useRequest**](/next/api/core-hooks#userequest)所有操作函数。 ### 事件 -继承[**useRequest**](/api/core-hooks#userequest)所有事件。 +继承[**useRequest**](/next/api/core-hooks#userequest)所有事件。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/12-use-serial-watcher.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/12-use-serial-watcher.md similarity index 80% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/12-use-serial-watcher.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/12-use-serial-watcher.md index 4a2d6a01d..d35959dd2 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/12-use-serial-watcher.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/12-use-serial-watcher.md @@ -1,6 +1,5 @@ --- title: 串行请求的useWatcher -sidebar_position: 120 --- import Tabs from '@theme/Tabs'; @@ -14,7 +13,7 @@ use hook > 在使用扩展 hooks 前,确保你已熟悉了 alova 的基本使用。 -状态更新触发一组串行请求,比[在最佳实践中的串行请求](/tutorial/best-practice/skills)更加简洁易用,统一的 loading 状态、error、回调函数。 +状态更新触发一组串行请求,比[在最佳实践中的串行请求](/next/tutorial/project/best-practice/serial-request)更加简洁易用,统一的 loading 状态、error、回调函数。 ## 特性 @@ -22,9 +21,9 @@ use hook - 统一的请求状态和回调函数; - 状态更新触发多个请求串行执行; -## 示例 + ## 使用 @@ -93,16 +92,16 @@ useSerialRequest([ ### Hook 配置 -继承[**useWatcher**](/api/core-hooks#usewatcher)所有配置。 +继承[**useWatcher**](/next/api/core-hooks#usewatcher)所有配置。 ### 响应式数据 -继承[**useWatcher**](/api/core-hooks#usewatcher)所有响应式数据。 +继承[**useWatcher**](/next/api/core-hooks#usewatcher)所有响应式数据。 ### 操作函数 -继承[**useWatcher**](/api/core-hooks#usewatcher)所有操作函数。 +继承[**useWatcher**](/next/api/core-hooks#usewatcher)所有操作函数。 ### 事件 -继承[**useWatcher**](/api/core-hooks#usewatcher)所有事件。 +继承[**useWatcher**](/next/api/core-hooks#usewatcher)所有事件。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/13-use-retriable-request.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/13-use-retriable-request.md similarity index 96% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/13-use-retriable-request.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/13-use-retriable-request.md index 3263875ba..2c0055845 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/13-use-retriable-request.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/13-use-retriable-request.md @@ -1,6 +1,5 @@ --- title: 请求重试策略 -sidebar_position: 130 --- import Tabs from '@theme/Tabs'; @@ -16,9 +15,9 @@ use hook 请求失败可自动重试的 use hook,你可以将它用于重要的请求上。 -## 示例 + ## 特性 @@ -177,7 +176,7 @@ const handleStop = () => { ### Hook 配置 -继承[**useRequest**](/api/core-hooks#userequest)所有配置。 +继承[**useRequest**](/next/api/core-hooks#userequest)所有配置。 | 名称 | 描述 | 类型 | 默认值 | 版本 | | ------- | ------------------------------------------------------------------------- | ------------------------------- | ---------------------------------------------- | ---- | @@ -195,11 +194,11 @@ const handleStop = () => { ### 响应式数据 -继承[**useRequest**](/api/core-hooks#userequest)所有响应式数据。 +继承[**useRequest**](/next/api/core-hooks#userequest)所有响应式数据。 ### 操作函数 -继承[**useRequest**](/api/core-hooks#userequest)所有操作函数。 +继承[**useRequest**](/next/api/core-hooks#userequest)所有操作函数。 | 名称 | 描述 | 函数参数 | 返回值 | 版本 | | ---- | ------------------------------------------------------------ | -------- | ------ | ---- | @@ -207,7 +206,7 @@ const handleStop = () => { ### 事件 -继承[**useRequest**](/api/core-hooks#userequest)所有事件。 +继承[**useRequest**](/next/api/core-hooks#userequest)所有事件。 | 名称 | 描述 | 回调参数 | 版本 | | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | ---- | diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/14-use-sse.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/14-use-sse.md similarity index 97% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/14-use-sse.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/14-use-sse.md index defff9c79..86458e3b2 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/14-use-sse.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/14-use-sse.md @@ -1,6 +1,5 @@ --- title: Server-sent events发送请求 -sidebar_position: 140 --- import Tabs from '@theme/Tabs'; @@ -159,7 +158,7 @@ on('event-name', ({ data }) => { ### 全局响应拦截 -默认情况下,响应数据受到[全局响应拦截器的捕获](/tutorial/combine-framework/response)。如果这不是你预期的行为,可以手动关闭。 +默认情况下,响应数据受到[全局响应拦截器的捕获](/next/tutorial/getting-started/basic/global-interceptor)。如果这不是你预期的行为,可以手动关闭。 ```typescript const { data, readyState, onMessage, on } = useSSE(method, { diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/15-use-breakpoint-uploader.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/15-use-breakpoint-uploader.md similarity index 69% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/15-use-breakpoint-uploader.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/15-use-breakpoint-uploader.md index 453a98a5a..2c19b9dff 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/15-use-breakpoint-uploader.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/15-use-breakpoint-uploader.md @@ -1,6 +1,5 @@ --- title: 断点续传策略 -sidebar_position: 150 --- 敬请期待... diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/16-use-uploader.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/16-use-uploader.md similarity index 98% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/16-use-uploader.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/16-use-uploader.md index 70bf48f1d..2f7f72f2e 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/16-use-uploader.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/16-use-uploader.md @@ -1,6 +1,5 @@ --- title: 通用的上传策略 -sidebar_position: 160 --- :::warning @@ -168,7 +167,7 @@ interface AlovaFileCompleteEvent extends AlovaCompleteEvent { ## 开发指南 -开发前请仔细阅读[开发指南](/contributing/developing-guidelines) +开发前请仔细阅读[开发指南](/next/contributing/developing-guidelines) 开发此 use hook 需要开发以下内容: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/17-rate-limit-middleware.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/17-rate-limit-middleware.md similarity index 85% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/17-rate-limit-middleware.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/17-rate-limit-middleware.md index b8c2486f6..f899928a3 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/17-rate-limit-middleware.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/17-rate-limit-middleware.md @@ -1,6 +1,5 @@ --- title: 请求速率限制 -sidebar_position: 170 --- 设置每个间隔应立即执行的请求数,其他请求将自动延迟。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/README.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/README.md similarity index 89% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/README.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/README.md index 0cce3bc14..76d38da3c 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/README.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/README.md @@ -8,7 +8,7 @@ import DocCardList from '@theme/DocCardList'; 所有的客户端 use hooks 都有以下共同点: -1. 它们都依赖 statesHook,请在使用前[设置 statesHook](/tutorial/getting-started/combine-framework)。 +1. 它们都依赖 statesHook,请在使用前[设置 statesHook](/next/tutorial/getting-started/basic/combine-framework)。 2. 它们的返回值中都包含`update`函数,用于主动更新导出的状态值。 3. 在 react 下为了性能表现,所有的操作函数例如`send`、`update`、`abort`等都使用了`useCallback`包装过。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/01-update-across-components.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/02-in-depth/01-update-across-components.md similarity index 84% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/01-update-across-components.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/02-in-depth/01-update-across-components.md index 63ee4d609..a8afaac2f 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/01-update-across-components.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/02-in-depth/01-update-across-components.md @@ -1,6 +1,5 @@ --- title: 跨组件更新状态 -sidebar_position: 20 --- :::info 使用范围 @@ -13,7 +12,7 @@ sidebar_position: 20 此时可以使用`updateState`来更新任意模块/页面下的已存在的响应状态,它可以查找并修改其他模块内的响应式状态。 -[这里有个`updateState`的 示例](/tutorial/example/update-state) + ## 使用 method 实例查找响应状态 @@ -58,7 +57,7 @@ onSuccess(() => { ## 动态更新响应状态 -可能有时候你并不确定需要更新 method 下的响应状态,但却知道以什么方式来找到需要失效的缓存数据,我们可以使用 [method 匹配器](/tutorial/advanced/method-matcher) 来动态查找对应的 method 实例。以下例子展示了为名称为 todoList 的 method 实例对应的列表添加一条数据。 +可能有时候你并不确定需要更新 method 下的响应状态,但却知道以什么方式来找到需要失效的缓存数据,我们可以使用 [method 匹配器](/next/tutorial/client/in-depth/method-matcher) 来动态查找对应的 method 实例。以下例子展示了为名称为 todoList 的 method 实例对应的列表添加一条数据。 ```javascript updateState('todoList', todoListRaw => { @@ -70,7 +69,7 @@ updateState('todoList', todoListRaw => { }); ``` -关于 [method 快照匹配器](/tutorial/advanced/method-matcher) 将在后面的章节中详细介绍。 +关于 [method 快照匹配器](/next/tutorial/client/in-depth/method-matcher) 将在后面的章节中详细介绍。 ## 监听匹配事件 @@ -98,7 +97,7 @@ updateState( 这个问题常常出现在跨页面更新状态,我们容易忽略的是,在默认情况下,当页面跳转时上一个页面已被销毁,因此,如果你希望跨页面更新状态,这里有两个建议: 1. 将页面组件持久化,以保证被更新的状态还可以被查找到; -2. 使用 [setCache](/tutorial/cache/set-and-query) 替代`updateState`,其原理是,当上一个页面的请求存在缓存时,更新它的缓存以保证再次创建页面时,所触发的请求可以命中更新后的缓存,达到同样的效果。 +2. 使用 [setCache](/next/tutorial/cache/set-and-query) 替代`updateState`,其原理是,当上一个页面的请求存在缓存时,更新它的缓存以保证再次创建页面时,所触发的请求可以命中更新后的缓存,达到同样的效果。 ::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/02-method-matcher.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/02-in-depth/02-method-matcher.md similarity index 89% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/02-method-matcher.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/02-in-depth/02-method-matcher.md index 940f0e952..279145bd8 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/02-method-matcher.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/02-in-depth/02-method-matcher.md @@ -1,6 +1,5 @@ --- title: method匹配器 -sidebar_position: 30 --- :::info 使用范围 @@ -13,11 +12,11 @@ method 快照匹配器是一个在已请求的 method 快照列表中动态查 它一般与以下 5 个需要使用 method 实例的函数中使用。 -1. [setCache](/tutorial/cache/set-and-query) -2. [queryCache](/tutorial/cache/set-and-query) -3. [invalidateCache](/tutorial/cache/manually-invalidate) -4. [updateState](/tutorial/advanced/update-across-components) -5. [useFetcher.fetch](/tutorial/client/use-fetcher) +1. [setCache](/next/tutorial/cache/set-and-query) +2. [queryCache](/next/tutorial/cache/set-and-query) +3. [invalidateCache](/next/tutorial/cache/manually-invalidate) +4. [updateState](/next/tutorial/client/in-depth/update-across-components) +5. [useFetcher.fetch](/next/tutorial/client/strategy/use-fetcher) ## 匹配规则 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/04-middleware.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/02-in-depth/03-middleware.md similarity index 98% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/04-middleware.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/02-in-depth/03-middleware.md index eb4065c25..180ac13b9 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/04-middleware.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/02-in-depth/03-middleware.md @@ -1,6 +1,5 @@ --- title: 请求中间件 -sidebar_position: 40 --- import Tabs from '@theme/Tabs'; @@ -210,7 +209,7 @@ async function middleware(context, next) { } ``` -关于状态代理的详细用法,请参考[状态代理](/docs/guide/state-proxy)。 +关于状态代理的详细用法,请参考[状态代理](/next/tutorial/advanced/custom/client-strategy)。 ## 中断或重复发送请求 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/08-manage-extra-states.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/02-in-depth/04-manage-extra-states.md similarity index 91% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/08-manage-extra-states.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/02-in-depth/04-manage-extra-states.md index 03f4714ab..319bc0075 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/08-manage-extra-states.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/02-in-depth/04-manage-extra-states.md @@ -1,6 +1,5 @@ --- title: 管理额外的状态 -sidebar_position: 80 --- :::info 使用范围 @@ -12,7 +11,7 @@ sidebar_position: 80 import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -在之前的[跨页面/模块更新响应状态](/tutorial/advanced/update-across-components)章节中,介绍了如何通过`updateState`跨页面或模块更新响应状态,但它只能更新由 useHooks 创建的状态,如果需要跨组件更新自定义的状态,应该怎么做呢?让我们继续吧! +在之前的[跨页面/模块更新响应状态](/next/tutorial/client/in-depth/update-across-components)章节中,介绍了如何通过`updateState`跨页面或模块更新响应状态,但它只能更新由 useHooks 创建的状态,如果需要跨组件更新自定义的状态,应该怎么做呢?让我们继续吧! ## 更新单个状态 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/_category_.json b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/_category_.json deleted file mode 100644 index d9b8c4b4a..000000000 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/_category_.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "label": "客户端策略" -} diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/01-retry.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/01-strategy/01-retry.md similarity index 97% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/01-retry.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/01-strategy/01-retry.md index e3bfea830..4a8235262 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/01-retry.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/01-strategy/01-retry.md @@ -1,6 +1,5 @@ --- title: 请求重试策略 -sidebar_position: 10 --- :::info 类型 @@ -123,6 +122,6 @@ retry(request, { }); ``` -## API + diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/02-send-captcha.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/01-strategy/02-send-captcha.md similarity index 89% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/02-send-captcha.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/01-strategy/02-send-captcha.md index f5a13670a..af3dd8f63 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/02-send-captcha.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/01-strategy/02-send-captcha.md @@ -1,6 +1,5 @@ --- title: 发送验证码 -sidebar_position: 20 --- 通过 sms 或 email 发送验证码,并根据 key 记录倒计时时间,在倒计时内再次发送验证码将报错。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/03-rate-limit-middleware.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/01-strategy/03-rate-limit-middleware.md similarity index 86% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/03-rate-limit-middleware.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/01-strategy/03-rate-limit-middleware.md index 2535e85eb..f899928a3 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/03-rate-limit-middleware.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/01-strategy/03-rate-limit-middleware.md @@ -1,6 +1,5 @@ --- title: 请求速率限制 -sidebar_position: 30 --- 设置每个间隔应立即执行的请求数,其他请求将自动延迟。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/README.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/01-strategy/README.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/README.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/01-strategy/README.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/_category_.json b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/_category_.json deleted file mode 100644 index aeaecc6d6..000000000 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/04-server/_category_.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "label": "服务端策略" -} diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/01-mode.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/01-mode.md index f759085c7..72a03824f 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/01-mode.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/01-mode.md @@ -1,6 +1,5 @@ --- title: 缓存模式 -sidebar_position: 10 --- import MemoryCache from '@site/example-links/MemoryCache'; @@ -112,7 +111,7 @@ const alovaInstance = createAlova({ }); ``` -内存缓存模式对应 l1 cache,在此我们更换了缓存适配器为进程间共享的 lru-cache,你也可以[自定义存储适配器](/tutorial/custom/custom-storage-adapter),例如当你只需要单级缓存时,你也可以直接将 l1 cache 设置为 redis 适配器。 +内存缓存模式对应 l1 cache,在此我们更换了缓存适配器为进程间共享的 lru-cache,你也可以[自定义存储适配器](/next/tutorial/advanced/custom/storage-adapter),例如当你只需要单级缓存时,你也可以直接将 l1 cache 设置为 redis 适配器。 ## 恢复模式 @@ -137,7 +136,7 @@ flowchart LR 在客户端中,当还未过期的缓存即使刷新页面缓存也不会失效,它一般用于一些需要服务端管理,但基本不变的数据,如每年的节假日具体日期有所不同,但不会再变动,这种场景下我们只需设置缓存过期时间为今年的最后一刻即可。 -客户端中使用 alova 时默认使用`localStorage`作为 L2 存储适配器,你也可以[自定义存储适配器](/tutorial/custom/custom-storage-adapter)。 +客户端中使用 alova 时默认使用`localStorage`作为 L2 存储适配器,你也可以[自定义存储适配器](/next/tutorial/advanced/custom/storage-adapter)。 在 method 实例上设置: @@ -186,7 +185,7 @@ const todoListGetter = alovaInstance.Get('/todo/list', { 3. 整合多个下游服务器的数据合并和处理,多个串行请求可能导致更长的响应时间,也可能因复杂的数据转换消耗性能,可将转换后的数据进行缓存。 4. API 速率限制和计费,天气预报服务 API 每小时更新一次天气信息,地理位置数据 API 等。 -在服务端使用 alova 时默认没有 L2 存储适配器,在[服务端 L2 存储实践](/tutorial/best-practice/l2-storage)中分别提供了文件存储适配器和 redis 适配器的实现。你也可以[自定义存储适配器](/tutorial/custom/custom-storage-adapter),例如将 MongoDB、mysql 等数据库作为响应数据的存储适配器。 +在服务端使用 alova 时默认没有 L2 存储适配器,在[服务端 L2 存储实践](/next/tutorial/project/best-practice/l2-storage)中分别提供了文件存储适配器和 redis 适配器的实现。你也可以[自定义存储适配器](/next/tutorial/advanced/custom/storage-adapter),例如将 MongoDB、mysql 等数据库作为响应数据的存储适配器。 :::warning 注意 @@ -326,4 +325,4 @@ cacheFor: { ## 响应自动维护说明 -响应数据缓存的 key 是由 method 实例的请求方法(method)、请求地址(url)、请求头参数(headers)、url 参数(params)、请求体参数(requestBody)组合作为唯一标识,任意一个信息或位置不同都将被当做不同的 key,如果要自定义缓存 key,可以参考[自定义 method key](/tutorial/advanced/custom-method-key)。 +响应数据缓存的 key 是由 method 实例的请求方法(method)、请求地址(url)、请求头参数(headers)、url 参数(params)、请求体参数(requestBody)组合作为唯一标识,任意一个信息或位置不同都将被当做不同的 key,如果要自定义缓存 key,可以参考[自定义 method key](/next/tutorial/advanced/in-depth/custom-method-key)。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/02-auto-invalidate.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/02-auto-invalidate.md index 40845c678..0250b5804 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/02-auto-invalidate.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/02-auto-invalidate.md @@ -1,6 +1,5 @@ --- title: 自动失效 -sidebar_position: 20 --- 自动失效缓存是在目标缓存中设置失效源 method 的匹配规则,当源 method 在请求成功后,将自动匹配并失效目标缓存,不需要再手动清除缓存。当目标缓存与失效源是一对一或一对多时,设置自动失效规则会很方便。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/03-manually-invalidate.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/03-manually-invalidate.md index 5bbad031c..fce94e945 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/03-manually-invalidate.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/03-manually-invalidate.md @@ -1,6 +1,5 @@ --- title: 手动失效 -sidebar_position: 30 --- 通常,自动失效缓存更加简洁,并且推荐优先使用它来失效缓存,当自动失效缓存不满足需求时,你还可以通过调用`invalidateCache`来失效缓存。 @@ -53,7 +52,7 @@ invalidateCache([method1, method2, ...]); ## 动态失效缓存 -可能有时候你并不确定需要失效哪个缓存数据,我们可以使用 [method 快照匹配器](/tutorial/advanced/method-matcher) 来动态查找对应的 method 实例。以下例子展示了如何让名称为 todoList 的前 5 个 method 实例的缓存失效。 +可能有时候你并不确定需要失效哪个缓存数据,我们可以使用 [method 快照匹配器](/next/tutorial/client/in-depth/method-matcher) 来动态查找对应的 method 实例。以下例子展示了如何让名称为 todoList 的前 5 个 method 实例的缓存失效。 ```javascript const getTodoList = currentPage => { @@ -89,7 +88,7 @@ onSuccess(() => { }); ``` -> 更多 method 匹配器的使用方法见 [method 快照匹配器](/tutorial/advanced/method-matcher) +> 更多 method 匹配器的使用方法见 [method 快照匹配器](/next/tutorial/client/in-depth/method-matcher) ## 失效所有缓存 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/04-force-request.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/04-force-request.md index 7b86c2724..0d62252a8 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/04-force-request.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/04-force-request.md @@ -1,6 +1,5 @@ --- title: 强制请求 -sidebar_position: 40 --- 强制请求是指绕过缓存的检查触发请求发送的机制,当需要在一定条件下获取最新的数据时很有用。 @@ -15,4 +14,4 @@ const response = await alovaInstance.Get('/api/user').send(true); ## 在 useHook 中强制请求 -请前往[自动管理请求状态-强制请求](/tutorial/client/use-request#强制请求)中查看详情。 +请前往[自动管理请求状态-强制请求](/next/tutorial/client/strategy/use-request#强制请求)中查看详情。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/05-set-and-query.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/05-set-and-query.md index 06af8d0d5..58a2971c2 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/05-set-and-query.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/05-set-and-query.md @@ -1,12 +1,11 @@ --- title: 更新与查找缓存 -sidebar_position: 50 --- import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -缓存也支持更新和查找,在[缓存模式](/tutorial/cache/mode)中我们提到过,每份缓存数据是以发送请求的 method 实例作为 key 进行保存的,因此在手动更新缓存时也将使用 method 实例来查找对应的缓存数据。 +缓存也支持更新和查找,在[缓存模式](/next/tutorial/cache/mode)中我们提到过,每份缓存数据是以发送请求的 method 实例作为 key 进行保存的,因此在手动更新缓存时也将使用 method 实例来查找对应的缓存数据。 ## 更新缓存 @@ -64,7 +63,7 @@ import { queryCache } from 'alova'; const cachedData = await queryCache(getTodoListByDate('2022-10-01')); ``` -你也可以通过 [method 快照匹配器](/tutorial/advanced/method-matcher) 动态查找 method 实例。 +你也可以通过 [method 快照匹配器](/next/tutorial/client/in-depth/method-matcher) 动态查找 method 实例。 ```javascript const lastMethod = alovaInstance.snapshots.match('todoList', true); diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/06-controlled-cache.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/06-controlled-cache.md index af56baf26..8fa6183a3 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/06-controlled-cache.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/06-controlled-cache.md @@ -1,6 +1,5 @@ --- title: 受控的缓存 -sidebar_position: 60 --- 在发送一个请求时,默认会优先匹配响应缓存,在某些情况下,你可能需要使用`IndexedDB`作为缓存管理方案,使用自定义的`IndexedDB`适配器,但这会让所有请求都使用它作为存储方案,而受控的缓存则可以让你从单个请求上控制自定义的缓存。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/05-custom-method-key.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/01-in-depth/05-custom-method-key.md similarity index 98% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/05-custom-method-key.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/01-in-depth/05-custom-method-key.md index 6681806e1..288a3918c 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/05-custom-method-key.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/01-in-depth/05-custom-method-key.md @@ -1,6 +1,5 @@ --- title: 自定义method key -sidebar_position: 50 --- :::info 使用范围 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/07-cache-logger.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/01-in-depth/07-cache-logger.md similarity index 98% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/07-cache-logger.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/01-in-depth/07-cache-logger.md index 1a1e9b679..3a6f42e1b 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/07-cache-logger.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/01-in-depth/07-cache-logger.md @@ -1,6 +1,5 @@ --- title: 缓存命中日志 -sidebar_position: 70 --- :::info 使用范围 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/09-ssr.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/01-in-depth/09-ssr.md similarity index 96% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/09-ssr.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/01-in-depth/09-ssr.md index b4e167855..71d314a8e 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/09-ssr.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/01-in-depth/09-ssr.md @@ -1,6 +1,5 @@ --- title: 服务端渲染(SSR) -sidebar_position: 90 --- import Tabs from '@theme/Tabs'; @@ -37,7 +36,7 @@ SSR 中经常需要在服务端获取数据并渲染成 HTML,这种情况下 ### Nextjs -Nextjs 提供了固定的服务端初始化页面数据的函数,如`getStaticProps`、`getServerSideProps`等,可以在函数中[直接使用 method 实例](/tutorial/getting-started/quick-start)调用接口。 +Nextjs 提供了固定的服务端初始化页面数据的函数,如`getStaticProps`、`getServerSideProps`等,可以在函数中[直接使用 method 实例](/next/tutorial/getting-started/quick-start)调用接口。 ```jsx export const getServerSideProps = async ctx => { @@ -64,7 +63,7 @@ export default function App(props) { ### Sveltekit -Sveltekit 中也提供了`load`函数进行服务端的页面数据初始化,你同样可以在函数中[直接使用 method 实例](/tutorial/getting-started/quick-start)调用接口。例如在`+page.server.js`中调用接口。 +Sveltekit 中也提供了`load`函数进行服务端的页面数据初始化,你同样可以在函数中[直接使用 method 实例](/next/tutorial/getting-started/quick-start)调用接口。例如在`+page.server.js`中调用接口。 ```javascript title=+page.server.js const todoListGetter = alovaInstance.Get('/todo/list', { diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/20-typescript.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/01-in-depth/10-typescript.md similarity index 86% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/20-typescript.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/01-in-depth/10-typescript.md index 5fa312aaf..49181413c 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/20-typescript.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/01-in-depth/10-typescript.md @@ -1,159 +1,173 @@ ---- -title: Typescript -sidebar_position: 100 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -在 Typescript 方面,我们确实花了很大的精力优化,为的就是提供更好的使用体验,我们尽力地使用自动推断类型来减少你定义类型的麻烦。 - -## 自动推断 alova useHooks 状态类型 - -在 createAlova 创建 alova 实例时会根据传入的`statesHook`自动推断出`useRequest`、`useWatcher`、`useFetcher`所创建的状态类型。 - -> `useFetcher`是一个用于数据拉取的 useHook,详情请阅读[进阶-数据拉取章节](/tutorial/advanced/use-fetcher)。 - -以下为预设中,useHooks 返回的状态类型。 - - - - -```typescript -const vueAlova = createAlova({ - statesHook: VueHook - // ... -}); -const { - loading, // Ref - data, // Ref<{ data: any }> - error // Ref -} = useRequest(vueAlova.Get<{ data: any }>('/todo/list')); -``` - - - - -```typescript -const reactAlova = createAlova({ - statesHook: ReactHook - // ... -}); -const { - loading, // boolean - data, // { data: any } - error // Error -} = useRequest(reactAlova.Get<{ data: any }>('/todo/list')); -``` - - - - -```typescript -const svelteAlova = createAlova({ - statesHook: SvelteHook - // ... -}); -const { - loading, // Writable - data, // Writable<{ data: any }> - error // Writable -} = useRequest(svelteAlova.Get<{ data: any }>('/todo/list')); -``` - - - - -data 的类型将会根据不同的 Method 实例中指定的响应数据类型而不同,我们继续往下看。 - -## 设置响应数据的类型 - -当你为一个数据接口指定类型时,需要分为两种情况。 - -### 情况 1 - -当响应数据不需要再调用`transformData`转换,直接通过泛型指定类型 - -```typescript -interface Todo { - title: string; - time: string; - done: boolean; -} -const Get = alovaInstance.Get('/todo/list'); -const { data } = useRequest(Get); -// vue: data的类型为Ref -// react: data的类型为Todo[] -// svelte: data的类型为Writable -``` - -### 情况 2 - -当响应数据需要再调用`transformData`转换,那就需要在转换函数参数中指定类型,然后它的返回值类型将会作为响应数据类型。 - -```typescript -interface Todo { - title: string; - time: string; - done: boolean; -} -const Get = alovaInstance.Get('/todo/list', { - // 将类型写到data参数中,而headers会自动推断,可以不用指定类型 - transformData(data: Todo[], headers) { - return data.map(item => ({ - ...item, - status: item.done ? '已完成' : '未完成' - })); - } -}); - -const { data } = useRequest(Get); -// vue: data的类型为Ref<(Todo & { status: string })[]> -// react: data的类型为(Todo & { status: string })[] -// svelte: data的类型为Writable<(Todo & { status: string })[]> -``` - -:::warning 注意 - -响应数据是经过全局响应拦截器转换后的,因此设置类型时也应该设置为转换后的类型。 - -::: - -## 根据请求适配器推断的类型 - -因为 alova 支持自定义请求适配器,而不同的适配器的请求配置对象、响应对象、响应头都可能不同,因此全局的`beforeRequest`、`responded`拦截器,以及 method 实例创建时的配置对象的类型,都会根据请求适配器提供的类型自动推断,我们先来看这几个类型。 - -如果你正在使用 [**GlobalFetch**](https://github.com/alovajs/alova/blob/main/src/predefine/GlobalFetch.ts),alova 将会使用`fetch api`的类型自动推断,fetch api 的类型如下。 - -```typescript -declare function fetch(input: RequestInfo | URL, init?: RequestInit): Promise; -``` - -### method 实例的配置类型 - -method 配置类型将被自动推断为: - -```typescript -// AlovaMethodCommonConfig为统一的请求参数和行为参数 -// highlight-start -const methodConfig: AlovaMethodCommonConfig & RequestInit = { - // highlight-end - // ... -}; -alovaInstance.Get('/api/user', methodConfig); -``` - -### 全局响应拦截器参数类型 - -responded 拦截器的类型将被自动推断为: - -```typescript -createAlova({ - // ... - // highlight-start - responded: (response: Response, method: Method) => { - // highlight-end - // ... - } -}); -``` +--- +title: Typescript +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +在 Typescript 方面,我们确实花了很大的精力优化,为的就是提供更好的使用体验,我们尽力地使用自动推断类型来减少你定义类型的麻烦。 + +## 自动推断 alova useHooks 状态类型 + +在 createAlova 创建 alova 实例时会根据传入的`statesHook`自动推断出`useRequest`、`useWatcher`、`useFetcher`等所有 useHooks 所创建的状态类型。 + +以下为预设中,useHooks 返回的状态类型。 + + + + +```typescript +const vueAlova = createAlova({ + statesHook: VueHook + // ... +}); +const { + loading, // Ref + data, // Ref<{ data: any }> + error // Ref +} = useRequest(vueAlova.Get<{ data: any }>('/todo/list')); +``` + + + + +```typescript +const reactAlova = createAlova({ + statesHook: ReactHook + // ... +}); +const { + loading, // boolean + data, // { data: any } + error // Error +} = useRequest(reactAlova.Get<{ data: any }>('/todo/list')); +``` + + + + +```typescript +const svelteAlova = createAlova({ + statesHook: SvelteHook + // ... +}); +const { + loading, // Writable + data, // Writable<{ data: any }> + error // Writable +} = useRequest(svelteAlova.Get<{ data: any }>('/todo/list')); +``` + + + + +data 的类型将会根据不同的 Method 实例中指定的响应数据类型而不同,我们继续往下看。 + +## 设置响应数据的类型 + +当你为一个数据接口指定类型时,需要分为两种情况。 + +### 情况 1 + +当响应数据不需要再调用`transformData`转换,直接通过泛型指定类型 + +```typescript +interface Todo { + title: string; + time: string; + done: boolean; +} +const Get = alovaInstance.Get('/todo/list'); +const { data } = useRequest(Get); +// vue: data的类型为Ref +// react: data的类型为Todo[] +// svelte: data的类型为Writable +``` + +### 情况 2 + +当响应数据需要再调用`transformData`转换,那就需要在转换函数参数中指定类型,然后它的返回值类型将会作为响应数据类型。 + +```typescript +interface Todo { + title: string; + time: string; + done: boolean; +} +const Get = alovaInstance.Get('/todo/list', { + // 将类型写到data参数中,而headers会自动推断,可以不用指定类型 + transformData(data: Todo[], headers) { + return data.map(item => ({ + ...item, + status: item.done ? '已完成' : '未完成' + })); + } +}); + +const { data } = useRequest(Get); +// vue: data的类型为Ref<(Todo & { status: string })[]> +// react: data的类型为(Todo & { status: string })[] +// svelte: data的类型为Writable<(Todo & { status: string })[]> +``` + +:::warning 注意 + +响应数据是经过全局响应拦截器转换后的,因此设置类型时也应该设置为转换后的类型。 + +::: + +## 根据请求适配器推断的类型 + +因为 alova 支持自定义请求适配器,而不同的适配器的请求配置对象、响应对象、响应头都可能不同,因此全局的`beforeRequest`、`responded`拦截器,以及 method 实例创建时的配置对象的类型,都会根据请求适配器提供的类型自动推断,我们先来看这几个类型。 + +如果你正在使用 `alova/fetch`,alova 将会使用`fetch api`的类型自动推断,fetch api 的类型如下。 + +```typescript +declare function fetch(input: RequestInfo | URL, init?: RequestInit): Promise; +``` + +### method 实例的配置类型 + +method 配置类型将被自动推断为: + +```typescript +// AlovaMethodCommonConfig为统一的请求参数和行为参数 +// highlight-start +const methodConfig: AlovaMethodCommonConfig & RequestInit = { + // highlight-end + // ... +}; +alovaInstance.Get('/api/user', methodConfig); +``` + +### 全局响应拦截器参数类型 + +responded 拦截器的类型将被自动推断为: + +```typescript +createAlova({ + // ... + // highlight-start + responded: (response: Response, method: Method) => { + // highlight-end + // ... + } +}); +``` + +## 自定义 meta 数据类型 + +默认情况下 method 元数据的类型为`any`,当你需要规范格式时,可以在项目的声明文件中配置以下类型。 + +```ts +import 'alova'; + +declare module 'alova' { + export interface AlovaCustomTypes { + meta: { + role: string; + errorModal: boolean; + }; + } +} +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/README.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/01-in-depth/README.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/README.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/01-in-depth/README.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-custom/01-http-adapter.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/02-custom/01-http-adapter.md similarity index 99% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-custom/01-http-adapter.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/02-custom/01-http-adapter.md index cdd503c7d..547f3033d 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-custom/01-http-adapter.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/02-custom/01-http-adapter.md @@ -1,6 +1,5 @@ --- title: 请求适配器 -sidebar_position: 10 --- 还记得如何创建一个 Alova 实例吗? diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-custom/02-storage-adapter.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/02-custom/02-storage-adapter.md similarity index 98% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-custom/02-storage-adapter.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/02-custom/02-storage-adapter.md index 080c78cac..2ffd81ca4 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-custom/02-storage-adapter.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/02-custom/02-storage-adapter.md @@ -1,6 +1,5 @@ --- title: 存储适配器 -sidebar_position: 20 --- import Tabs from '@theme/Tabs'; diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-custom/03-client-strategy.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/02-custom/03-client-strategy.md similarity index 97% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-custom/03-client-strategy.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/02-custom/03-client-strategy.md index e76722c1b..40bece0f4 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-custom/03-client-strategy.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/02-custom/03-client-strategy.md @@ -1,19 +1,18 @@ --- title: 客户端策略 -sidebar_position: 30 --- alova 的客户端策略分为 中间件、拦截器、useHook 三种,当你的项目需要自定义时,你可以参考这个章节。 ## 中间件 -中间件提供了强大的、几乎能控制一个请求的所有行为的能力,你可以通过它控制请求行为、自定义修改请求状态、错误处理等,详情请前往[请求中间件](/tutorial/advanced/middleware)查看,以下的源码可以告诉你中间件到底能做什么。 +中间件提供了强大的、几乎能控制一个请求的所有行为的能力,你可以通过它控制请求行为、自定义修改请求状态、错误处理等,详情请前往[请求中间件](/next/tutorial/client/in-depth/middleware)查看,以下的源码可以告诉你中间件到底能做什么。 - [actionDelegationMiddleware](https://github.com/alovajs/alova/blob/main/packages/client/src/middlewares/actionDelegation.ts) 通过中间件中实现了跨组件触发请求。 - [useSQRequest](https://github.com/alovajs/alova/blob/main/packages/client/src/hooks/silent/useSQRequest.ts) 在中间件中实现立即响应请求,无需等待。 - [useSerialRequest](https://github.com/alovajs/alova/blob/main/packages/client/src/hooks/serial/useSerialRequest.ts) 在中间件中串行请求并管理多个请求的响应数据。 - [useRetriableRequest](https://github.com/alovajs/alova/blob/main/packages/client/src/hooks/useRetriableRequest.ts) 在中间件中重试失败的请求。 -- [延迟更新 loading](/tutorial/best-practice/middleware)示例。 +- [延迟更新 loading](/next/tutorial/project/best-practice/middleware)示例。 ## 拦截器 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-custom/04-server-strategy.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/02-custom/04-server-strategy.md similarity index 98% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-custom/04-server-strategy.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/02-custom/04-server-strategy.md index 01beab4f2..c0ecf20e6 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-custom/04-server-strategy.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/02-custom/04-server-strategy.md @@ -1,6 +1,5 @@ --- title: 服务端策略 -sidebar_position: 40 --- 服务端策略也被称为`Server hook`,是一个 method 实例的装饰函数。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-custom/05-stateshook.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/02-custom/05-stateshook.md similarity index 99% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-custom/05-stateshook.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/02-custom/05-stateshook.md index 44cf5bdd7..4bb19d430 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-custom/05-stateshook.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/02-custom/05-stateshook.md @@ -1,6 +1,5 @@ --- title: States Hook -sidebar_position: 50 --- 还记得如何创建一个 Alova 实例吗? diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-custom/README.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/02-custom/README.md similarity index 88% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-custom/README.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/02-custom/README.md index b66342b5c..31a6642d7 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-custom/README.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/02-custom/README.md @@ -12,11 +12,11 @@ alova 具有很高的扩展性,它除了提供核心的缓存机制、请求 - [localStorage 存储适配器](https://github.com/alovajs/alova/blob/main/packages/alova/src/defaults/cacheAdapter.ts) - [vue states hook](https://github.com/alovajs/alova/blob/main/packages/client/src/statesHook/vue.ts) -你也可以将多个类型的适配器组成一个集合,例如[Uniapp 适配器](/tutorial/request-adapter/alova-adapter-uniapp)。 +你也可以将多个类型的适配器组成一个集合,例如[Uniapp 适配器](/next/resource/request-adapter/alova-adapter-uniapp)。 ## 自定义客户端策略 -alova 提供了 10+个自定义的客户端策略模块,但有时候可能你需要编写自己的策略模块,通常情况下,一个自定义的请求策略是基于`useRequest`、`useWatcher` 和 `useFetcher` 三个核心 useHook 的组合,以及为它们编写[中间件](/tutorial/advanced/middleware)、缓存的操纵函数来控制它们的请求方式,从而实现各种效果的请求策略 +alova 提供了 10+个自定义的客户端策略模块,但有时候可能你需要编写自己的策略模块,通常情况下,一个自定义的请求策略是基于`useRequest`、`useWatcher` 和 `useFetcher` 三个核心 useHook 的组合,以及为它们编写[中间件](/next/tutorial/client/in-depth/middleware)、缓存的操纵函数来控制它们的请求方式,从而实现各种效果的请求策略 以下的策略模块具有很好的代表性,强烈建议你参考源码寻找灵感。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-custom/_category_.json b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/02-custom/_category_.json similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/08-custom/_category_.json rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/02-custom/_category_.json diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/_category_.json b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/_category_.json deleted file mode 100644 index 3462df72e..000000000 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/_category_.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "label": "Advanced" -} diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/01-manage-apis.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-project/01-best-practice/01-manage-apis.md similarity index 91% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/01-manage-apis.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-project/01-best-practice/01-manage-apis.md index 28562ab19..9ce2c99f1 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/01-manage-apis.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-project/01-best-practice/01-manage-apis.md @@ -1,154 +1,153 @@ ---- -title: 管理APIs -sidebar_position: 10 ---- - -在一个项目中,我们可能需要使用到成百上千个请求 api,因此管理这些请求 api 变得尤为重要。 - -你可能会像 [快速开始](/tutorial/getting-started/quick-start) 中的代码片段那样编写请求发送的代码,所有请求代码写在一个文件中。 - -```javascript -const { loading, data, error } = useRequest( - alovaInstance.Get('https://api.alovajs.org/profile', { - params: { - id: 1 - } - }) -); -``` - -这只是便于初学者理解,但在实际项目中,我们并不推荐这样做,因为 method 实例的用途不仅用于发送请求,它还可以用于操作缓存和状态,上面的用法会让这些请求 api 变得难以管理,如果你认为不对的话,你可能忘记一点: - -> 响应数据缓存的 key 是由 method 实例的请求方法(method)、请求地址(url)、请求头参数(headers)、url 参数(params)、请求体参数(requestBody)组合作为唯一标识,任意一个信息或位置不同都将被当做不同的 key。 - -因此,在实际项目中应该把 method 实例进行管理,也可以统一管理 alova 实例。 - -## api 文件结构 - -首先,你的项目需要一个统一存放 method 实例和 alova 实例的文件夹,例如叫做`api`,以下为一个常见的 api 管理结构,你也可以使用适合项目的任何结构。 - -``` -|-api -| |-index.js -> 包含所有的alova实例 -| |-methods -| | |-user.js -| | |-article.js -| | |-order.js -| | |-... -|-... -``` - -总之,你的项目应该使用适合的文件夹结构将它们管理起来。 - -> 接下来以 vue 为例展示示例代码 - -## 管理 alova 实例 - -你的项目可能需要和不同的服务器通信,也可能需要在特定的请求中使用特殊的请求方案,或者使用不同的响应拦截器等,这些都需要在项目中创建并维护多个 alova 实例,建议可以使用一个单独的文件来管理它们,例如在上面的 api 管理结构中,将使用`api/index.js`来管理。 - -```javascript title=api/index.js -import { createAlova } from 'alova'; -import VueHook from 'alova/vue'; -import GlobalFetch from 'alova/GlobalFetch'; -import { axiosRequestAdapter } from '@alova/adapter-axios'; - -// user alova instance -export const userAlova = createAlova({ - baseURL: 'https://api-user.alovajs.org', - statesHook: VueHook, - requestAdapter: GlobalFetch(), - async responded(method) { - method.config.headers.token = 'user token'; - } -}); - -// order alova instance -export const orderAlova = createAlova({ - baseURL: 'https://api-order.alovajs.org', - statesHook: VueHook, - requestAdapter: GlobalFetch(), - async responded(method) { - method.config.headers.token = 'order token'; - } -}); - -// upload alova instance -export const uploadAlova = createAlova({ - baseURL: 'https://api-order.alovajs.org', - statesHook: VueHook, - requestAdapter: axiosRequestAdapter() -}); -``` - -## 管理 method 实例 - -我们可以使用不同的 js 文件将 method 实例分类管理,例如上面的 api 管理结构中,将使用`api/methods/user.js`来管理用户信息相关的 method 实例,用`api/methods/order.js`管理订单相关的 method 实例。 - -此外,在上文中提到过一点,method 实例除了用于发送请求外,还可以用于操作缓存和状态,为了确保请求参数的个数和顺序,我们可以使用一个函数来对应一个请求 api,通过传入请求参数的形式来返回对应的 method 实例,只要传入参数是相同的,method 实例的请求信息和参数顺序也是相同的,这样就可以确保用于操作缓存和状态的 method 实例不出错。 - -```javascript title=api/methods/user.js -import { userAlova } from '..'; - -// 获取用户信息 -export const getUserInfo = id => userAlova.Get('/user/' + id); - -// 编辑用户信息 -export const editUserInfo = (name, age, mobile) => - userAlova.Post('/user', { - name, - age, - mobile - }); - -// 移除用户 -export const removeUser = id => userAlova.Delete('/user/' + id); - -// ... -``` - -在**user 组件**中可以直接导入 method 函数进行使用,并且可以在调用`invalidateCache`再次使用 method 函数来失效对应的缓存。 - -```html title=views/user.vue - - -``` +--- +title: 管理APIs +--- + +在一个项目中,我们可能需要使用到成百上千个请求 api,因此管理这些请求 api 变得尤为重要。 + +你可能会像 [快速开始](/next/tutorial/getting-started/quick-start) 中的代码片段那样编写请求发送的代码,所有请求代码写在一个文件中。 + +```javascript +const { loading, data, error } = useRequest( + alovaInstance.Get('https://api.alovajs.org/profile', { + params: { + id: 1 + } + }) +); +``` + +这只是便于初学者理解,但在实际项目中,我们并不推荐这样做,因为 method 实例的用途不仅用于发送请求,它还可以用于操作缓存和状态,上面的用法会让这些请求 api 变得难以管理,如果你认为不对的话,你可能忘记一点: + +> 响应数据缓存的 key 是由 method 实例的请求方法(method)、请求地址(url)、请求头参数(headers)、url 参数(params)、请求体参数(requestBody)组合作为唯一标识,任意一个信息或位置不同都将被当做不同的 key。 + +因此,在实际项目中应该把 method 实例进行管理,也可以统一管理 alova 实例。 + +## api 文件结构 + +首先,你的项目需要一个统一存放 method 实例和 alova 实例的文件夹,例如叫做`api`,以下为一个常见的 api 管理结构,你也可以使用适合项目的任何结构。 + +``` +|-api +| |-index.js -> 包含所有的alova实例 +| |-methods +| | |-user.js +| | |-article.js +| | |-order.js +| | |-... +|-... +``` + +总之,你的项目应该使用适合的文件夹结构将它们管理起来。 + +> 接下来以 vue 为例展示示例代码 + +## 管理 alova 实例 + +你的项目可能需要和不同的服务器通信,也可能需要在特定的请求中使用特殊的请求方案,或者使用不同的响应拦截器等,这些都需要在项目中创建并维护多个 alova 实例,建议可以使用一个单独的文件来管理它们,例如在上面的 api 管理结构中,将使用`api/index.js`来管理。 + +```javascript title=api/index.js +import { createAlova } from 'alova'; +import VueHook from 'alova/vue'; +import adapterFetch from 'alova/fetch'; +import { axiosRequestAdapter } from '@alova/adapter-axios'; + +// user alova instance +export const userAlova = createAlova({ + baseURL: 'https://api-user.alovajs.org', + statesHook: VueHook, + requestAdapter: adapterFetch(), + async responded(method) { + method.config.headers.token = 'user token'; + } +}); + +// order alova instance +export const orderAlova = createAlova({ + baseURL: 'https://api-order.alovajs.org', + statesHook: VueHook, + requestAdapter: adapterFetch(), + async responded(method) { + method.config.headers.token = 'order token'; + } +}); + +// upload alova instance +export const uploadAlova = createAlova({ + baseURL: 'https://api-order.alovajs.org', + statesHook: VueHook, + requestAdapter: axiosRequestAdapter() +}); +``` + +## 管理 method 实例 + +我们可以使用不同的 js 文件将 method 实例分类管理,例如上面的 api 管理结构中,将使用`api/methods/user.js`来管理用户信息相关的 method 实例,用`api/methods/order.js`管理订单相关的 method 实例。 + +此外,在上文中提到过一点,method 实例除了用于发送请求外,还可以用于操作缓存和状态,为了确保请求参数的个数和顺序,我们可以使用一个函数来对应一个请求 api,通过传入请求参数的形式来返回对应的 method 实例,只要传入参数是相同的,method 实例的请求信息和参数顺序也是相同的,这样就可以确保用于操作缓存和状态的 method 实例不出错。 + +```javascript title=api/methods/user.js +import { userAlova } from '..'; + +// 获取用户信息 +export const getUserInfo = id => userAlova.Get('/user/' + id); + +// 编辑用户信息 +export const editUserInfo = (name, age, mobile) => + userAlova.Post('/user', { + name, + age, + mobile + }); + +// 移除用户 +export const removeUser = id => userAlova.Delete('/user/' + id); + +// ... +``` + +在**user 组件**中可以直接导入 method 函数进行使用,并且可以在调用`invalidateCache`再次使用 method 函数来失效对应的缓存。 + +```html title=views/user.vue + + +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/02-skills.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-project/01-best-practice/02-skills.md similarity index 92% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/02-skills.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-project/01-best-practice/02-skills.md index c1baa555f..78377ee06 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/02-skills.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-project/01-best-practice/02-skills.md @@ -1,6 +1,5 @@ --- title: 使用技巧 -sidebar_position: 20 --- import Tabs from '@theme/Tabs'; @@ -226,9 +225,9 @@ const handleInvalidateCache = id => { 如果你的项目,在开发环境下需要使用模拟数据模拟部分或全部接口,在生产切换回真实的网络请求,你可以通过环境变量来控制。 ```javascript -const globalFetch = GlobalFetch(); +const fetchAdapter = adapterFetch(); const mockAdapter = createAlovaMockAdapter([mockGroup1 /** ... */], { - httpAdapter: globalFetch, + httpAdapter: fetchAdapter, delay: 1000 }); @@ -241,7 +240,7 @@ export const alovaInst = createAlova({ }); ``` -并且推荐团队内不同的开发者可以根据每次迭代的版本号分别创建不同的模拟接口数据,以便于在团队中管理这些模拟数据,具体可参照 [模拟数据](/tutorial/request-adapter/alova-mock) 章节。 +并且推荐团队内不同的开发者可以根据每次迭代的版本号分别创建不同的模拟接口数据,以便于在团队中管理这些模拟数据,具体可参照 [模拟数据](/next/resource/request-adapter/alova-mock) 章节。 ## 使用 useRequest 并行请求 @@ -259,8 +258,16 @@ const { data: todoCounter } = useRequest(todoCountGetter); 手动创建 promise 对象,并使用`Promise.all`完成效果。 ```javascript -const { data: todoList, onSuccess: onListSuccess, onError: onListError } = useRequest(todoListGetter); -const { data: todoCounter, onSuccess: onCountSuccess, onError: onCountError } = useRequest(todoCountGetter); +const { + data: todoList, + onSuccess: onListSuccess, + onError: onListError +} = useRequest(todoListGetter); +const { + data: todoCounter, + onSuccess: onCountSuccess, + onError: onCountError +} = useRequest(todoCountGetter); // 手动创建promise对象 const listPromise = new Promise((resolve, reject) => { @@ -302,7 +309,10 @@ const parallelRequest = async () => { ```javascript // const { data: todoList, onSuccess } = useRequest(todoListGetter); -const { data: todoDetail, send: sendTodoDetail } = useRequest(todoId => todoDetailGetter(todoId), { immediate: false }); +const { data: todoDetail, send: sendTodoDetail } = useRequest( + todoId => todoDetailGetter(todoId), + { immediate: false } +); // 先获取列表,再获取第一个todo的详情 onSuccess(event => { @@ -317,7 +327,9 @@ onSuccess(event => { ```javascript // 先让它们不自动发送请求 const { send: sendList } = useRequest(todoListGetter, { immediate: false }); -const { send: sendTodoDetail } = useRequest(todoId => todoDetailGetter(todoId), { immediate: false }); +const { send: sendTodoDetail } = useRequest(todoId => todoDetailGetter(todoId), { + immediate: false +}); // 利用send函数返回的promise对象 const serialRequest = async () => { @@ -327,4 +339,4 @@ const serialRequest = async () => { }; ``` -> 串行请求建议直接使用[useSerialRequest](/tutorial/strategy/useSerialRequest)和[useSerialWatcher](/tutorial/strategy/useSerialWatcher)。 +> 串行请求建议直接使用[useSerialRequest](/next/tutorial/client/strategy/use-serial-request)和[useSerialWatcher](/next/tutorial/client/strategy/use-serial-watcher)。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/03-manage-cache-by-indexeddb.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-project/01-best-practice/03-manage-cache-by-indexeddb.md similarity index 89% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/03-manage-cache-by-indexeddb.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-project/01-best-practice/03-manage-cache-by-indexeddb.md index 521109e18..559436a36 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/03-manage-cache-by-indexeddb.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-project/01-best-practice/03-manage-cache-by-indexeddb.md @@ -1,123 +1,122 @@ ---- -title: 使用IndexedDB管理缓存 -sidebar_position: 30 ---- - -如果你正在开发需要大量使用本地缓存的应用,如图形编辑类应用、文件管理类应用等,低容量的 localStorage 已经无法满足开发需求,此时你可以使用 IndexedDB 配合 alova 进行大容量的本地缓存管理。 - -这一功能主要得益于 alova 的 [受控缓存](/tutorial/cache/controlled-cache) 功能,它可以实现自定义的缓存管理,我们来看看实践步骤。 - -这里有一个[使用 IndexedDB 管理缓存的示例](/tutorial/example/controlled-cache-by-indexeddb) - -我们以自定义管理大图片数据为例。 - -## 创建 IndexedDB 实例 - -首先创建一个 IndexedDB 实例用于操作本地缓存,并导出缓存操作的函数。 - -```javascript title=db.js -const dbVersion = 1; -let dbInstance; -const request = window.indexedDB.open('MyTestDatabase', dbVersion); -request.onupgradeneeded = ({ target }) => { - dbInstance = target.result; - const imgStore = dbInstance.createObjectStore('images', { - autoIncrement: true - }); - imgStore.createIndex('fileName', 'fileName', { - unique: true - }); -}; -request.onerror = () => { - throw new Error('Database open fail'); -}; -request.onsuccess = ({ target }) => { - dbInstance = target.result; -}; - -// 新增数据到IndexedDB -export const addImage2Cache = async (fileName, data) => { - const tx = dbInstance.transaction(['images'], 'readwrite'); - const request = tx.objectStore('images').add({ - fileName, - data - }); - return new Promise((resolve, reject) => { - request.onerror = () => { - reject('data add fail'); - }; - request.onsuccess = ({ result }) => { - resolve(result); - }; - }); -}; - -// 根据fileName获取文件数据 -export const getImageFromCache = async fileName => { - const tx = dbInstance.transaction(['images']); - const request = tx.objectStore('images').index('fileName').get(fileName); - return new Promise((resolve, reject) => { - request.onerror = () => { - reject('data add fail'); - }; - request.onsuccess = ({ target }) => { - resolve(target.result); - }; - }); -}; -``` - -## 保存数据 - -在保存数据时,我们可以在 method 的`transformData`中保存缓存,因为`transformData`只会在网络请求响应时被触发,而命中缓存时不会触发的特性。在示例代码中,将图片 blob 实例转换为 base64 数据,缓存并返回这个 base64 数据。 - -```javascript api.js -import { addImage2Cache } from './db'; - -export const image = fileName => - alovaInst.Get(`/image/${fileName}`, { - // highlight-start - async transformData(imgBlob) { - // 将blob异步转换为base64 - const reader = new FileReader(); - reader.readAsDataURL(imgBlob); - const base64Img = await new Promise(resolve => { - reader.onload = ({ target }) => { - resolve(target.result); - }; - }); - - // 缓存image数据到IndexedDB中 - await addImage2Cache(fileName, base64Img); - return base64Img; - } - // highlight-end - }); -``` - -## 获取数据 - -将这个 method 实例的`localCache`指定为一个异步函数,让缓存转变为受控状态,在这个函数中匹配 IndexedDB 中的缓存,如果匹配则返回它,否则返回`undefined`继续发起请求获取数据。 - -```javascript title=api.js -import { getImageFromCache } from './db'; - -export const image = fileName => - alovaInst.Get(`/image/${fileName}`, { - async transformData(imgBlob) { - // ... - }, - - // highlight-start - async localCache() { - // 获取缓存 - const cache = await getImageFromCache(fileName); - return cache && cache.data; - } - // highlight-end - }); -``` - -这样就基本完成了一个基本的自定义缓存管理,你也可以保存缓存的过期时间,并在`localCache`中匹配到缓存时再判断是否已过期,从而实现缓存过期功能。 - -IndexedDB 只是其中一个异步管理缓存的案例,你也可以连接你的缓存服务器来管理它们。 +--- +title: 使用IndexedDB管理缓存 +--- + +如果你正在开发需要大量使用本地缓存的应用,如图形编辑类应用、文件管理类应用等,低容量的 localStorage 已经无法满足开发需求,此时你可以使用 IndexedDB 配合 alova 进行大容量的本地缓存管理。 + +这一功能主要得益于 alova 的 [受控缓存](/next/tutorial/cache/controlled-cache) 功能,它可以实现自定义的缓存管理,我们来看看实践步骤。 + + + +我们以自定义管理大图片数据为例。 + +## 创建 IndexedDB 实例 + +首先创建一个 IndexedDB 实例用于操作本地缓存,并导出缓存操作的函数。 + +```javascript title=db.js +const dbVersion = 1; +let dbInstance; +const request = window.indexedDB.open('MyTestDatabase', dbVersion); +request.onupgradeneeded = ({ target }) => { + dbInstance = target.result; + const imgStore = dbInstance.createObjectStore('images', { + autoIncrement: true + }); + imgStore.createIndex('fileName', 'fileName', { + unique: true + }); +}; +request.onerror = () => { + throw new Error('Database open fail'); +}; +request.onsuccess = ({ target }) => { + dbInstance = target.result; +}; + +// 新增数据到IndexedDB +export const addImage2Cache = async (fileName, data) => { + const tx = dbInstance.transaction(['images'], 'readwrite'); + const request = tx.objectStore('images').add({ + fileName, + data + }); + return new Promise((resolve, reject) => { + request.onerror = () => { + reject('data add fail'); + }; + request.onsuccess = ({ result }) => { + resolve(result); + }; + }); +}; + +// 根据fileName获取文件数据 +export const getImageFromCache = async fileName => { + const tx = dbInstance.transaction(['images']); + const request = tx.objectStore('images').index('fileName').get(fileName); + return new Promise((resolve, reject) => { + request.onerror = () => { + reject('data add fail'); + }; + request.onsuccess = ({ target }) => { + resolve(target.result); + }; + }); +}; +``` + +## 保存数据 + +在保存数据时,我们可以在 method 的`transformData`中保存缓存,因为`transformData`只会在网络请求响应时被触发,而命中缓存时不会触发的特性。在示例代码中,将图片 blob 实例转换为 base64 数据,缓存并返回这个 base64 数据。 + +```javascript api.js +import { addImage2Cache } from './db'; + +export const image = fileName => + alovaInst.Get(`/image/${fileName}`, { + // highlight-start + async transformData(imgBlob) { + // 将blob异步转换为base64 + const reader = new FileReader(); + reader.readAsDataURL(imgBlob); + const base64Img = await new Promise(resolve => { + reader.onload = ({ target }) => { + resolve(target.result); + }; + }); + + // 缓存image数据到IndexedDB中 + await addImage2Cache(fileName, base64Img); + return base64Img; + } + // highlight-end + }); +``` + +## 获取数据 + +将这个 method 实例的`localCache`指定为一个异步函数,让缓存转变为受控状态,在这个函数中匹配 IndexedDB 中的缓存,如果匹配则返回它,否则返回`undefined`继续发起请求获取数据。 + +```javascript title=api.js +import { getImageFromCache } from './db'; + +export const image = fileName => + alovaInst.Get(`/image/${fileName}`, { + async transformData(imgBlob) { + // ... + }, + + // highlight-start + async localCache() { + // 获取缓存 + const cache = await getImageFromCache(fileName); + return cache && cache.data; + } + // highlight-end + }); +``` + +这样就基本完成了一个基本的自定义缓存管理,你也可以保存缓存的过期时间,并在`localCache`中匹配到缓存时再判断是否已过期,从而实现缓存过期功能。 + +IndexedDB 只是其中一个异步管理缓存的案例,你也可以连接你的缓存服务器来管理它们。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/04-multiple-servers.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-project/01-best-practice/04-multiple-servers.md similarity index 92% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/04-multiple-servers.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-project/01-best-practice/04-multiple-servers.md index 89cfa40c7..235018429 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/04-multiple-servers.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-project/01-best-practice/04-multiple-servers.md @@ -1,20 +1,19 @@ ---- -title: 多服务器 -sidebar_position: 40 ---- - -如果你的项目需要请求多个服务器,可以创建多个 alova 实例来分别对应不同的服务器,为了便于区分不同的环境,也可以使用环境变量来管理多个服务器的 host。 - -```ts -import { createAlova } from '@alova/core'; - -// 创建user相关的alova实例 -const userAlova = createAlova({ - baseURL: VITE_API_USER -}); - -// 创建order相关的alova实例 -const alova2 = createAlova({ - baseURL: VITE_API_ORDER -}); -``` +--- +title: 多服务器 +--- + +如果你的项目需要请求多个服务器,可以创建多个 alova 实例来分别对应不同的服务器,为了便于区分不同的环境,也可以使用环境变量来管理多个服务器的 host。 + +```ts +import { createAlova } from '@alova/core'; + +// 创建user相关的alova实例 +const userAlova = createAlova({ + baseURL: VITE_API_USER +}); + +// 创建order相关的alova实例 +const alova2 = createAlova({ + baseURL: VITE_API_ORDER +}); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/05-middleware.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-project/01-best-practice/05-middleware.md similarity index 97% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/05-middleware.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-project/01-best-practice/05-middleware.md index aeb237f32..d2242dc8c 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/05-middleware.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-project/01-best-practice/05-middleware.md @@ -1,6 +1,5 @@ --- title: 常用中间件实践 -sidebar_position: 50 --- ## 延迟更新 loading diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/06-parallel-request.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-project/01-best-practice/06-parallel-request.md similarity index 98% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/06-parallel-request.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-project/01-best-practice/06-parallel-request.md index ff72e5183..42eb62cf6 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/06-parallel-request.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-project/01-best-practice/06-parallel-request.md @@ -1,6 +1,5 @@ --- title: 并行请求 -sidebar_position: 60 --- ## 使用 method diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/07-serial-request.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-project/01-best-practice/07-serial-request.md similarity index 98% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/07-serial-request.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-project/01-best-practice/07-serial-request.md index 461edd129..5e2e7b097 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/07-serial-request.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-project/01-best-practice/07-serial-request.md @@ -1,6 +1,5 @@ --- title: 串行请求 -sidebar_position: 70 --- ## 使用 method diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/08-l2-storage.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-project/01-best-practice/08-l2-storage.md similarity index 99% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/08-l2-storage.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-project/01-best-practice/08-l2-storage.md index b726b0dc9..396bcb8ff 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-best-practice/08-l2-storage.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-project/01-best-practice/08-l2-storage.md @@ -1,6 +1,5 @@ --- title: 服务端L2存储实践 -sidebar_position: 80 --- ## 文件存储适配器 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-project/01-best-practice/README.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-project/01-best-practice/README.md new file mode 100644 index 000000000..ed0c4347c --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-project/01-best-practice/README.md @@ -0,0 +1,9 @@ +--- +title: 最佳实践 +--- + +import DocCardList from '@theme/DocCardList'; + +在真实项目中实践并提炼出来的最佳实践,希望对你能有所帮助。 + + diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/09-migration/01-v2-to-v3.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-project/02-migration/01-v2-to-v3.md similarity index 82% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/09-migration/01-v2-to-v3.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-project/02-migration/01-v2-to-v3.md index 8c3f7d11a..ed0b1c204 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/09-migration/01-v2-to-v3.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-project/02-migration/01-v2-to-v3.md @@ -1,6 +1,5 @@ --- title: v3 升级指南 -sidebar_position: 10 --- alova@3 处于 beta 中,暂不建议在实际项目中升级。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/09-migration/02-from-axios.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-project/02-migration/02-from-axios.md similarity index 69% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/09-migration/02-from-axios.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-project/02-migration/02-from-axios.md index 0fd015016..386690809 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/09-migration/02-from-axios.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-project/02-migration/02-from-axios.md @@ -1,6 +1,5 @@ --- title: 从axios迁移 -sidebar_position: 20 --- 敬请期待... diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-project/_category_.json b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-project/_category_.json new file mode 100644 index 000000000..8d25b5dc2 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-project/_category_.json @@ -0,0 +1,3 @@ +{ + "label": "项目" +} diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x.json b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x.json new file mode 100644 index 000000000..7e9afddfa --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x.json @@ -0,0 +1,70 @@ +{ + "version.label": { + "message": "2.x", + "description": "The label for version current" + }, + "sidebar.tutorial.category.Overview": { + "message": "概览", + "description": "The label for category Overview in sidebar tutorial" + }, + "sidebar.tutorial.category.Examples": { + "message": "示例", + "description": "The label for category Examples in sidebar tutorial" + }, + "sidebar.tutorial.category.Examples.link.generated-index.description": { + "message": "这里有丰富的示例,来展示alova在不同请求场景下的表现。", + "description": "The generated-index page description for category Examples in sidebar tutorial" + }, + "sidebar.tutorial.category.Getting Started": { + "message": "开始", + "description": "The label for category Get Started in sidebar tutorial" + }, + "sidebar.tutorial.category.Response Cache": { + "message": "响应缓存", + "description": "The label for category Learn in sidebar tutorial" + }, + "sidebar.tutorial.category.Combine Framework": { + "message": "结合框架", + "description": "The label for category Combine Framework in sidebar tutorial" + }, + "sidebar.tutorial.category.Strategy": { + "message": "请求策略", + "description": "The label for category Strategy in sidebar tutorial" + }, + "sidebar.tutorial.category.Sensorless data interaction": { + "message": "无感数据交互", + "description": "The label for category Sensorless data interaction in sidebar tutorial" + }, + "sidebar.tutorial.category.Basics completed": { + "message": "🎉你已完成基础", + "description": "The label for category Basics completed in sidebar tutorial" + }, + "sidebar.tutorial.category.Advanced": { + "message": "进阶", + "description": "The label for category Advanced in sidebar tutorial" + }, + "sidebar.tutorial.category.Custom": { + "message": "自定义", + "description": "The label for category custom in sidebar tutorial" + }, + "sidebar.tutorial.category.Request adapter": { + "message": "请求适配器", + "description": "The label for category Request adapter in sidebar tutorial" + }, + "sidebar.tutorial.category.extend hooks": { + "message": "扩展hooks", + "description": "The label for category extend hooks in sidebar tutorial" + }, + "sidebar.tutorial.category.Best practice": { + "message": "最佳实践", + "description": "The label for category best practice in sidebar tutorial" + }, + "sidebar.tutorial.category.Framework": { + "message": "UI框架", + "description": "The label for category Framework in sidebar tutorial" + }, + "sidebar.tutorial.category.Others": { + "message": "其他", + "description": "The label for category Others in sidebar tutorial" + } +} diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/about/01-RSM.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/about/01-RSM.md new file mode 100644 index 000000000..ea86a506c --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/about/01-RSM.md @@ -0,0 +1,60 @@ +--- +title: 请求场景模型(RSM) +--- + +## 什么是请求场景模型 + +请求场景模型是以客户端视角的,描述客户端从触发请求意图到接收请求结果的抽象模型,分别由请求时机、请求行为、请求事件以及响应管理四个阶段组成。例如在进行一次请求时经常需要思考以下问题, + +1. 什么时候发出请求; +2. 是否要展示请求状态; +3. 是否需要对请求进行失败重试; +4. 要如何加工响应数据; +5. 是否需要对请求参数加密; +6. 是否要对高频使用的响应数据做缓存; +7. 如何进行跨页面操作数据; +8. 弱网或断网环境下需要如何处理请求; +9. ... + +`fetch`或`axios`往往更专注于如何与服务端交互,但对于上面的问题我们总是需要自己处理,这些有利于应用性能和稳定性的功能,总会让程序员们编写出低维护性的代码。请求场景模型就是从准备请求到响应数据加工完毕的所有环节进行抽象,从而覆盖以前端为视角的,整个 CS 交互生命周期的模型。`alova`就是一个以请求场景模型的库,它是对`axios`等请求库的一种补充,而非替代品。 + +> CS 交互:泛指所有客户端类型和服务端的数据交互 + +## 请求场景模型 + +![RSM](/img/rsm-cn.png) + +## 请求时机 + +描述在什么时候需要发出请求,在`alova`中以`useHook`实现。 + +- 初始化展示数据,如刚进入某个界面或子界面; +- 人机交互触发 CS 交互,需要变更数据重新发出请求,如翻页、筛选、排序、模糊搜索等; +- 以防抖方式发送请求,避免视图数据闪动,以及降低服务端压力 +- 预加载数据,如分页内预先加载下一页内容、预测用户点击某个按钮后预先拉取数据; +- 操作服务端数据,需发出增删改查请求,如提交数据、删除数据等; +- 同步服务端状态,如数据变化较快的场景下轮询请求、操作了某个数据后重新拉取数据; + +## 请求行为 + +描述以怎样的方式处理请求,在`alova`中以 Method 抽象实现。 + +- 占位请求,请求时展示 loading、骨架图、或者是上次使用的真实数据; +- 缓存高频响应,多次执行请求会使用保鲜数据; +- 多请求串行与并行; +- 重要接口的重试机制,降低网络不稳定造成的请求失败概率; +- 静默提交,当只关心提交数据时,提交请求后直接响应成功事件,后台保证请求成功; +- 离线提交,离线时将提交数据暂存到本地,网络连接后再提交; + +## 请求事件 + +表示携带请求参数发送请求,获得响应,`alova`可以与`axios`、`fetch`、`XMLHttpRequest`等任意请求库或原生方案共同协作。 + +## 响应管理 + +`alova`将响应数据状态化并统一管理,以请求层面的方式刷新视图数据、操作缓存,避免了在组件层面的操作,更加优雅和统一。 + +- 移除缓存响应数据,再次发起请求时将从服务端拉取; +- 更新缓存响应数据,可更新任意位置响应数据,非常有利于跨页面更新数据; +- 刷新响应数据,可重新刷新任意位置的响应数据,也非常有利于跨页面更新数据; +- 自定义设置缓存,在请求批量数据时,可手动对批量数据一一设置缓存,从而满足后续单条数据的缓存命中; diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/about/02-comparison.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/about/02-comparison.md new file mode 100644 index 000000000..ad1e92a3f --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/about/02-comparison.md @@ -0,0 +1,92 @@ +--- +title: 与其他库比较 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## 与 axios 对比 + +axios 提供了基于 promise 的非常简单易用的 HTTP 请求功能,只需要简单的一行代码即可发送和接收请求,并且可以在浏览器和 nodejs 环境下运行,是一个非常优秀的请求 js 库。 + +但是 axios 聚焦于请求发送和接收响应,这意味着如果你需要自行编写更多代码来主动优化请求功能,而 alova 像是 axios 的武器装备,将 axios 与 alova 组合使用可以获得更强大的请求能力,以下是 alova 为 axios 附加的请求管理能力。 + +### alova 为 axios 提供自动化请求状态管理 + +仅使用 axios 时,通常需要你自行维护请求相关状态,使用 alova 的 use hook 后可以获得自动化的请求状态管理能力。 + + + + +```javascript +// vue3代码示例 +const loading = ref(false); +const data = ref({}); +const error = ref(null); +const request = async () => { + try { + loading.value = true; + data.value = await axios.get('/xxx'); + } catch (e) { + error.value = e; + } + loading.value = false; +}; +mounted(request); +``` + + + + +```javascript +// 将axios作为alova的请求适配器 +const { loading, data, error } = useRequest(alova.Get('/xxx')); +``` + + + + +### alova 提供开箱即用的高性能请求策略 + +alova 为你提供了[多个高性能的请求策略模块](/tutorial/strategy),你可以根据不同请求场景使用不同的模块,这是 axios 不具备的。 + +### alova 为 axios 提供响应数据缓存 + +alova 分别提供了 3 种缓存模式来满足不同的缓存场景,分别为内存模式、缓存占位模式、恢复模式。它们是组件无关的,只要请求地址和参数相同都可以命中缓存,除非你关闭了它。响应数据缓存可以极大地提高请求流畅性,降低服务端压力。 + +### alova 为 axios 提供请求共享功能 + +请求共享在同时发送多个相同请求时,将会复用同一个请求,它也可以提升应用流畅性和降低服务端压力。 + +### alova 为 axios 提供数据预拉取 + +提前请求将要使用的数据,也可以极大提升应用流畅性。 + +### alova 可管理请求状态 + +你可以使用 alova 跨任意的组件层级来访问其他组件内的状态化数据,这可以让你减少跨组件通信的一些麻烦。 + +## 与 react-query、swr 对比 + +react-query 是一个强大的异步状态管理,swr 是一个用于数据请求的 React Hooks 库,它们的共同特性也是使用 use hook 来发送和管理请求,和数据缓存功能,对于它们,alova 有以下不同之处。 + +### alova 的目标不同 + +实际上,alova 的 use hook 也是参考了 react-query 和 swr 的设计,但是 alova 选择了请求策略库的方向,你可以在不同的请求场景下使用不同的请求策略模块,让你在编写更少量代码同时,也能实现更高效地 Client-Server 数据交互。 + +### Method 代理设计 + +react-query 和 swr 都是在 use hook 中直接使用`axios`或`fetch api`发送请求,而 alova 使用了 `Method` 代理的设计模式,这样设计具有以下 3 个好处: + +1. 统一的使用体验,不会因平台或 UI 框架不同而存在不同的使用方式。 +2. `axios`和`fetch api`等请求库以请求适配器的方式,与每个 api 解耦,这让 alova 提供了统一的开发体验和完美的代码迁移。 +3. 每个 method 实例都代表不同的 api,你可以将同一个 api 的请求参数与请求行为参数聚合到同一个 method 实例中,而不会分散开,更适合管理大量的 api。 +4. alova 通过对 method 实例上的请求参数序列化,实现了自动化管理响应数据缓存,你不需要指定缓存 key,而 react-query 和 swr 都需要自定义设置`queryKey`来管理缓存。 + +### 高灵活性 + +alova 通过各种适配器、中间件实现了很高的灵活性,不仅可以运行在任何 js 环境,还可以支持用户自定义不同场景下的请求模块。 + +### 轻量化 + +alova 很轻量,体积只有 react-query 和 axios 的 30%+。与 swr 体积相似,但提供更丰富的功能。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/about/03-Q&A.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/about/03-Q&A.md similarity index 83% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/about/03-Q&A.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/about/03-Q&A.md index 8c70bd750..c4bea7e39 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/about/03-Q&A.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/about/03-Q&A.md @@ -1,6 +1,5 @@ --- title: 提问&回答 -sidebar_position: 30 --- import Tabs from '@theme/Tabs'; @@ -26,7 +25,7 @@ alova 是一个请求策略库,它的创建初衷是对不同请求场景提 对一个 js 库来说解耦意味着更多场景下的使用,例如 axios 可以在 nodejs 中使用,但同时意味着开发者需要写更多的模板代码,比如使用 useHooks 封装 axios 等。而 alova 摒弃了解耦带来的更多使用场景,将使用范围定位在与 UI 框架配合使用,以最精简的方式使用 alova,这是为了开发者的收益方面而考量的,在一个 UI 框架盛行的时候,深度绑定可以为开发者提供直接使用的功能,提升开发者的使用体验,而不需要太多的模板代码。 -## 如何通过 cnd 使用 alova? +## 如何通过 cdn 使用 alova? @@ -116,49 +115,6 @@ svelte 依赖于编译工具,不能通过 CDN 直接使用,详情见 [svelte ::: - - - -```html - - - - - - - - - -
-
Loading...
-
{{ todo.error.message }}
- responseData: {{ todo.data }} -
- - - -``` -
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/01-alova.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/01-alova.md index 8e8a04786..a87961c92 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/01-alova.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/01-alova.md @@ -1,6 +1,5 @@ --- title: alova实例 -sidebar_position: 10 --- ## createAlova() @@ -146,7 +145,11 @@ const getUsers = alovaInstance.Get('/users', { ```ts interface Alova { - Post(url: string, data?: object | FormData | string | null, config?: AlovaMethodCreateConfig): Method; + Post( + url: string, + data?: object | FormData | string | null, + config?: AlovaMethodCreateConfig + ): Method; } ``` @@ -184,7 +187,11 @@ const postUsers = alovaInstance.Post( ```ts interface Alova { - Delete(url: string, data?: object | FormData | string | null, config?: AlovaMethodCreateConfig): Method; + Delete( + url: string, + data?: object | FormData | string | null, + config?: AlovaMethodCreateConfig + ): Method; } ``` @@ -220,7 +227,11 @@ const deleteUsers = alovaInstance.Delete( ```ts interface Alova { - Put(url: string, data?: object | FormData | string | null, config?: AlovaMethodCreateConfig): Method; + Put( + url: string, + data?: object | FormData | string | null, + config?: AlovaMethodCreateConfig + ): Method; } ``` @@ -278,7 +289,11 @@ method 实例 ```ts interface Alova { - Patch(url: string, data?: object | FormData | string | null, config?: AlovaMethodCreateConfig): Method; + Patch( + url: string, + data?: object | FormData | string | null, + config?: AlovaMethodCreateConfig + ): Method; } ``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/02-method.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/02-method.md index 00430e831..0ffdb6cc6 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/02-method.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/02-method.md @@ -1,6 +1,5 @@ --- title: method实例 -sidebar_position: 20 --- 一个 method 实例对应一个请求信息描述,它拥有一次请求的 url、请求头、请求参数,以及响应数据处理、缓存数据处理等请求行为参数。通过 method 实例,你可以在任意的 js 环境下感受到统一的使用体验,只需要非常少的改动就可以正常运行起来,此外,method 实例将请求参数和请求行为参数放在了一起,更便于 api 的管理,而不是分散在多个代码文件中。 @@ -110,7 +109,10 @@ type MethodFilter = filter?: MethodFilterHandler; alova?: Alova; }; -function matchSnapshotMethod(matcher: MethodFilter, matchAll?: boolean): Method[] | Method | undefined; +function matchSnapshotMethod( + matcher: MethodFilter, + matchAll?: boolean +): Method[] | Method | undefined; ``` - **参数** @@ -311,7 +313,10 @@ method.abort(); ```ts interface Method { - then(onFulfilled?: (value: Response) => any, onRejected?: (reason: any) => any): Promise; + then( + onFulfilled?: (value: Response) => any, + onRejected?: (reason: any) => any + ): Promise; } ``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/03-core-hooks.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/03-core-hooks.md index 3ba7087fa..788ae744c 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/03-core-hooks.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/03-core-hooks.md @@ -1,268 +1,267 @@ ---- -title: 核心useHooks -sidebar_position: 30 ---- - -## useRequest - -表示一次请求的发送,执行 useRequest 时默认会发送一次请求,并创建和维护状态化的请求相关数据,如`loading/data/error`等。在页面获取初始数据时是最常用的方法,同时也支持关闭它的默认的请求发送,这在提交数据等通过点击事件触发的请求场景非常有用。 - -> 前往[useRequest](/tutorial/combine-framework/use-request)查看详情。 - -### 类型 - -```ts -function useRequest( - methodHandler: Method | (...args: any[]) => Method, - config?: RequestHookConfig -): UseHookReturnType; -``` - -### 参数 - -1. `methodHandler`: 可传入 method 实例和函数两种形式,当指定为函数时的可接收`send`传入的参数,并要求返回一个 method 实例。 -2. `config`: hook 的配置参数。 - -| 名称 | 描述 | 类型 | 默认值 | 版本 | -| ------------- | ------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | ------ | ---- | -| immediate | 是否立即发起请求 | boolean | true | - | -| initialData | 初始的 data 值,在首次响应前 data 值为初始值,未设置时为`undefined` | any | - | - | -| force | 是否强制请求,可设置为函数动态返回 boolean 值 | boolean \| (...args: any[]) => boolean \| false | - | - | -| managedStates | 额外的监管状态,可通过 updateState 更新 | Record\ | - | - | -| middleware | 中间件函数,[了解 alova 中间件](/tutorial/advanced/middleware) | (context: [AlovaFrontMiddlewareContext](#alovafrontmiddlewarecontext), next: [AlovaGuardNext](#alovaguardnext)) => Promise\ | - | - | - -#### AlovaFrontMiddlewareContext - -| 名称 | 描述 | 类型 | 版本 | -| ---------------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- | -| method | 当前请求的 method 对象 | Method | - | -| cachedResponse | 命中的缓存数据 | any | - | -| config | 当前的 use hook 配置 | Record\ | - | -| sendArgs | 响应处理回调的参数,该参数由 use hooks 的 send 传入 | any[] | - | -| frontStates | use hook 前端状态集合,如 data、loading、error 等 | [FrontRequestState](#frontrequeststate) | - | -| send | 发送请求函数 | (...args: any[]) => void | Promise | -| abort | 中断函数 | () => void | - | -| decorateSuccess | 装饰成功回调函数 | (decorator: (
handler: (event: [AlovaSuccessEvent](#alovasuccessevent)) => void,
event: [AlovaSuccessEvent](#alovasuccessevent),
index: number,
length: number
) => void) => void | - | -| decorateError | 装饰失败回调函数 | (decorator: (
handler: (event: [AlovaErrorEvent](#alovaerrorevent)) => void,
event: [AlovaErrorEvent](#alovaerrorevent),
index: number,
length: number
) => void) => void | - | -| decorateComplete | 装饰完成回调函数 | (decorator: (
handler: (event: [AlovaCompleteEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovacompleteevent),
index: number,
length: number
) => void) => void | - | -| update | 更新当前 use hook 前端状态的函数,在 react 中较有用 | (newFrontStates: [FrontRequestState](#frontrequeststate)) => void; | - | -| controlLoading | 将自定义控制 loading 的状态,调用内部不再触发 loading 状态的变更,传入 control 为 false 时将取消控制 | (control?: boolean) => void | - | - -#### AlovaGuardNext - -```typescript -type AlovaGuardNext = (guardNextConfig?: { - force?: boolean | (...args: any[]) => boolean; - method?: Method; -}): Promise; -``` - -#### FrontRequestState - -以下属性值将会根据`statesHook`自动推断出对应 UI 框架的响应式数据类型,在 vue3 中为`Ref`类型,在 react 中为普通值,在 svelte 中为`Writable`类型 - -| 名称 | 描述 | 类型 | 版本 | -| ----------- | ------------ | ------------------ | ---- | -| loading | 请求加载状态 | boolean | - | -| data | 响应数据 | any | - | -| error | 请求错误信息 | Error \| undefined | - | -| downloading | 下载进度信息 | Object | - | -| uploading | 上传进度信息 | Object | - | - -#### AlovaSuccessEvent - -| 名称 | 描述 | 类型 | 版本 | -| --------- | --------------------------------------------------- | ------- | ---- | -| method | 当前请求的 method 对象 | Method | - | -| sendArgs | 响应处理回调的参数,该参数由 use hooks 的 send 传入 | any[] | - | -| data | 响应数据 | any | - | -| fromCache | 响应数据是否来自缓存 | boolean | - | - -#### AlovaErrorEvent - -| 名称 | 描述 | 类型 | 版本 | -| -------- | --------------------------------------------------- | ------ | ---- | -| method | 当前请求的 method 对象 | Method | - | -| sendArgs | 响应处理回调的参数,该参数由 use hooks 的 send 传入 | any[] | - | -| error | 响应错误实例 | Error | - | - -#### AlovaCompleteEvent - -| 名称 | 描述 | 类型 | 版本 | -| --------- | --------------------------------------------------- | -------------------- | ---- | -| method | 当前请求的 method 对象 | Method | - | -| sendArgs | 响应处理回调的参数,该参数由 use hooks 的 send 传入 | any[] | - | -| status | 响应状态,成功时为 success,失败时为 error | 'success' \| 'error' | - | -| data | 响应数据,成功时有值 | any | - | -| fromCache | 响应数据是否来自缓存,成功时有值 | boolean | - | -| error | 响应错误实例,失败时有值 | Error | - | - -### 返回值 - -`UseHookReturnType`包含响应数据和请求相关的状态、操作函数和事件绑定函数,它们会根据`statesHook`自动推断出对应 UI 框架的响应式数据类型,在 vue3 中为`Ref`类型,在 react 中为普通值,在 svelte 中为`Writable`类型。 - -#### 响应式数据 - -| 名称 | 描述 | 类型 | 版本 | -| ----------- | ------------ | ------------------ | ---- | -| loading | 请求加载状态 | boolean | - | -| data | 响应数据 | any | - | -| error | 请求错误信息 | Error \| undefined | - | -| downloading | 下载进度信息 | Object | - | -| uploading | 上传进度信息 | Object | - | - -#### 操作函数 - -| 名称 | 描述 | 函数参数 | 返回值 | 版本 | -| ------ | --------------------------------------------------- | ------------------------------------------------------- | ------- | ---- | -| send | 发送请求函数 | ...args: any[] | - | - | -| abort | 中断函数 | - | Promise | - | -| update | 更新当前 use hook 前端状态的函数,在 react 中较有用 | newFrontStates: [FrontRequestState](#frontrequeststate) | - | - -#### 事件 - -| 名称 | 描述 | 回调参数 | 版本 | -| ---------- | ---------------- | ------------------------------------------------ | ---- | -| onSuccess | 请求成功事件绑定 | event: [AlovaSuccessEvent](#alovasuccessevent) | - | -| onError | 请求错误事件绑定 | event: [AlovaErrorEvent](#alovaerrorevent) | - | -| onComplete | 请求完成事件绑定 | event: [AlovaCompleteEvent](#alovacompleteevent) | - | - -## useWatcher - -监听状态,并在状态变化后发起请求,在一些需要随数据变化而重新请求的场景下,如分页、数据筛选、模糊搜索使用。 - -> 前往[状态变化请求](/tutorial/combine-framework/use-watcher)查看详情。 - -### 类型 - -```typescript -function useWatcher( - handler: Method | (...args: any[]) => Method, - watchingStates: State[], - config?: WatcherHookConfig -): UseHookReturnType; -``` - -### 参数 - -1. `handler`: 可传入 method 实例和函数两种形式,当指定为函数时的可接收`send`传入的参数,并要求返回一个 method 实例。 -2. `config`: hook 的配置参数。 - -| 名称 | 描述 | 类型 | 默认值 | 版本 | -| ------------- | -------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | ---------- | ---- | -| immediate | 是否立即发起请求 | boolean | true | - | -| initialData | 初始的 data 值,在首次响应前 data 值为初始值,未设置时为`undefined` | any | - | - | -| force | 是否强制请求,可设置为函数动态返回 boolean 值 | boolean \| (...args: any[]) => boolean \| false | -| managedStates | 额外的监管状态,可通过 updateState 更新 | Record\ | - | -| debounce | 请求防抖时间(毫秒),传入数组时可按 watchingStates 的顺序单独设置防抖时间 | number \| number[] | - | -| middleware | 中间件函数,[了解 alova 中间件](/tutorial/advanced/middleware) | (context: [AlovaFrontMiddlewareContext](#alovafrontmiddlewarecontext), next: [AlovaGuardNext](#alovaguardnext)) => Promise\ | - | - | -| sendable | 监听的状态改变时是否发送请求 | (methodInstance: AlovaEvent) => boolean | () => true | - | -| abortLast | 是否中断上一次的未响应请求 | boolean | true | - | - -### 返回值 - -`UseHookReturnType`包含响应数据和请求相关的状态、操作函数和事件绑定函数,它们会根据 statesHook 自动推断出对应 UI 框架的响应式数据类型,在 vue3 中为 Ref 类型,在 react 中为普通值,在 svelte 中为 Writable 类型。 - -#### 响应式数据 - -| 名称 | 描述 | 类型 | 版本 | -| ----------- | ------------ | ------------------ | ---- | -| loading | 请求加载状态 | boolean | - | -| data | 响应数据 | any | - | -| error | 请求错误信息 | Error \| undefined | - | -| downloading | 下载进度信息 | Object | - | -| uploading | 上传进度信息 | Object | - | - -#### 操作函数 - -| 名称 | 描述 | 函数参数 | 返回值 | 版本 | -| ------ | --------------------------------------------------- | ------------------------------------------------------- | ------- | ---- | -| send | 发送请求函数 | ...args: any[] | Promise | - | -| abort | 中断函数 | - | - | - | -| update | 更新当前 use hook 前端状态的函数,在 react 中较有用 | newFrontStates: [FrontRequestState](#frontrequeststate) | - | - -#### 事件 - -| 名称 | 描述 | 回调参数 | 版本 | -| ---------- | ---------------- | ------------------------------------------------ | ---- | -| onSuccess | 请求成功事件绑定 | event: [AlovaSuccessEvent](#alovasuccessevent) | - | -| onError | 请求错误事件绑定 | event: [AlovaErrorEvent](#alovaerrorevent) | - | -| onComplete | 请求完成事件绑定 | event: [AlovaCompleteEvent](#alovacompleteevent) | - | - -## useFetcher - -通过`useFetcher`用来拉取数据,在预加载数据和跨模块更新状态时很有用。 - -> 前往[数据拉取](/tutorial/advanced/use-fetcher)查看详情。 - -### 类型 - -```typescript -function useFetcher(config?: FetcherHookConfig): UseFetchHookReturnType; -``` - -### 参数 - -1. `config`: hook 的配置参数。 - -| 名称 | 描述 | 类型 | 默认值 | 版本 | -| ---------- | -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ | ---- | -| force | 是否强制请求,可设置为函数动态返回 boolean 值 | boolean | (...args: any[]) => boolean \| false | - | -| middleware | 中间件函数,[了解 alova 中间件](/tutorial/advanced/middleware) | (context: [AlovaFetcherMiddlewareContext](#alovafetchermiddlewarecontext), next: [AlovaGuardNext](#alovaguardnext)) => Promise\ | - | - | - -#### AlovaFetcherMiddlewareContext - -| 名称 | 描述 | 类型 | 版本 | -| ---------------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- | -| method | 当前请求的 method 对象 | Method | - | -| cachedResponse | 命中的缓存数据 | any | - | -| config | 当前的 use hook 配置 | Record\ | - | -| fetchArgs | 响应处理回调的参数,该参数由 useFetcher 的 fetch 传入 | any[] | - | -| fetchStates | use hook 预加载状态集合,如 fetching、error 等 | [FetchRequestState](#fetchrequeststate) | - | -| fetch | 数据预加载函数 | (method: Method, ...args: any[]) => void | Promise | -| abort | 中断函数 | () => void | - | -| decorateSuccess | 装饰成功回调函数 | (decorator: (
handler: (event: [AlovaSuccessEvent](#alovasuccessevent)) => void,
event: [AlovaSuccessEvent](#alovasuccessevent),
index: number,
length: number
) => void) => void | - | -| decorateError | 装饰失败回调函数 | (decorator: (
handler: (event: [AlovaErrorEvent](#alovaerrorevent)) => void,
event: [AlovaErrorEvent](#alovaerrorevent),
index: number,
length: number
) => void) => void | - | -| decorateComplete | 装饰完成回调函数 | (decorator: (
handler: (event: [AlovaCompleteEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovacompleteevent),
index: number,
length: number
) => void) => void | - | -| update | 更新当前 use hook 预加载状态的函数,在 react 中较有用 | (newFrontStates: [FetchRequestState](#fetchrequeststate)) => void; | - | -| controlFetching | 调用后将自定义控制 fetching 的状态,内部不再触发 fetching 状态的变更,传入 control 为 false 时将取消控制 | (control?: boolean) => void | - | - -#### FetchRequestState - -以下属性值将会根据`statesHook`自动推断出对应 UI 框架的响应式数据类型,在 vue3 中为`Ref`类型,在 react 中为普通值,在 svelte 中为`Writable`类型 - -| 名称 | 描述 | 类型 | 版本 | -| ----------- | -------------- | ------------------ | ---- | -| fetching | 预加载请求状态 | boolean | - | -| error | 请求错误信息 | Error \| undefined | - | -| downloading | 下载进度信息 | Object | - | -| uploading | 上传进度信息 | Object | - | - -### 返回值 - -`UseFetchHookReturnType`包含请求相关的状态、操作函数和事件绑定函数,它们会根据 statesHook 自动推断出对应 UI 框架的响应式数据类型,在 vue3 中为 Ref 类型,在 react 中为普通值,在 svelte 中为 Writable 类型。 - -#### 响应式数据 - -| 名称 | 描述 | 类型 | 版本 | -| ----------- | -------------- | ------------------ | ---- | -| fetching | 预加载请求状态 | boolean | - | -| error | 请求错误信息 | Error \| undefined | - | -| downloading | 下载进度信息 | Object | - | -| uploading | 上传进度信息 | Object | - | - -#### 操作函数 - -| 名称 | 描述 | 函数参数 | 返回值 | 版本 | -| ------ | --------------------------------------------------- | ------------------------------------------------------- | ------- | ---- | -| fetch | 数据预加载函数 | 1. method: 预加载的 Method 实例
2. ...args: any[] | Promise | - | -| abort | 中断函数 | - | - | - | -| update | 更新当前 use hook 前端状态的函数,在 react 中较有用 | newFrontStates: [FrontRequestState](#frontrequeststate) | - | - -#### 事件 - -| 名称 | 描述 | 回调参数 | 版本 | -| ---------- | ---------------- | ---------------------------------------------------------------------------------------------------------------- | ---- | -| onSuccess | 请求成功事件绑定 | event: [AlovaSuccessEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovasuccessevent) | - | -| onError | 请求错误事件绑定 | event: [AlovaErrorEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovaerrorevent) | - | -| onComplete | 请求完成事件绑定 | event: [AlovaCompleteEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovacompleteevent) | - | +--- +title: 核心useHooks +--- + +## useRequest + +表示一次请求的发送,执行 useRequest 时默认会发送一次请求,并创建和维护状态化的请求相关数据,如`loading/data/error`等。在页面获取初始数据时是最常用的方法,同时也支持关闭它的默认的请求发送,这在提交数据等通过点击事件触发的请求场景非常有用。 + +> 前往[useRequest](/tutorial/combine-framework/use-request)查看详情。 + +### 类型 + +```ts +function useRequest( + methodHandler: Method | (...args: any[]) => Method, + config?: RequestHookConfig +): UseHookReturnType; +``` + +### 参数 + +1. `methodHandler`: 可传入 method 实例和函数两种形式,当指定为函数时的可接收`send`传入的参数,并要求返回一个 method 实例。 +2. `config`: hook 的配置参数。 + +| 名称 | 描述 | 类型 | 默认值 | 版本 | +| ------------- | ------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | ------ | ---- | +| immediate | 是否立即发起请求 | boolean | true | - | +| initialData | 初始的 data 值,在首次响应前 data 值为初始值,未设置时为`undefined` | any | - | - | +| force | 是否强制请求,可设置为函数动态返回 boolean 值 | boolean \| (...args: any[]) => boolean \| false | - | - | +| managedStates | 额外的监管状态,可通过 updateState 更新 | Record\ | - | - | +| middleware | 中间件函数,[了解 alova 中间件](/tutorial/advanced/middleware) | (context: [AlovaFrontMiddlewareContext](#alovafrontmiddlewarecontext), next: [AlovaGuardNext](#alovaguardnext)) => Promise\ | - | - | + +#### AlovaFrontMiddlewareContext + +| 名称 | 描述 | 类型 | 版本 | +| ---------------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- | +| method | 当前请求的 method 对象 | Method | - | +| cachedResponse | 命中的缓存数据 | any | - | +| config | 当前的 use hook 配置 | Record\ | - | +| sendArgs | 响应处理回调的参数,该参数由 use hooks 的 send 传入 | any[] | - | +| frontStates | use hook 前端状态集合,如 data、loading、error 等 | [FrontRequestState](#frontrequeststate) | - | +| send | 发送请求函数 | (...args: any[]) => void | Promise | +| abort | 中断函数 | () => void | - | +| decorateSuccess | 装饰成功回调函数 | (decorator: (
handler: (event: [AlovaSuccessEvent](#alovasuccessevent)) => void,
event: [AlovaSuccessEvent](#alovasuccessevent),
index: number,
length: number
) => void) => void | - | +| decorateError | 装饰失败回调函数 | (decorator: (
handler: (event: [AlovaErrorEvent](#alovaerrorevent)) => void,
event: [AlovaErrorEvent](#alovaerrorevent),
index: number,
length: number
) => void) => void | - | +| decorateComplete | 装饰完成回调函数 | (decorator: (
handler: (event: [AlovaCompleteEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovacompleteevent),
index: number,
length: number
) => void) => void | - | +| update | 更新当前 use hook 前端状态的函数,在 react 中较有用 | (newFrontStates: [FrontRequestState](#frontrequeststate)) => void; | - | +| controlLoading | 将自定义控制 loading 的状态,调用内部不再触发 loading 状态的变更,传入 control 为 false 时将取消控制 | (control?: boolean) => void | - | + +#### AlovaGuardNext + +```typescript +type AlovaGuardNext = (guardNextConfig?: { + force?: boolean | (...args: any[]) => boolean; + method?: Method; +}): Promise; +``` + +#### FrontRequestState + +以下属性值将会根据`statesHook`自动推断出对应 UI 框架的响应式数据类型,在 vue3 中为`Ref`类型,在 react 中为普通值,在 svelte 中为`Writable`类型 + +| 名称 | 描述 | 类型 | 版本 | +| ----------- | ------------ | ------------------ | ---- | +| loading | 请求加载状态 | boolean | - | +| data | 响应数据 | any | - | +| error | 请求错误信息 | Error \| undefined | - | +| downloading | 下载进度信息 | Object | - | +| uploading | 上传进度信息 | Object | - | + +#### AlovaSuccessEvent + +| 名称 | 描述 | 类型 | 版本 | +| --------- | --------------------------------------------------- | ------- | ---- | +| method | 当前请求的 method 对象 | Method | - | +| sendArgs | 响应处理回调的参数,该参数由 use hooks 的 send 传入 | any[] | - | +| data | 响应数据 | any | - | +| fromCache | 响应数据是否来自缓存 | boolean | - | + +#### AlovaErrorEvent + +| 名称 | 描述 | 类型 | 版本 | +| -------- | --------------------------------------------------- | ------ | ---- | +| method | 当前请求的 method 对象 | Method | - | +| sendArgs | 响应处理回调的参数,该参数由 use hooks 的 send 传入 | any[] | - | +| error | 响应错误实例 | Error | - | + +#### AlovaCompleteEvent + +| 名称 | 描述 | 类型 | 版本 | +| --------- | --------------------------------------------------- | -------------------- | ---- | +| method | 当前请求的 method 对象 | Method | - | +| sendArgs | 响应处理回调的参数,该参数由 use hooks 的 send 传入 | any[] | - | +| status | 响应状态,成功时为 success,失败时为 error | 'success' \| 'error' | - | +| data | 响应数据,成功时有值 | any | - | +| fromCache | 响应数据是否来自缓存,成功时有值 | boolean | - | +| error | 响应错误实例,失败时有值 | Error | - | + +### 返回值 + +`UseHookReturnType`包含响应数据和请求相关的状态、操作函数和事件绑定函数,它们会根据`statesHook`自动推断出对应 UI 框架的响应式数据类型,在 vue3 中为`Ref`类型,在 react 中为普通值,在 svelte 中为`Writable`类型。 + +#### 响应式数据 + +| 名称 | 描述 | 类型 | 版本 | +| ----------- | ------------ | ------------------ | ---- | +| loading | 请求加载状态 | boolean | - | +| data | 响应数据 | any | - | +| error | 请求错误信息 | Error \| undefined | - | +| downloading | 下载进度信息 | Object | - | +| uploading | 上传进度信息 | Object | - | + +#### 操作函数 + +| 名称 | 描述 | 函数参数 | 返回值 | 版本 | +| ------ | --------------------------------------------------- | ------------------------------------------------------- | ------- | ---- | +| send | 发送请求函数 | ...args: any[] | - | - | +| abort | 中断函数 | - | Promise | - | +| update | 更新当前 use hook 前端状态的函数,在 react 中较有用 | newFrontStates: [FrontRequestState](#frontrequeststate) | - | + +#### 事件 + +| 名称 | 描述 | 回调参数 | 版本 | +| ---------- | ---------------- | ------------------------------------------------ | ---- | +| onSuccess | 请求成功事件绑定 | event: [AlovaSuccessEvent](#alovasuccessevent) | - | +| onError | 请求错误事件绑定 | event: [AlovaErrorEvent](#alovaerrorevent) | - | +| onComplete | 请求完成事件绑定 | event: [AlovaCompleteEvent](#alovacompleteevent) | - | + +## useWatcher + +监听状态,并在状态变化后发起请求,在一些需要随数据变化而重新请求的场景下,如分页、数据筛选、模糊搜索使用。 + +> 前往[状态变化请求](/tutorial/combine-framework/use-watcher)查看详情。 + +### 类型 + +```typescript +function useWatcher( + handler: Method | (...args: any[]) => Method, + watchingStates: State[], + config?: WatcherHookConfig +): UseHookReturnType; +``` + +### 参数 + +1. `handler`: 可传入 method 实例和函数两种形式,当指定为函数时的可接收`send`传入的参数,并要求返回一个 method 实例。 +2. `config`: hook 的配置参数。 + +| 名称 | 描述 | 类型 | 默认值 | 版本 | +| ------------- | -------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | ---------- | ---- | +| immediate | 是否立即发起请求 | boolean | true | - | +| initialData | 初始的 data 值,在首次响应前 data 值为初始值,未设置时为`undefined` | any | - | - | +| force | 是否强制请求,可设置为函数动态返回 boolean 值 | boolean \| (...args: any[]) => boolean \| false | +| managedStates | 额外的监管状态,可通过 updateState 更新 | Record\ | - | +| debounce | 请求防抖时间(毫秒),传入数组时可按 watchingStates 的顺序单独设置防抖时间 | number \| number[] | - | +| middleware | 中间件函数,[了解 alova 中间件](/tutorial/advanced/middleware) | (context: [AlovaFrontMiddlewareContext](#alovafrontmiddlewarecontext), next: [AlovaGuardNext](#alovaguardnext)) => Promise\ | - | - | +| sendable | 监听的状态改变时是否发送请求 | (methodInstance: AlovaEvent) => boolean | () => true | - | +| abortLast | 是否中断上一次的未响应请求 | boolean | true | - | + +### 返回值 + +`UseHookReturnType`包含响应数据和请求相关的状态、操作函数和事件绑定函数,它们会根据 statesHook 自动推断出对应 UI 框架的响应式数据类型,在 vue3 中为 Ref 类型,在 react 中为普通值,在 svelte 中为 Writable 类型。 + +#### 响应式数据 + +| 名称 | 描述 | 类型 | 版本 | +| ----------- | ------------ | ------------------ | ---- | +| loading | 请求加载状态 | boolean | - | +| data | 响应数据 | any | - | +| error | 请求错误信息 | Error \| undefined | - | +| downloading | 下载进度信息 | Object | - | +| uploading | 上传进度信息 | Object | - | + +#### 操作函数 + +| 名称 | 描述 | 函数参数 | 返回值 | 版本 | +| ------ | --------------------------------------------------- | ------------------------------------------------------- | ------- | ---- | +| send | 发送请求函数 | ...args: any[] | Promise | - | +| abort | 中断函数 | - | - | - | +| update | 更新当前 use hook 前端状态的函数,在 react 中较有用 | newFrontStates: [FrontRequestState](#frontrequeststate) | - | + +#### 事件 + +| 名称 | 描述 | 回调参数 | 版本 | +| ---------- | ---------------- | ------------------------------------------------ | ---- | +| onSuccess | 请求成功事件绑定 | event: [AlovaSuccessEvent](#alovasuccessevent) | - | +| onError | 请求错误事件绑定 | event: [AlovaErrorEvent](#alovaerrorevent) | - | +| onComplete | 请求完成事件绑定 | event: [AlovaCompleteEvent](#alovacompleteevent) | - | + +## useFetcher + +通过`useFetcher`用来拉取数据,在预加载数据和跨模块更新状态时很有用。 + +> 前往[数据拉取](/tutorial/advanced/use-fetcher)查看详情。 + +### 类型 + +```typescript +function useFetcher(config?: FetcherHookConfig): UseFetchHookReturnType; +``` + +### 参数 + +1. `config`: hook 的配置参数。 + +| 名称 | 描述 | 类型 | 默认值 | 版本 | +| ---------- | -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ | ---- | +| force | 是否强制请求,可设置为函数动态返回 boolean 值 | boolean | (...args: any[]) => boolean \| false | - | +| middleware | 中间件函数,[了解 alova 中间件](/tutorial/advanced/middleware) | (context: [AlovaFetcherMiddlewareContext](#alovafetchermiddlewarecontext), next: [AlovaGuardNext](#alovaguardnext)) => Promise\ | - | - | + +#### AlovaFetcherMiddlewareContext + +| 名称 | 描述 | 类型 | 版本 | +| ---------------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- | +| method | 当前请求的 method 对象 | Method | - | +| cachedResponse | 命中的缓存数据 | any | - | +| config | 当前的 use hook 配置 | Record\ | - | +| fetchArgs | 响应处理回调的参数,该参数由 useFetcher 的 fetch 传入 | any[] | - | +| fetchStates | use hook 预加载状态集合,如 fetching、error 等 | [FetchRequestState](#fetchrequeststate) | - | +| fetch | 数据预加载函数 | (method: Method, ...args: any[]) => void | Promise | +| abort | 中断函数 | () => void | - | +| decorateSuccess | 装饰成功回调函数 | (decorator: (
handler: (event: [AlovaSuccessEvent](#alovasuccessevent)) => void,
event: [AlovaSuccessEvent](#alovasuccessevent),
index: number,
length: number
) => void) => void | - | +| decorateError | 装饰失败回调函数 | (decorator: (
handler: (event: [AlovaErrorEvent](#alovaerrorevent)) => void,
event: [AlovaErrorEvent](#alovaerrorevent),
index: number,
length: number
) => void) => void | - | +| decorateComplete | 装饰完成回调函数 | (decorator: (
handler: (event: [AlovaCompleteEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovacompleteevent),
index: number,
length: number
) => void) => void | - | +| update | 更新当前 use hook 预加载状态的函数,在 react 中较有用 | (newFrontStates: [FetchRequestState](#fetchrequeststate)) => void; | - | +| controlFetching | 调用后将自定义控制 fetching 的状态,内部不再触发 fetching 状态的变更,传入 control 为 false 时将取消控制 | (control?: boolean) => void | - | + +#### FetchRequestState + +以下属性值将会根据`statesHook`自动推断出对应 UI 框架的响应式数据类型,在 vue3 中为`Ref`类型,在 react 中为普通值,在 svelte 中为`Writable`类型 + +| 名称 | 描述 | 类型 | 版本 | +| ----------- | -------------- | ------------------ | ---- | +| fetching | 预加载请求状态 | boolean | - | +| error | 请求错误信息 | Error \| undefined | - | +| downloading | 下载进度信息 | Object | - | +| uploading | 上传进度信息 | Object | - | + +### 返回值 + +`UseFetchHookReturnType`包含请求相关的状态、操作函数和事件绑定函数,它们会根据 statesHook 自动推断出对应 UI 框架的响应式数据类型,在 vue3 中为 Ref 类型,在 react 中为普通值,在 svelte 中为 Writable 类型。 + +#### 响应式数据 + +| 名称 | 描述 | 类型 | 版本 | +| ----------- | -------------- | ------------------ | ---- | +| fetching | 预加载请求状态 | boolean | - | +| error | 请求错误信息 | Error \| undefined | - | +| downloading | 下载进度信息 | Object | - | +| uploading | 上传进度信息 | Object | - | + +#### 操作函数 + +| 名称 | 描述 | 函数参数 | 返回值 | 版本 | +| ------ | --------------------------------------------------- | ------------------------------------------------------- | ------- | ---- | +| fetch | 数据预加载函数 | 1. method: 预加载的 Method 实例
2. ...args: any[] | Promise | - | +| abort | 中断函数 | - | - | - | +| update | 更新当前 use hook 前端状态的函数,在 react 中较有用 | newFrontStates: [FrontRequestState](#frontrequeststate) | - | + +#### 事件 + +| 名称 | 描述 | 回调参数 | 版本 | +| ---------- | ---------------- | ---------------------------------------------------------------------------------------------------------------- | ---- | +| onSuccess | 请求成功事件绑定 | event: [AlovaSuccessEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovasuccessevent) | - | +| onError | 请求错误事件绑定 | event: [AlovaErrorEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovaerrorevent) | - | +| onComplete | 请求完成事件绑定 | event: [AlovaCompleteEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovacompleteevent) | - | diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/04-cache.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/04-cache.md index dbab8559b..2fa545428 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/04-cache.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/04-cache.md @@ -1,6 +1,5 @@ --- title: 缓存操作 -sidebar_position: 40 --- ## invalidateCache() diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/05-states.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/05-states.md index bea840a6d..ea8dda2f6 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/05-states.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/05-states.md @@ -1,6 +1,5 @@ --- title: 响应状态操作 -sidebar_position: 50 --- ## updateState diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/06-global-config.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/06-global-config.md index 45be3e559..f923a2a80 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/06-global-config.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/06-global-config.md @@ -1,36 +1,35 @@ ---- -title: 全局配置 -sidebar_position: 60 ---- - -## globalConfig() - -全局配置。 - -- **类型** - -```ts -function globalConfig(config: AlovaGlobalConfig): void; -``` - -- **参数** - -1. config: 配置 - -| 参数名 | 类型 | 说明 | -| -------------- | ------ | -------------------------------------------------------------------------------- | -| limitSnapshots | number | method 快照数量限制,设置为 0 表示关闭保存快照,关闭后 method 快照匹配器将不可用 | - -- **返回** - -无 - -- **示例** - -```ts -import { globalConfig } from 'alova'; - -globalConfig({ - limitSnapshots: 10 -}); -``` +--- +title: 全局配置 +--- + +## globalConfig() + +全局配置。 + +- **类型** + +```ts +function globalConfig(config: AlovaGlobalConfig): void; +``` + +- **参数** + +1. config: 配置 + +| 参数名 | 类型 | 说明 | +| -------------- | ------ | -------------------------------------------------------------------------------- | +| limitSnapshots | number | method 快照数量限制,设置为 0 表示关闭保存快照,关闭后 method 快照匹配器将不可用 | + +- **返回** + +无 + +- **示例** + +```ts +import { globalConfig } from 'alova'; + +globalConfig({ + limitSnapshots: 10 +}); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/contributing/01-overview.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/contributing/01-overview.md index 7c68f3b19..b246d0cb3 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/contributing/01-overview.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/contributing/01-overview.md @@ -1,176 +1,175 @@ ---- -title: 贡献指南 -sidebar_position: 10 ---- - -# alova 贡献指南 - -你好,很高兴在这遇到你,这是一份详细的 alova 贡献指南,它包含对 alova 各个方面的贡献提供了详细的指导,请继续往下看。 - -## 前言 - -在过去的一段时间里,我们在 Github issues 和 Github Disscussion 中收到了来自世界各地的开发者积极参与的信息,深感荣幸,这意味着 alova 正在被越来越多的开发者喜爱。即便如此,alova 也还属于新秀,它依然还有很长一段路需要走。 - -**我们期望将 alova 打造成每位愿意参与的人的共同项目,我们以开放包容的态度鼓励每个人成为 alova 社区的贡献者。而且,我们认为贡献 alova 不局限于代码贡献,而是参与任何有利于 alova 发展的活动都属于贡献 alova,** 现在参与贡献可以为你赢得更多的有效贡献机会,它可以让你为全世界的开发者提供你的价值,即使你是一位初级开发者,只要想法符合 [alova 的使命和设计理念](#alova-使命和设计理念),也请大方地参与进来! - -> 这里有一份[社区行为公约](./code-of-conduct),请参阅。 - -## 贡献目录 - -这里提供 13 个可贡献之处供你选择,但不局限于这些,你可以选择自己希望参与的部分后,链接到对应位置详细阅读: - -- [在项目中使用 alova](#在项目中使用-alova) -- [为 alova 点星](#为-alova-点星) -- [报告 bug](#报告-bug) -- [提出新特性想法](#提出新特性想法) -- [Pull Request](#pull-request) -- [基于 alova 编写适配器和策略库](#基于-alova-编写适配器和策略库) -- [参与社区交流/PR review](#参与社区交流pr-review) -- [发布和传播有利于 alova 的信息](#发布和传播有利于-alova-的信息) -- [分享使用经验](#分享使用经验) -- [项目合作](#项目合作) -- [项目捐赠](#项目捐赠) -- [更正或编写文档](#更正或编写文档) -- [翻译文档](#翻译文档) - -## alova 使命和设计理念 - -### alova 使命 - -alova 的使命为它指出了明确的发展方向,它清晰地定义了 alova 什么应该做。 - -alova 是一个轻量级的请求策略库,**它的使命就是让开发者在编写少量代码的同时,也能实现更高效地 Client-Server 数据交互**。 - -对于开发者来说,alova 为他们提供了简单的 api 和开箱即用的高级请求功能,以及各种简单的、高性能的请求策略模块,对于应用的用户来说,它们可以享受到 alova 的高性能数据交互带来的流畅体验,因此,alova 具备了以下特性: - -1. 与 axios 相似的 api 设计,让使用者学习成本更低; -2. 深度绑定 UI 框架,大大提高开发者的使用收益; -3. 开箱即用的高级功能,避免重复封装,例如请求共享、请求缓存等,减少开发者重复封装; -4. 平台无关的编码方式,可在不同平台完美迁移; -5. 高扩展性设计,可封装高复用、高性能的业务相关的请求策略; -6. 高聚合低耦合的 method 设计,提高 api 代码维护性; - -### alova 设计理念 - -设计理念指出了它应该如何设计,以下为 alova 的核心设计理念。 - -1. Method 代理设计,高聚合、平台无关的设计,贯穿请求始终,你在任意请求函数中都应该可以访问到它,从另一个角度说,与请求相关的信息也应该被放在 method 实例中; -2. 轻量级,在编码中尽量保持源码简洁,例如避免重复代码、合并变量声明、原型链函数封装、无相似 api、tree shaking,但长变量名是被允许的,因为在编译时它将会被单个字母替代; -3. 高扩展性设计,其一,alova 的设计中大量使用了适配器模式和钩子函数,例如适配器有`requestAdapter`、`storageAdapter`等,钩子函数有`beforeRequest`、`reseponded`、`transformData`、`localCache`等,而且大多存在默认行为,这样设计的目的是为了在保留高扩展性的同时,使用也足够简单;其二,全局请求参数可覆盖,例如`timeout`、`shareRequest`等,对于特别的请求可单独设置这些参数。 -4. api 设计具有普适性,其一,它表示此 api 的功能具有较高的抽象层级,而不是针对某一个具体业务而提出的;其二,api 设计具有可扩展性,以适应 api 的迭代 - -> api 普适性设计仅适用于 alova 库,如果你正在构思一个请求策略,那么可以根据具体业务来设计。 - -## 选择你感兴趣的贡献点 - -### 在项目中使用 alova - -我们认为,你们在项目中使用 alova 也是属于 alova 的贡献者,这也是在告诉人们,alova 是值得信任的开源项目,请在[这个 issue](https://github.com/alovajs/alova/issues/165)中提交你的项目,这可能会获得在 alova 官网展示你项目的机会。 - -### 为 alova 点星 - -虽然这可能会被认为微不足道,但这代表了你对 alova 的认可,对 alova 来说每一个 star 也是至关重要,请在[alova 的 Github 仓库](https://github.com/alovajs/alova)的右上角为我们点亮星星,这对我们很重要。 - -### 报告 bug - -请移步到[Github new issue](https://github.com/alovajs/alova/issues/new/choose)中选择对应的模板提交,详细说明将会在提交 issue 中展示。 - -**请注意:** 如果你想问 alova 相关的问题,请到[Github Disscussion](https://github.com/alovajs/alova/discussions)中创建,在 issue 中提问将会被立即关闭。 - -### 提出新特性想法 - -为了让 alova 可以实现它的价值和目标,在提交一个新特性想法前,请仔细阅读[alova 使命和设计理念](#alova-使命和设计理念),并保证你的新想法符合 alova 的使命和设计理念。 - -然后,请到[🚀 新特性提案](https://github.com/alovajs/alova/issues/new?assignees=&labels=feature-request&projects=&template=FEATURE_REQUEST_zh-CN.yml)中提交,详细说明将会在提交 issue 时展示。 - -### Pull Request - -你可以通过 pull request 贡献以下 3 个方面的代码。如果你是一位有意向参与的新伙伴,在[Github 贡献列表](https://github.com/alovajs/alova/contribute)中列出了所有的`good first issue`的 issues,它用来告诉有兴趣参与贡献的新伙伴,这个是一个好的开始。 - -#### bug 修改 - -在 Github issues 中被标记为[`bug:confirmed`](https://github.com/alovajs/alova/labels/bug%3Aconfirmed)的 issues,都是已被确认的 bug,你可以自由选择。 - -如果你自己遇到了 bug,也请先[报告 bug](#报告-bug)确保 bug 被确认,以避免造成无效的 pull request。 - -#### 新特性开发 - -在 Github issues 中被标记为[`feature-request:confirmed`](https://github.com/alovajs/alova/labels/feature-request%3Aconfirmed)的 issues,都是已被确认的新特性,你可以自由选择。 - -如果你有一个添加新特性的想法,也请先[提交一个新特性想法的 issue](#提出新特性想法)确保想法被确认,以避免造成无效的 pull request。 - -#### 项目配置 - -如果你很擅长项目配置,并发现了 alova 项目的不足之处,例如不够完整的配置、配置版本太老旧、自动化不足(包含项目开发自动化和 Github 仓库管理的自动化),你也可以按[新特性开发](#新特性开发)的流程进行贡献。 - -:::warning 重要 - -1. 在开发前请仔细阅读[开发指南](./developing-guidelines),它可以一步步地指导你如何贡献代码。 -2. 在你确定了一个需要解决的 issue 时,请确保它没有被其他人的 pull request 标记,它表示已被人先占有。 - -::: - -### 基于 alova 编写适配器和策略库 - -alova 提供了高扩展特性,你可以基于它编写自己的 js 库。 - -#### 自定义适配器 - -自定义各类适配器以满足不同环境下的运行要求,以下几个方向可供参考: - -1. 自定义 statesHook,满足在不同 UI 框架下执行,例如`solid/qwik`,目前内置支持`react/vue/svelte`,请阅读[自定义 statesHook](/tutorial/custom/custom-stateshook); -2. 自定义请求适配器,让 alova 可以与更多请求方案协作,例如`GraphQL/SSE`等; -3. 自定义存储适配器,满足不同环境的存储,例如`react-native`; -4. 以上任意的组合,例如官方的[uniapp 适配器](https://github.com/alovajs/adapter-uniapp),其中包含了请求适配器、存储适配器。 - -#### 自定义请求策略 - -请求策略可以帮助开发者更高效地编写出高性能功能,虽然官方的 [alova/scene](/tutorial/strategy) 提供了一些常用的请求策略,但还不足以满足广大开发者各种请求相关的业务场景,基于 alova 自定义你自己的可复用请求策略是一个不错的选择,也可以将它们发布到 npm 上给大家使用。 - -:::tip 提交你的项目 - -如果你编写了基于 alova 的 js 库,请在[这个 issue](https://github.com/alovajs/alova/issues/165)中提交你的项目,这可以让你的项目获得在 alova 官网展示的机会。 - -::: - -### 参与社区交流/PR review - -如果你对技术交流感兴趣,那么参与更多的社区交流可能更适合你,你可以在 Github issues 中参与 bug 和新特性的讨论,也可以在[Github Disscussion](https://github.com/alovajs/alova/discussions)中、[Discord](https://discord.gg/S47QGJgkVb)或[QQ 频道](https://pd.qq.com/s/1cdjx0nnw)中为别人解答问题,这可以让你与世界各地的人交流,是一件很有趣的事情。 - -同时,你也可以在[pull request](https://github.com/alovajs/alova/pulls)中参与 PR review,这也是一种交流的主题。 - -### 发布和传播 alova 的信息 - -你可以在任何社交平台、短视频平台,或技术分享平台发布或转发传播任何有利于 alova 发展的信息,这有利于提高 alova 的影响力。我们将会筛选出相关的文章或视频在 alova 官网展示它们。这里有一些优质的文章: - -- [是时候该换掉你的 axios 了](https://juejin.cn/post/7213923957824979000) -- [(深度)开源框架/库的伟大与罪恶](https://juejin.cn/post/7215608036394729532) -- [Alova.js 筆記-試用相較 axios 更輕量、更高集成的請求庫](https://uu9924079.medium.com/alova-js-%E7%AD%86%E8%A8%98-%E8%A9%A6%E7%94%A8%E7%9B%B8%E8%BC%83-axios-%E6%9B%B4%E8%BC%95%E9%87%8F-%E6%9B%B4%E9%AB%98%E9%9B%86%E6%88%90%E7%9A%84%E8%AB%8B%E6%B1%82%E5%BA%AB-546ec5424df9) - -### 分享使用经验 - -如果你有值得分享的 alova 使用经验,或者较好的实践案例,你可以在[Github Disscussion 的 Practices](https://github.com/alovajs/alova/discussions/categories/practices)中分享,较好的分享也将会在官方文档中展示。 - -### 项目合作 - -我们欢迎与任何组织或个人进行项目合作,这可以帮助我们扩大 alova 的影响力,加速项目的发展。如果您有任何合作建议或意向,请发送邮件到**hujou555@gmail.com**与我们联系。 - -### 项目捐赠 - -您可以在以下三个渠道为项目捐赠,捐赠特权说明请进入捐赠页面查看。 - -1. [Github sponsors](https://github.com/alovajs) -2. [OpenCollective](https://opencollective.com/alova) -3. [afdian](https://afdian.net/a/huzhen555) - -### 更正或编写文档 - -如果你需要添加新的文档内容,或发现 alova 的文档有错误,例如错误的示例、错误的用词、描述不正确、或未提及的内容,你可以[新建文档仓库 issue](https://github.com/alovajs/alovajs.github.io/issues/new),或[新建文档仓库 pull request](https://github.com/alovajs/alovajs.github.io/fork)直接修改错误,这应该是更好的选择,我们欢迎任何对文档的改进建议或贡献。 - -### 翻译文档 - -如果你擅长不同的语言,欢迎你为 alova 文档进行翻译,这将有助于扩展 alova 的使用范围和受众群体。 - -## 成为核心团队成员 - -具体查看[这边的内容](./become-core-member) +--- +title: 贡献指南 +--- + +# alova 贡献指南 + +你好,很高兴在这遇到你,这是一份详细的 alova 贡献指南,它包含对 alova 各个方面的贡献提供了详细的指导,请继续往下看。 + +## 前言 + +在过去的一段时间里,我们在 Github issues 和 Github Disscussion 中收到了来自世界各地的开发者积极参与的信息,深感荣幸,这意味着 alova 正在被越来越多的开发者喜爱。即便如此,alova 也还属于新秀,它依然还有很长一段路需要走。 + +**我们期望将 alova 打造成每位愿意参与的人的共同项目,我们以开放包容的态度鼓励每个人成为 alova 社区的贡献者。而且,我们认为贡献 alova 不局限于代码贡献,而是参与任何有利于 alova 发展的活动都属于贡献 alova,** 现在参与贡献可以为你赢得更多的有效贡献机会,它可以让你为全世界的开发者提供你的价值,即使你是一位初级开发者,只要想法符合 [alova 的使命和设计理念](#alova-使命和设计理念),也请大方地参与进来! + +> 这里有一份[社区行为公约](./code-of-conduct),请参阅。 + +## 贡献目录 + +这里提供 13 个可贡献之处供你选择,但不局限于这些,你可以选择自己希望参与的部分后,链接到对应位置详细阅读: + +- [在项目中使用 alova](#在项目中使用-alova) +- [为 alova 点星](#为-alova-点星) +- [报告 bug](#报告-bug) +- [提出新特性想法](#提出新特性想法) +- [Pull Request](#pull-request) +- [基于 alova 编写适配器和策略库](#基于-alova-编写适配器和策略库) +- [参与社区交流/PR review](#参与社区交流pr-review) +- [发布和传播有利于 alova 的信息](#发布和传播有利于-alova-的信息) +- [分享使用经验](#分享使用经验) +- [项目合作](#项目合作) +- [项目捐赠](#项目捐赠) +- [更正或编写文档](#更正或编写文档) +- [翻译文档](#翻译文档) + +## alova 使命和设计理念 + +### alova 使命 + +alova 的使命为它指出了明确的发展方向,它清晰地定义了 alova 什么应该做。 + +alova 是一个轻量级的请求策略库,**它的使命就是让开发者在编写少量代码的同时,也能实现更高效地 Client-Server 数据交互**。 + +对于开发者来说,alova 为他们提供了简单的 api 和开箱即用的高级请求功能,以及各种简单的、高性能的请求策略模块,对于应用的用户来说,它们可以享受到 alova 的高性能数据交互带来的流畅体验,因此,alova 具备了以下特性: + +1. 与 axios 相似的 api 设计,让使用者学习成本更低; +2. 深度绑定 UI 框架,大大提高开发者的使用收益; +3. 开箱即用的高级功能,避免重复封装,例如请求共享、请求缓存等,减少开发者重复封装; +4. 平台无关的编码方式,可在不同平台完美迁移; +5. 高扩展性设计,可封装高复用、高性能的业务相关的请求策略; +6. 高聚合低耦合的 method 设计,提高 api 代码维护性; + +### alova 设计理念 + +设计理念指出了它应该如何设计,以下为 alova 的核心设计理念。 + +1. Method 代理设计,高聚合、平台无关的设计,贯穿请求始终,你在任意请求函数中都应该可以访问到它,从另一个角度说,与请求相关的信息也应该被放在 method 实例中; +2. 轻量级,在编码中尽量保持源码简洁,例如避免重复代码、合并变量声明、原型链函数封装、无相似 api、tree shaking,但长变量名是被允许的,因为在编译时它将会被单个字母替代; +3. 高扩展性设计,其一,alova 的设计中大量使用了适配器模式和钩子函数,例如适配器有`requestAdapter`、`storageAdapter`等,钩子函数有`beforeRequest`、`reseponded`、`transformData`、`localCache`等,而且大多存在默认行为,这样设计的目的是为了在保留高扩展性的同时,使用也足够简单;其二,全局请求参数可覆盖,例如`timeout`、`shareRequest`等,对于特别的请求可单独设置这些参数。 +4. api 设计具有普适性,其一,它表示此 api 的功能具有较高的抽象层级,而不是针对某一个具体业务而提出的;其二,api 设计具有可扩展性,以适应 api 的迭代 + +> api 普适性设计仅适用于 alova 库,如果你正在构思一个请求策略,那么可以根据具体业务来设计。 + +## 选择你感兴趣的贡献点 + +### 在项目中使用 alova + +我们认为,你们在项目中使用 alova 也是属于 alova 的贡献者,这也是在告诉人们,alova 是值得信任的开源项目,请在[这个 issue](https://github.com/alovajs/alova/issues/165)中提交你的项目,这可能会获得在 alova 官网展示你项目的机会。 + +### 为 alova 点星 + +虽然这可能会被认为微不足道,但这代表了你对 alova 的认可,对 alova 来说每一个 star 也是至关重要,请在[alova 的 Github 仓库](https://github.com/alovajs/alova)的右上角为我们点亮星星,这对我们很重要。 + +### 报告 bug + +请移步到[Github new issue](https://github.com/alovajs/alova/issues/new/choose)中选择对应的模板提交,详细说明将会在提交 issue 中展示。 + +**请注意:** 如果你想问 alova 相关的问题,请到[Github Disscussion](https://github.com/alovajs/alova/discussions)中创建,在 issue 中提问将会被立即关闭。 + +### 提出新特性想法 + +为了让 alova 可以实现它的价值和目标,在提交一个新特性想法前,请仔细阅读[alova 使命和设计理念](#alova-使命和设计理念),并保证你的新想法符合 alova 的使命和设计理念。 + +然后,请到[🚀 新特性提案](https://github.com/alovajs/alova/issues/new?assignees=&labels=feature-request&projects=&template=FEATURE_REQUEST_zh-CN.yml)中提交,详细说明将会在提交 issue 时展示。 + +### Pull Request + +你可以通过 pull request 贡献以下 3 个方面的代码。如果你是一位有意向参与的新伙伴,在[Github 贡献列表](https://github.com/alovajs/alova/contribute)中列出了所有的`good first issue`的 issues,它用来告诉有兴趣参与贡献的新伙伴,这个是一个好的开始。 + +#### bug 修改 + +在 Github issues 中被标记为[`bug:confirmed`](https://github.com/alovajs/alova/labels/bug%3Aconfirmed)的 issues,都是已被确认的 bug,你可以自由选择。 + +如果你自己遇到了 bug,也请先[报告 bug](#报告-bug)确保 bug 被确认,以避免造成无效的 pull request。 + +#### 新特性开发 + +在 Github issues 中被标记为[`feature-request:confirmed`](https://github.com/alovajs/alova/labels/feature-request%3Aconfirmed)的 issues,都是已被确认的新特性,你可以自由选择。 + +如果你有一个添加新特性的想法,也请先[提交一个新特性想法的 issue](#提出新特性想法)确保想法被确认,以避免造成无效的 pull request。 + +#### 项目配置 + +如果你很擅长项目配置,并发现了 alova 项目的不足之处,例如不够完整的配置、配置版本太老旧、自动化不足(包含项目开发自动化和 Github 仓库管理的自动化),你也可以按[新特性开发](#新特性开发)的流程进行贡献。 + +:::warning 重要 + +1. 在开发前请仔细阅读[开发指南](./developing-guidelines),它可以一步步地指导你如何贡献代码。 +2. 在你确定了一个需要解决的 issue 时,请确保它没有被其他人的 pull request 标记,它表示已被人先占有。 + +::: + +### 基于 alova 编写适配器和策略库 + +alova 提供了高扩展特性,你可以基于它编写自己的 js 库。 + +#### 自定义适配器 + +自定义各类适配器以满足不同环境下的运行要求,以下几个方向可供参考: + +1. 自定义 statesHook,满足在不同 UI 框架下执行,例如`solid/qwik`,目前内置支持`react/vue/svelte`,请阅读[自定义 statesHook](/tutorial/custom/custom-stateshook); +2. 自定义请求适配器,让 alova 可以与更多请求方案协作,例如`GraphQL/SSE`等; +3. 自定义存储适配器,满足不同环境的存储,例如`react-native`; +4. 以上任意的组合,例如官方的[uniapp 适配器](https://github.com/alovajs/adapter-uniapp),其中包含了请求适配器、存储适配器。 + +#### 自定义请求策略 + +请求策略可以帮助开发者更高效地编写出高性能功能,虽然官方的 [alova/scene](/tutorial/strategy) 提供了一些常用的请求策略,但还不足以满足广大开发者各种请求相关的业务场景,基于 alova 自定义你自己的可复用请求策略是一个不错的选择,也可以将它们发布到 npm 上给大家使用。 + +:::tip 提交你的项目 + +如果你编写了基于 alova 的 js 库,请在[这个 issue](https://github.com/alovajs/alova/issues/165)中提交你的项目,这可以让你的项目获得在 alova 官网展示的机会。 + +::: + +### 参与社区交流/PR review + +如果你对技术交流感兴趣,那么参与更多的社区交流可能更适合你,你可以在 Github issues 中参与 bug 和新特性的讨论,也可以在[Github Disscussion](https://github.com/alovajs/alova/discussions)中、[Discord](https://discord.gg/S47QGJgkVb)或[QQ 频道](https://pd.qq.com/s/1cdjx0nnw)中为别人解答问题,这可以让你与世界各地的人交流,是一件很有趣的事情。 + +同时,你也可以在[pull request](https://github.com/alovajs/alova/pulls)中参与 PR review,这也是一种交流的主题。 + +### 发布和传播 alova 的信息 + +你可以在任何社交平台、短视频平台,或技术分享平台发布或转发传播任何有利于 alova 发展的信息,这有利于提高 alova 的影响力。我们将会筛选出相关的文章或视频在 alova 官网展示它们。这里有一些优质的文章: + +- [是时候该换掉你的 axios 了](https://juejin.cn/post/7213923957824979000) +- [(深度)开源框架/库的伟大与罪恶](https://juejin.cn/post/7215608036394729532) +- [Alova.js 筆記-試用相較 axios 更輕量、更高集成的請求庫](https://uu9924079.medium.com/alova-js-%E7%AD%86%E8%A8%98-%E8%A9%A6%E7%94%A8%E7%9B%B8%E8%BC%83-axios-%E6%9B%B4%E8%BC%95%E9%87%8F-%E6%9B%B4%E9%AB%98%E9%9B%86%E6%88%90%E7%9A%84%E8%AB%8B%E6%B1%82%E5%BA%AB-546ec5424df9) + +### 分享使用经验 + +如果你有值得分享的 alova 使用经验,或者较好的实践案例,你可以在[Github Disscussion 的 Practices](https://github.com/alovajs/alova/discussions/categories/practices)中分享,较好的分享也将会在官方文档中展示。 + +### 项目合作 + +我们欢迎与任何组织或个人进行项目合作,这可以帮助我们扩大 alova 的影响力,加速项目的发展。如果您有任何合作建议或意向,请发送邮件到**hujou555@gmail.com**与我们联系。 + +### 项目捐赠 + +您可以在以下三个渠道为项目捐赠,捐赠特权说明请进入捐赠页面查看。 + +1. [Github sponsors](https://github.com/alovajs) +2. [OpenCollective](https://opencollective.com/alova) +3. [afdian](https://afdian.net/a/huzhen555) + +### 更正或编写文档 + +如果你需要添加新的文档内容,或发现 alova 的文档有错误,例如错误的示例、错误的用词、描述不正确、或未提及的内容,你可以[新建文档仓库 issue](https://github.com/alovajs/alovajs.github.io/issues/new),或[新建文档仓库 pull request](https://github.com/alovajs/alovajs.github.io/fork)直接修改错误,这应该是更好的选择,我们欢迎任何对文档的改进建议或贡献。 + +### 翻译文档 + +如果你擅长不同的语言,欢迎你为 alova 文档进行翻译,这将有助于扩展 alova 的使用范围和受众群体。 + +## 成为核心团队成员 + +具体查看[这边的内容](./become-core-member) diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/contributing/02-become-core-member.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/contributing/02-become-core-member.md index 798bca5a2..9f9d5a3cc 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/contributing/02-become-core-member.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/contributing/02-become-core-member.md @@ -1,36 +1,35 @@ ---- -title: 成为核心团队成员 -sidebar_position: 20 ---- - -🤝🤝🤝 如果你也认同 alovajs 的理念,那就让我们一同创造下一代请求库吧! - -## 职责 - -1. 负责 vscode 插件项目、请求场景模块和适配器项目,以及鸿蒙版 alovajs 的开发和维护。 -2. 编写和优化相关的开发文档。 -3. 深度参与项目设计和实现,推广维护等整个生命周期。 -4. 参与项目发展的决策和商业化探索。 - -## 收益 - -1. 核心成员头衔,有机会在前端领域提升自己的声誉,从而拓宽职业发展道路。 -2. 更多公开分享的机会,与更多前端人员接触的机会。 -3. 通过深度参与项目的设计、实现和运营,拓宽项目思维和技术经验。 -4. 对项目的决策权。 -5. 盈利分成。 - -## 要求 - -1. 认同 alova 设计理念和发展方向,并且愿意付出努力一同前行。 -2. 对技术的热情和责任心、团队协作。 -3. 技术能力能覆盖现有需求即可。 -4. 有一定的业余时间。 - -## 如何加入 - -有意向者请添加以下微信,并注明 **alova 核心成员申请**。 - -import wechatQrcode from '@site/static/img/wechat_personal.jpg'; - -wechat_qrcode +--- +title: 成为核心团队成员 +--- + +🤝🤝🤝 如果你也认同 alovajs 的理念,那就让我们一同创造下一代请求库吧! + +## 职责 + +1. 负责 vscode 插件项目、请求场景模块和适配器项目,以及鸿蒙版 alovajs 的开发和维护。 +2. 编写和优化相关的开发文档。 +3. 深度参与项目设计和实现,推广维护等整个生命周期。 +4. 参与项目发展的决策和商业化探索。 + +## 收益 + +1. 核心成员头衔,有机会在前端领域提升自己的声誉,从而拓宽职业发展道路。 +2. 更多公开分享的机会,与更多前端人员接触的机会。 +3. 通过深度参与项目的设计、实现和运营,拓宽项目思维和技术经验。 +4. 对项目的决策权。 +5. 盈利分成。 + +## 要求 + +1. 认同 alova 设计理念和发展方向,并且愿意付出努力一同前行。 +2. 对技术的热情和责任心、团队协作。 +3. 技术能力能覆盖现有需求即可。 +4. 有一定的业余时间。 + +## 如何加入 + +有意向者请添加以下微信,并注明 **alova 核心成员申请**。 + +import wechatQrcode from '@site/static/img/wechat_personal.jpg'; + +wechat_qrcode diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/contributing/03-developing-guidelines.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/contributing/03-developing-guidelines.md index c3177a591..538a77392 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/contributing/03-developing-guidelines.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/contributing/03-developing-guidelines.md @@ -1,138 +1,137 @@ ---- -title: 开发指南 -sidebar_position: 30 ---- - -:::info 版本要求 - -Node.js 16+, npm 8+ - -::: - -## 1. fork 仓库 - -[打开 alova 仓库 fork 页](https://github.com/alovajs/alova/fork),点击“Create fork”fork 仓库,并将已 fork 的仓库克隆到本地。 - -## 2. 克隆项目到本地 - -使用`git clone`命令,或`Github Desktop`应用克隆项目。 - -## 3. 新建 pull request - -你可以在编写完代码后[通过 fork 仓库创建 pull request](https://docs.github.com/zh/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork),也可以分为任意多次提交代码,而无需一次提交完整代码。 - -## 4. 在本地编码 - -### 安装依赖 - -使用`npm install`安装依赖。 - -### 安装推荐插件(vscode) - -如果你使用 vscode,将会推荐你安装以下插件: - -- eslint:检查代码质量 -- prettier:格式化代码 -- jest:自动执行单元测试用例,以及执行单个合集或单元测试用例 -- EditorConfig:保证文件格式一致 - -### 项目结构 - -``` -|-.github -| |-ISSUE_TEMPLATE -> github issues模板 -| |-workflows -> github action -|-.husky -> husky配置 -|-.vscode -> vscode配置 -|-config -> rollup打包文件 -|-src -> 源代码 -|-test -> 单元测试 -| |-browser -> 浏览器环境单元测试 -| |-server -> SSR单元测试 -| |-components -> 单元测试组件 -| |-mockServer.ts -> mock接口(msw) -|-typings -> ts类型声明 -|-其他配置文件 - -``` - -### 编码规范 - -#### 代码格式 - -如果你安装了`prettier`插件,在每次保存文件时会自动进行格式化代码,因此你可以不必在意格式的问题。 - -#### 尽量减少代码 - -alova 的特性之一是轻量化,因此在编码时需要尽量减少编码量,这里有几个需要遵循的编码规范: - -1. 避免出现相同的代码块,这可以减少库的代码量,但两行代码可能就不值得封装; -2. 使用一个变量声明符聚合变量的声明,例如: - -```javascript -// ❌ -const a = 1; -const b = 2; - -// ✅ -const a = 1, - b = 2; -``` - -3. 使用常量保存固定值、原型方法,在编译`uglify`阶段减少代码量。在`src/utils/variables.ts`中定义了常用的固定值和原型方法。 - -```javascript -// ❌ -if (a === false) { - // ... -} -arr.forEach(item => { - // ... -}); - -// ✅ -import { falseValue, forEach } from '@/utils/variables'; -if (a === falseValue) { - // ... -} -forEach(arr, item => { - // ... -}); -``` - -## 5. 单元测试指南 - -编写完代码后,添加对应的单元测试用例,尽量包含边缘情况的测试。 - -alova 项目使用 jest 作为单元测试框架,使用 msw 作为模拟服务器。建议使用 TDD 模式。每次修改代码后,运行对应的单元测试并通过它。 - -:::warning 重要 - -当准备提交代码时,请确保通过了全部单元测试,当您处理 pull request 时,可以有多个小提交,GitHub 可以在合并之前自动压缩它们。 - -::: - -1. 添加浏览器相关单元测试用例,请添加到`test/browser`中对应的测试合集,如果没有合适的测试合集可自行创建; -2. 添加 SSR 相关单元测试用例,请添加到`test/server`中对应的测试合集,如果没有合适的测试合集可自行创建; - -### 运行和调试单个测试用例或合集 - -建议使用**jest**插件(上面推荐的插件之一)对单个用例或合集进行测试,你可以在测试用例中右键点击运行指定的用例,选择`Run Test`运行此测试用例,选择`Debug Test`断点调试此测试用例,如图: - -![image](https://github.com/alovajs/alova/assets/29848971/a94ba9db-c100-472f-b870-6bcecb031bea) - -### 运行全部测试用例 - -1. 使用**jest**插件运行,如下图: - -![image](https://github.com/alovajs/alova/assets/29848971/5af3ff15-16b7-4b28-9ae6-d0b5a236b181) - -2. 通过命令行`npm run test:browser`运行浏览器单元测试,通过`npm run test:node`运行 SSR 单元测试,通过`npm run test`同时运行两者。 - -## 6. 提交代码 - -alova 使用了 [semantic-release](https://semantic-release.gitbook.io) 作为自动发布工具,它可以在合并代码到`main`后自动发布新版本包,以及生成`CHANGELOG`,但需要确保提交的消息格式遵循[提交信息约定](https://www.conventionalcommits.org/zh-hans/v1.0.0/),建议你尽量使用`npm run commit`来自动生成符合规范的 git message。 - -## 7.编写文档 - -如果你正在添加新特性,可尝试添加新特性的相关文档说明,详细请阅读[更正或编写文档](/contributing/overview#更正或编写文档),否则请在 pull request 中说明。 +--- +title: 开发指南 +--- + +:::info 版本要求 + +Node.js 16+, npm 8+ + +::: + +## 1. fork 仓库 + +[打开 alova 仓库 fork 页](https://github.com/alovajs/alova/fork),点击“Create fork”fork 仓库,并将已 fork 的仓库克隆到本地。 + +## 2. 克隆项目到本地 + +使用`git clone`命令,或`Github Desktop`应用克隆项目。 + +## 3. 新建 pull request + +你可以在编写完代码后[通过 fork 仓库创建 pull request](https://docs.github.com/zh/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork),也可以分为任意多次提交代码,而无需一次提交完整代码。 + +## 4. 在本地编码 + +### 安装依赖 + +使用`npm install`安装依赖。 + +### 安装推荐插件(vscode) + +如果你使用 vscode,将会推荐你安装以下插件: + +- eslint:检查代码质量 +- prettier:格式化代码 +- jest:自动执行单元测试用例,以及执行单个合集或单元测试用例 +- EditorConfig:保证文件格式一致 + +### 项目结构 + +``` +|-.github +| |-ISSUE_TEMPLATE -> github issues模板 +| |-workflows -> github action +|-.husky -> husky配置 +|-.vscode -> vscode配置 +|-config -> rollup打包文件 +|-src -> 源代码 +|-test -> 单元测试 +| |-browser -> 浏览器环境单元测试 +| |-server -> SSR单元测试 +| |-components -> 单元测试组件 +| |-mockServer.ts -> mock接口(msw) +|-typings -> ts类型声明 +|-其他配置文件 + +``` + +### 编码规范 + +#### 代码格式 + +如果你安装了`prettier`插件,在每次保存文件时会自动进行格式化代码,因此你可以不必在意格式的问题。 + +#### 尽量减少代码 + +alova 的特性之一是轻量化,因此在编码时需要尽量减少编码量,这里有几个需要遵循的编码规范: + +1. 避免出现相同的代码块,这可以减少库的代码量,但两行代码可能就不值得封装; +2. 使用一个变量声明符聚合变量的声明,例如: + +```javascript +// ❌ +const a = 1; +const b = 2; + +// ✅ +const a = 1, + b = 2; +``` + +3. 使用常量保存固定值、原型方法,在编译`uglify`阶段减少代码量。在`src/utils/variables.ts`中定义了常用的固定值和原型方法。 + +```javascript +// ❌ +if (a === false) { + // ... +} +arr.forEach(item => { + // ... +}); + +// ✅ +import { falseValue, forEach } from '@/utils/variables'; +if (a === falseValue) { + // ... +} +forEach(arr, item => { + // ... +}); +``` + +## 5. 单元测试指南 + +编写完代码后,添加对应的单元测试用例,尽量包含边缘情况的测试。 + +alova 项目使用 jest 作为单元测试框架,使用 msw 作为模拟服务器。建议使用 TDD 模式。每次修改代码后,运行对应的单元测试并通过它。 + +:::warning 重要 + +当准备提交代码时,请确保通过了全部单元测试,当您处理 pull request 时,可以有多个小提交,GitHub 可以在合并之前自动压缩它们。 + +::: + +1. 添加浏览器相关单元测试用例,请添加到`test/browser`中对应的测试合集,如果没有合适的测试合集可自行创建; +2. 添加 SSR 相关单元测试用例,请添加到`test/server`中对应的测试合集,如果没有合适的测试合集可自行创建; + +### 运行和调试单个测试用例或合集 + +建议使用**jest**插件(上面推荐的插件之一)对单个用例或合集进行测试,你可以在测试用例中右键点击运行指定的用例,选择`Run Test`运行此测试用例,选择`Debug Test`断点调试此测试用例,如图: + +![image](https://github.com/alovajs/alova/assets/29848971/a94ba9db-c100-472f-b870-6bcecb031bea) + +### 运行全部测试用例 + +1. 使用**jest**插件运行,如下图: + +![image](https://github.com/alovajs/alova/assets/29848971/5af3ff15-16b7-4b28-9ae6-d0b5a236b181) + +2. 通过命令行`npm run test:browser`运行浏览器单元测试,通过`npm run test:node`运行 SSR 单元测试,通过`npm run test`同时运行两者。 + +## 6. 提交代码 + +alova 使用了 [semantic-release](https://semantic-release.gitbook.io) 作为自动发布工具,它可以在合并代码到`main`后自动发布新版本包,以及生成`CHANGELOG`,但需要确保提交的消息格式遵循[提交信息约定](https://www.conventionalcommits.org/zh-hans/v1.0.0/),建议你尽量使用`npm run commit`来自动生成符合规范的 git message。 + +## 7.编写文档 + +如果你正在添加新特性,可尝试添加新特性的相关文档说明,详细请阅读[更正或编写文档](/contributing/overview#更正或编写文档),否则请在 pull request 中说明。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/contributing/04-code-of-conduct.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/contributing/04-code-of-conduct.md index 42f683071..593d57076 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/contributing/04-code-of-conduct.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/contributing/04-code-of-conduct.md @@ -1,90 +1,89 @@ ---- -title: 行为准则 -sidebar_position: 40 ---- - -## 我们的承诺 - -身为社区成员、贡献者和领袖,我们承诺使社区参与者不受骚扰,无论其年龄、体型、可见或不可见的缺陷、族裔、性征、性别认同和表达、经验水平、教育程度、社会与经济地位、国籍、相貌、种族、种姓、肤色、宗教信仰、性倾向或性取向如何。 - -我们承诺以有助于建立开放、友善、多样化、包容、健康社区的方式行事和互动。 - -## 我们的准则 - -有助于为我们的社区创造积极环境的行为例子包括但不限于: - -- 表现出对他人的同情和善意 -- 尊重不同的主张、观点和感受 -- 提出和大方接受建设性意见 -- 承担责任并向受我们错误影响的人道歉 -- 注重社区共同诉求,而非个人得失 - -不当行为例子包括: - -- 使用情色化的语言或图像,及性引诱或挑逗 -- 嘲弄、侮辱或诋毁性评论,以及人身或政治攻击 -- 公开或私下的骚扰行为 -- 未经他人明确许可,公布他人的私人信息,如物理或电子邮件地址 -- 其他有理由认定为违反职业操守的不当行为 - -## 责任和权力 - -社区领袖有责任解释和落实我们所认可的行为准则,并妥善公正地对他们认为不当、威胁、冒犯或有害的任何行为采取纠正措施。 - -社区领导有权力和责任删除、编辑或拒绝或拒绝与本行为准则不相符的评论(comment)、提交(commits)、代码、维基(wiki)编辑、议题(issues)或其他贡献,并在适当时机知采取措施的理由。 - -## 适用范围 - -本行为准则适用于所有社区场合,也适用于在公共场所代表社区时的个人。 - -代表社区的情形包括使用官方电子邮件地址、通过官方社交媒体帐户发帖或在线上或线下活动中担任指定代表。 - -## 监督 - -辱骂、骚扰或其他不可接受的行为可通过 [插入联系方式] 向负责监督的社区领袖报告。 -所有投诉都将得到及时和公平的审查和调查。 - -所有社区领袖都有义务尊重任何事件报告者的隐私和安全。 - -## 处理方针 - -社区领袖将遵循下列社区处理方针来明确他们所认定违反本行为准则的行为的处理方式: - -### 1. 纠正 - -**社区影响**:使用不恰当的语言或其他在社区中被认定为不符合职业道德或不受欢迎的行为。 - -**处理意见**:由社区领袖发出非公开的书面警告,明确说明违规行为的性质,并解释举止如何不妥。或将要求公开道歉。 - -### 2. 警告 - -**社区影响**:单个或一系列违规行为。 - -**处理意见**:警告并对连续性行为进行处理。在指定时间内,不得与相关人员互动,包括主动与行为准则执行者互动。这包括避免在社区场所和外部渠道中的互动。违反这些条款可能会导致临时或永久封禁。 - -### 3. 临时封禁 - -**社区影响**: 严重违反社区准则,包括持续的不当行为。 - -**处理意见**: 在指定时间内,暂时禁止与社区进行任何形式的互动或公开交流。在此期间,不得与相关人员进行公开或私下互动,包括主动与行为准则执行者互动。违反这些条款可能会导致永久封禁。 - -### 4. 永久封禁 - -**社区影响**:行为模式表现出违反社区准则,包括持续的不当行为、骚扰个人或攻击或贬低某个类别的个体。 - -**处理意见**:永久禁止在社区内进行任何形式的公开互动。 - -## 参见 - -本行为准则改编自 [Contributor Covenant][homepage] 2.1 版, 参见 [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]。 - -社区处理方针灵感来源于 [Mozilla's code of conduct enforcement ladder][mozilla coc]。 - -有关本行为准则的常见问题的答案,参见 [https://www.contributor-covenant.org/faq][faq]。 -其他语言翻译参见 [https://www.contributor-covenant.org/translations][translations]。 - -[homepage]: https://www.contributor-covenant.org -[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html -[mozilla coc]: https://github.com/mozilla/diversity -[faq]: https://www.contributor-covenant.org/faq -[translations]: https://www.contributor-covenant.org/translations +--- +title: 行为准则 +--- + +## 我们的承诺 + +身为社区成员、贡献者和领袖,我们承诺使社区参与者不受骚扰,无论其年龄、体型、可见或不可见的缺陷、族裔、性征、性别认同和表达、经验水平、教育程度、社会与经济地位、国籍、相貌、种族、种姓、肤色、宗教信仰、性倾向或性取向如何。 + +我们承诺以有助于建立开放、友善、多样化、包容、健康社区的方式行事和互动。 + +## 我们的准则 + +有助于为我们的社区创造积极环境的行为例子包括但不限于: + +- 表现出对他人的同情和善意 +- 尊重不同的主张、观点和感受 +- 提出和大方接受建设性意见 +- 承担责任并向受我们错误影响的人道歉 +- 注重社区共同诉求,而非个人得失 + +不当行为例子包括: + +- 使用情色化的语言或图像,及性引诱或挑逗 +- 嘲弄、侮辱或诋毁性评论,以及人身或政治攻击 +- 公开或私下的骚扰行为 +- 未经他人明确许可,公布他人的私人信息,如物理或电子邮件地址 +- 其他有理由认定为违反职业操守的不当行为 + +## 责任和权力 + +社区领袖有责任解释和落实我们所认可的行为准则,并妥善公正地对他们认为不当、威胁、冒犯或有害的任何行为采取纠正措施。 + +社区领导有权力和责任删除、编辑或拒绝或拒绝与本行为准则不相符的评论(comment)、提交(commits)、代码、维基(wiki)编辑、议题(issues)或其他贡献,并在适当时机知采取措施的理由。 + +## 适用范围 + +本行为准则适用于所有社区场合,也适用于在公共场所代表社区时的个人。 + +代表社区的情形包括使用官方电子邮件地址、通过官方社交媒体帐户发帖或在线上或线下活动中担任指定代表。 + +## 监督 + +辱骂、骚扰或其他不可接受的行为可通过 [插入联系方式] 向负责监督的社区领袖报告。 +所有投诉都将得到及时和公平的审查和调查。 + +所有社区领袖都有义务尊重任何事件报告者的隐私和安全。 + +## 处理方针 + +社区领袖将遵循下列社区处理方针来明确他们所认定违反本行为准则的行为的处理方式: + +### 1. 纠正 + +**社区影响**:使用不恰当的语言或其他在社区中被认定为不符合职业道德或不受欢迎的行为。 + +**处理意见**:由社区领袖发出非公开的书面警告,明确说明违规行为的性质,并解释举止如何不妥。或将要求公开道歉。 + +### 2. 警告 + +**社区影响**:单个或一系列违规行为。 + +**处理意见**:警告并对连续性行为进行处理。在指定时间内,不得与相关人员互动,包括主动与行为准则执行者互动。这包括避免在社区场所和外部渠道中的互动。违反这些条款可能会导致临时或永久封禁。 + +### 3. 临时封禁 + +**社区影响**: 严重违反社区准则,包括持续的不当行为。 + +**处理意见**: 在指定时间内,暂时禁止与社区进行任何形式的互动或公开交流。在此期间,不得与相关人员进行公开或私下互动,包括主动与行为准则执行者互动。违反这些条款可能会导致永久封禁。 + +### 4. 永久封禁 + +**社区影响**:行为模式表现出违反社区准则,包括持续的不当行为、骚扰个人或攻击或贬低某个类别的个体。 + +**处理意见**:永久禁止在社区内进行任何形式的公开互动。 + +## 参见 + +本行为准则改编自 [Contributor Covenant][homepage] 2.1 版, 参见 [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]。 + +社区处理方针灵感来源于 [Mozilla's code of conduct enforcement ladder][mozilla coc]。 + +有关本行为准则的常见问题的答案,参见 [https://www.contributor-covenant.org/faq][faq]。 +其他语言翻译参见 [https://www.contributor-covenant.org/translations][translations]。 + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[mozilla coc]: https://github.com/mozilla/diversity +[faq]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/01-init-page.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/01-init-page.md index aff0d9759..364f39533 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/01-init-page.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/01-init-page.md @@ -1,16 +1,15 @@ ---- -title: (vue)页面初始化请求 -sidebar_position: 10 ---- - -import InitPage from '@site/example-links/InitPage'; - -> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); - - - -:::info 示例说明 - -本示例主要展示请求的简单性,它将返回状态化的请求状态、数据等,这些状态可直接在视图中使用,并且直接由 alova 管理 - -::: +--- +title: 页面初始化请求 +--- + +import InitPage from '@site/example-links/InitPage'; + +> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); + + + +:::info 示例说明 + +本示例主要展示请求的简单性,它将返回状态化的请求状态、数据等,这些状态可直接在视图中使用,并且直接由 alova 管理 + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/02-submit-form.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/02-submit-form.md index fccbbb3b7..b830748ea 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/02-submit-form.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/02-submit-form.md @@ -1,16 +1,15 @@ ---- -title: (vue)表单提交 -sidebar_position: 20 ---- - -import SubmitForm from '@site/example-links/SubmitForm'; - -> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); - - - -:::info 示例说明 - -本示例主要展示基础的数据提交编码方式 - -::: +--- +title: 表单提交 +--- + +import SubmitForm from '@site/example-links/SubmitForm'; + +> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); + + + +:::info 示例说明 + +本示例主要展示基础的数据提交编码方式 + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/03-condition-search.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/03-condition-search.md index 02ea0abeb..fcf2e42f0 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/03-condition-search.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/03-condition-search.md @@ -1,14 +1,13 @@ ---- -title: (vue)条件搜索 -sidebar_position: 30 ---- - -import ConditionSearch from '@site/example-links/ConditionSearch'; - -> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); - - - -:::info 示例说明 -本示例主要展示数据搜索的简单性,无需开发者自己维护请求状态、数据等状态,也无需手动触发请求发送,只需绑定好搜索条件的 state 即可 -::: +--- +title: 条件搜索 +--- + +import ConditionSearch from '@site/example-links/ConditionSearch'; + +> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); + + + +:::info 示例说明 +本示例主要展示数据搜索的简单性,无需开发者自己维护请求状态、数据等状态,也无需手动触发请求发送,只需绑定好搜索条件的 state 即可 +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/04-memory-cache.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/04-memory-cache.md index c6bc2dcc5..5a5dd85d1 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/04-memory-cache.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/04-memory-cache.md @@ -1,21 +1,20 @@ ---- -title: (vue)响应缓存-内存模式 -sidebar_position: 40 ---- - -import MemoryCache from '@site/example-links/MemoryCache'; - -> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); - - - -:::info 示例说明 - -内存缓存模式是将响应数据存放在内存中,缓存在刷新页面即失效。 - -_操作引导:_ - -1. 点击以下的学生列表项,将会发送请求学生详细信息,此时模态框显示 Loading 状态; -2. 点击遮罩关闭弹框,并重新打开它,此时将会命中缓存并立即显示学生详细信息,Request Records 中不再打印请求记录; - -::: +--- +title: 响应缓存-内存模式 +--- + +import MemoryCache from '@site/example-links/MemoryCache'; + +> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); + + + +:::info 示例说明 + +内存缓存模式是将响应数据存放在内存中,缓存在刷新页面即失效。 + +_操作引导:_ + +1. 点击以下的学生列表项,将会发送请求学生详细信息,此时模态框显示 Loading 状态; +2. 点击遮罩关闭弹框,并重新打开它,此时将会命中缓存并立即显示学生详细信息,Request Records 中不再打印请求记录; + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/05-storage-placeholder.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/05-storage-placeholder.md index 5a63eafae..3ea6b4d62 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/05-storage-placeholder.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/05-storage-placeholder.md @@ -1,21 +1,20 @@ ---- -title: (vue)响应缓存-缓存占位模式 -sidebar_position: 50 ---- - -import StoragePlaceholder from '@site/example-links/StoragePlaceholder'; - -> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); - - - -:::info 示例说明 - -缓存占位模式是将响应数据持久化,它将在刷新页面后立即更新到 data state 中作为占位数据,同时发送请求,开发者可以在响应前使用占位数据替代 Loading 状态。 - -_操作引导:_ - -1. 点击`Reload page`刷新页面,你不再看到 Loading 状态,而是旧数据被渲染出来了,且当请求响应后被替换为新数据; -2. 点击`Invalidate the data of placeholder`让缓存数据失效,此时你将重新看到 Loading 状态; - -::: +--- +title: 响应缓存-缓存占位模式 +--- + +import StoragePlaceholder from '@site/example-links/StoragePlaceholder'; + +> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); + + + +:::info 示例说明 + +缓存占位模式是将响应数据持久化,它将在刷新页面后立即更新到 data state 中作为占位数据,同时发送请求,开发者可以在响应前使用占位数据替代 Loading 状态。 + +_操作引导:_ + +1. 点击`Reload page`刷新页面,你不再看到 Loading 状态,而是旧数据被渲染出来了,且当请求响应后被替换为新数据; +2. 点击`Invalidate the data of placeholder`让缓存数据失效,此时你将重新看到 Loading 状态; + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/06-storage-restore.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/06-storage-restore.md index 3115eaa6b..4b349b748 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/06-storage-restore.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/06-storage-restore.md @@ -1,21 +1,20 @@ ---- -title: (vue)响应缓存-恢复模式 -sidebar_position: 60 ---- - -import StorageRestore from '@site/example-links/StorageRestore'; - -> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); - - - -:::info 示例说明 - -缓存恢复模式是将响应数据持久化,当请求命中缓存时将持久化缓存数据返回,不再发出请求。它一般用于一些需要服务端管理,但在一定时间内不变的数据,以下是节假日信息的恢复模式示例。 - -_操作引导:_ - -1. 点击`Reload page`刷新页面,你不再看到 Loading 状态,而会使用缓存数据并立即渲染到页面中,也不再发送请求; -2. 点击`Invalidate the data of placeholder`让缓存数据失效,此时你将重新看到 Loading 状态; - -::: +--- +title: 响应缓存-恢复模式 +--- + +import StorageRestore from '@site/example-links/StorageRestore'; + +> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); + + + +:::info 示例说明 + +缓存恢复模式是将响应数据持久化,当请求命中缓存时将持久化缓存数据返回,不再发出请求。它一般用于一些需要服务端管理,但在一定时间内不变的数据,以下是节假日信息的恢复模式示例。 + +_操作引导:_ + +1. 点击`Reload page`刷新页面,你不再看到 Loading 状态,而会使用缓存数据并立即渲染到页面中,也不再发送请求; +2. 点击`Invalidate the data of placeholder`让缓存数据失效,此时你将重新看到 Loading 状态; + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/07-update-state.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/07-update-state.md index 5258b992e..89ee68320 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/07-update-state.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/07-update-state.md @@ -1,20 +1,19 @@ ---- -title: (vue)跨组件/页面更新状态 -sidebar_position: 70 ---- - -import UpdateState from '@site/example-links/UpdateState'; - -> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); - - - -:::info 示例说明 - -当需要跨组件或页面更新状态时,常常会受组件层级的限制,这里有一个可以打破这个限制的方式。 - -_操作引导:_ - -在编辑框或编辑页中,新增或编辑列表数据后,列表数据被跨越组件层级的方式更新了。 - -::: +--- +title: 跨组件/页面更新状态 +--- + +import UpdateState from '@site/example-links/UpdateState'; + +> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); + + + +:::info 示例说明 + +当需要跨组件或页面更新状态时,常常会受组件层级的限制,这里有一个可以打破这个限制的方式。 + +_操作引导:_ + +在编辑框或编辑页中,新增或编辑列表数据后,列表数据被跨越组件层级的方式更新了。 + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/08-prefetch.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/08-prefetch.md index 152c1352c..6c346a73a 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/08-prefetch.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/08-prefetch.md @@ -1,21 +1,20 @@ ---- -title: (vue)数据预拉取 -sidebar_position: 90 ---- - -import Prefetch from '@site/example-links/Prefetch'; - -> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); - - - -:::info 示例说明 - -数据预拉取是一种预测用户操作行为的一种方式,来达到内容更快展示在用户面前的策略。 - -_操作引导:_ - -1. 鼠标移动到任意列表项,并停留 0.2 秒,将会在底部面板上看到详情数据的请求被发送; -2. 单击点开这个列表项,可以立即看到详情数据; - -::: +--- +title: 数据预拉取 +--- + +import Prefetch from '@site/example-links/Prefetch'; + +> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); + + + +:::info 示例说明 + +数据预拉取是一种预测用户操作行为的一种方式,来达到内容更快展示在用户面前的策略。 + +_操作引导:_ + +1. 鼠标移动到任意列表项,并停留 0.2 秒,将会在底部面板上看到详情数据的请求被发送; +2. 单击点开这个列表项,可以立即看到详情数据; + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/09-load-more.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/09-load-more.md index fa7d8ccdb..66f518a1c 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/09-load-more.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/09-load-more.md @@ -1,23 +1,22 @@ ---- -title: (vue)下拉加载更多 -sidebar_position: 80 ---- - -import LoadMore from '@site/example-links/LoadMore'; - -> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); - - - -:::info 示例说明 - -使用分页策略,实现更加高性能易用的分页功能,分页相关状态自动管理、前后一页预加载、自动维护数据的新增/编辑/替换/移除,以及请求级的防抖功能。 - -_操作引导:_ - -1. 初始化完成后会预加载下一页数据,下拉翻页无需等待; -2. 添加、删除、修改列表项无需重置列表,它将自动处理成和重新请求一致的效果; - -[usePagination 文档](/tutorial/strategy/usePagination) - -::: +--- +title: 下拉加载更多 +--- + +import LoadMore from '@site/example-links/LoadMore'; + +> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); + + + +:::info 示例说明 + +使用分页策略,实现更加高性能易用的分页功能,分页相关状态自动管理、前后一页预加载、自动维护数据的新增/编辑/替换/移除,以及请求级的防抖功能。 + +_操作引导:_ + +1. 初始化完成后会预加载下一页数据,下拉翻页无需等待; +2. 添加、删除、修改列表项无需重置列表,它将自动处理成和重新请求一致的效果; + +[usePagination 文档](/tutorial/strategy/usePagination) + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/10-paginated-list.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/10-paginated-list.md index 3b5b350cc..49183f6f0 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/10-paginated-list.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/10-paginated-list.md @@ -1,23 +1,22 @@ ---- -title: (vue)页码列表 -sidebar_position: 100 ---- - -import Pagination from '@site/example-links/Pagination'; - -> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); - - - -:::info 示例说明 - -使用分页策略,实现更加高性能易用的分页功能,分页相关状态自动管理、前后一页预加载、自动维护数据的新增/编辑/替换/移除,以及请求级的防抖功能。 - -_操作引导:_ - -1. 初始化完成后会预加载下一页数据,翻页到下一页时无需等待; -2. 添加、删除、修改列表项无需重置列表,它将自动处理成和重新请求一致的效果; - -[usePagination 文档](/tutorial/strategy/usePagination) - -::: +--- +title: 页码列表 +--- + +import Pagination from '@site/example-links/Pagination'; + +> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); + + + +:::info 示例说明 + +使用分页策略,实现更加高性能易用的分页功能,分页相关状态自动管理、前后一页预加载、自动维护数据的新增/编辑/替换/移除,以及请求级的防抖功能。 + +_操作引导:_ + +1. 初始化完成后会预加载下一页数据,翻页到下一页时无需等待; +2. 添加、删除、修改列表项无需重置列表,它将自动处理成和重新请求一致的效果; + +[usePagination 文档](/tutorial/strategy/usePagination) + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/11-controlled-cache-by-indexeddb.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/11-controlled-cache-by-indexeddb.md index 58b630c8d..c7a553669 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/11-controlled-cache-by-indexeddb.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/11-controlled-cache-by-indexeddb.md @@ -1,23 +1,22 @@ ---- -title: (vue)使用IndexedDB管理缓存 -sidebar_position: 110 ---- - -import ControlledCacheByIndexedDB from '@site/example-links/ControlledCacheByIndexedDB'; - -> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); - - - -:::info 示例说明 - -使用受控缓存让开发者自定义管理缓存,在大文件缓存下,可以配合 IndexedDB 管理本地缓存。 - -_操作引导:_ - -1. 选择其中一张图片,图片会先请求网络加载,图片数据将会保存在本地 IndexedDB 中; -2. 刷新页面,再次选择相同的图片,图片将在 IndexedDB 中获取数据,而不再发起网络请求; - -[受控缓存文档](/tutorial/cache/controlled-cache) - -::: +--- +title: 使用IndexedDB管理缓存 +--- + +import ControlledCacheByIndexedDB from '@site/example-links/ControlledCacheByIndexedDB'; + +> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); + + + +:::info 示例说明 + +使用受控缓存让开发者自定义管理缓存,在大文件缓存下,可以配合 IndexedDB 管理本地缓存。 + +_操作引导:_ + +1. 选择其中一张图片,图片会先请求网络加载,图片数据将会保存在本地 IndexedDB 中; +2. 刷新页面,再次选择相同的图片,图片将在 IndexedDB 中获取数据,而不再发起网络请求; + +[受控缓存文档](/tutorial/cache/controlled-cache) + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/12-silent-submit-setting.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/12-silent-submit-setting.md index e3f4af9c8..a85bc27e0 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/12-silent-submit-setting.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/12-silent-submit-setting.md @@ -1,23 +1,22 @@ ---- -title: (svelte)静默提交-设置页 -sidebar_position: 120 ---- - -import SettingSilentSvelte from '@site/example-links/SettingSilentSvelte'; - -> 示例以 svelte 为例,但你还可以在 react、vue 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); - - - -:::info 示例说明 - -使用静默提交策略,提交即响应,大幅降低网络波动造成的影响,让你的应用在网络不稳定,甚至断网状态下依然非常流畅。 - -_操作引导:_ - -1. 操作设置项,它将立即产生反馈,而不需要等待服务端响应; -2. 切换请求模式和网络状态,体验它们的区别; - -[静默提交策略文档](/tutorial/strategy/sensorless-data-interaction) - -::: +--- +title: 静默提交-设置页 +--- + +import SettingSilentSvelte from '@site/example-links/SettingSilentSvelte'; + +> 示例以 svelte 为例,但你还可以在 react、vue 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); + + + +:::info 示例说明 + +使用静默提交策略,提交即响应,大幅降低网络波动造成的影响,让你的应用在网络不稳定,甚至断网状态下依然非常流畅。 + +_操作引导:_ + +1. 操作设置项,它将立即产生反馈,而不需要等待服务端响应; +2. 切换请求模式和网络状态,体验它们的区别; + +[静默提交策略文档](/tutorial/strategy/sensorless-data-interaction) + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/13-silent-submit-simple-list.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/13-silent-submit-simple-list.md index d9df67255..0f37dc692 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/13-silent-submit-simple-list.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/13-silent-submit-simple-list.md @@ -1,23 +1,22 @@ ---- -title: (vue)静默提交-简单列表 -sidebar_position: 130 ---- - -import SimpleListSilentVue from '@site/example-links/SimpleListSilentVue'; - -> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); - - - -:::info 示例说明 - -使用静默提交策略实现的简单列表,提交即响应,大幅降低网络波动造成的影响,让你的应用在网络不稳定,甚至断网状态下依然非常流畅。 - -_操作引导:_ - -1. 新增、编辑、删除列表项,它将立即产生反馈,而不需要等待服务端响应; -2. 切换请求模式和网络状态,体验它们的区别; - -[静默提交策略文档](/tutorial/strategy/sensorless-data-interaction) - -::: +--- +title: 静默提交-简单列表 +--- + +import SimpleListSilentVue from '@site/example-links/SimpleListSilentVue'; + +> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); + + + +:::info 示例说明 + +使用静默提交策略实现的简单列表,提交即响应,大幅降低网络波动造成的影响,让你的应用在网络不稳定,甚至断网状态下依然非常流畅。 + +_操作引导:_ + +1. 新增、编辑、删除列表项,它将立即产生反馈,而不需要等待服务端响应; +2. 切换请求模式和网络状态,体验它们的区别; + +[静默提交策略文档](/tutorial/strategy/sensorless-data-interaction) + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/14-silent-submit-notes.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/14-silent-submit-notes.md index ec3130abe..bb356f337 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/14-silent-submit-notes.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/14-silent-submit-notes.md @@ -1,23 +1,22 @@ ---- -title: (react)静默提交-笔记本 -sidebar_position: 140 ---- - -import NoteSilentReact from '@site/example-links/NoteSilentReact'; - -> 示例以 react 为例,但你还可以在 vue3、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); - - - -:::info 示例说明 - -使用静默提交策略实现的简单列表,提交即响应,大幅降低网络波动造成的影响,让你的应用在网络不稳定,甚至断网状态下依然非常流畅。 - -_操作引导:_ - -1. 新增、编辑、删除笔记,它将立即产生反馈,而不需要等待服务端响应; -2. 切换请求模式和网络状态,体验它们的区别; - -[静默提交策略文档](/tutorial/strategy/sensorless-data-interaction) - -::: +--- +title: 静默提交-笔记本 +--- + +import NoteSilentReact from '@site/example-links/NoteSilentReact'; + +> 示例以 react 为例,但你还可以在 vue3、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); + + + +:::info 示例说明 + +使用静默提交策略实现的简单列表,提交即响应,大幅降低网络波动造成的影响,让你的应用在网络不稳定,甚至断网状态下依然非常流畅。 + +_操作引导:_ + +1. 新增、编辑、删除笔记,它将立即产生反馈,而不需要等待服务端响应; +2. 切换请求模式和网络状态,体验它们的区别; + +[静默提交策略文档](/tutorial/strategy/sensorless-data-interaction) + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/15-form-hook.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/15-form-hook.md index 253e94e36..bc2db6d97 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/15-form-hook.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/15-form-hook.md @@ -1,18 +1,17 @@ ---- -title: (react)表单提交策略 -sidebar_position: 150 ---- - -import FormHook from '@site/example-links/FormHook'; - -> 示例以 react 为例,但你还可以在 vue3、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); - - - -:::info 示例说明 - -本示例分别提供了表单数据持久化、表单编辑、多模块表单、表单筛选数据 4 个部分,可分别尝试体验。 - -[表单提交策略文档](/tutorial/strategy/useForm) - -::: +--- +title: 表单提交策略 +--- + +import FormHook from '@site/example-links/FormHook'; + +> 示例以 react 为例,但你还可以在 vue3、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); + + + +:::info 示例说明 + +本示例分别提供了表单数据持久化、表单编辑、多模块表单、表单筛选数据 4 个部分,可分别尝试体验。 + +[表单提交策略文档](/tutorial/strategy/useForm) + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/16-captcha-send.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/16-captcha-send.md index 47e28292b..371400b7a 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/16-captcha-send.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/16-captcha-send.md @@ -1,18 +1,17 @@ ---- -title: (react)发送验证码 -sidebar_position: 160 ---- - -import CaptchaSend from '@site/example-links/CaptchaSend'; - -> 示例以 react 为例,但你还可以在 vue3、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); - - - -:::info 示例说明 - -本示例展示便捷地实现验证码发送功能。 - -[验证码发送策略文档](/tutorial/strategy/useCaptcha) - -::: +--- +title: 发送验证码 +--- + +import CaptchaSend from '@site/example-links/CaptchaSend'; + +> 示例以 react 为例,但你还可以在 vue3、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); + + + +:::info 示例说明 + +本示例展示便捷地实现验证码发送功能。 + +[验证码发送策略文档](/tutorial/strategy/useCaptcha) + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/17-retriable-hook.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/17-retriable-hook.md index 7f5145279..3db59a124 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/17-retriable-hook.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/17-retriable-hook.md @@ -1,18 +1,17 @@ ---- -title: (react)请求重试/轮询请求 -sidebar_position: 170 ---- - -import RetriableHook from '@site/example-links/RetriableHook'; - -> 示例以 react 为例,但你还可以在 vue3、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); - - - -:::info 示例说明 - -本示例分别提供了表单数据持久化、表单编辑、多模块表单、表单筛选数据 4 个部分,可分别尝试体验。 - -[请求重试策略文档](/tutorial/strategy/useRetriableRequest) - -::: +--- +title: 请求重试/轮询请求 +--- + +import RetriableHook from '@site/example-links/RetriableHook'; + +> 示例以 react 为例,但你还可以在 vue3、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); + + + +:::info 示例说明 + +本示例分别提供了表单数据持久化、表单编辑、多模块表单、表单筛选数据 4 个部分,可分别尝试体验。 + +[请求重试策略文档](/tutorial/strategy/useRetriableRequest) + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/18.action-delegation-middleware.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/18.action-delegation-middleware.md index e8226402d..cea8253f8 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/18.action-delegation-middleware.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/18.action-delegation-middleware.md @@ -1,18 +1,17 @@ ---- -title: (react)跨组件触发请求 -sidebar_position: 180 ---- - -import ActionDelegationMiddleware from '@site/example-links/ActionDelegationMiddleware'; - -> 示例以 react 为例,但你还可以在 vue3、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); - - - -:::info 示例说明 - -本示例主要通过`actionDelegationMiddleware`中间件展示,在不使用全局状态管理的情况下,跨越任意层级组件服务端数据的刷新。 - -[操作函数委托中间件文档](/tutorial/strategy/actionDelegationMiddleware) - -::: +--- +title: 跨组件触发请求 +--- + +import ActionDelegationMiddleware from '@site/example-links/ActionDelegationMiddleware'; + +> 示例以 react 为例,但你还可以在 vue3、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); + + + +:::info 示例说明 + +本示例主要通过`actionDelegationMiddleware`中间件展示,在不使用全局状态管理的情况下,跨越任意层级组件服务端数据的刷新。 + +[操作函数委托中间件文档](/tutorial/strategy/actionDelegationMiddleware) + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/19-serial-request.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/19-serial-request.md index cd05f8509..1560d89f2 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/19-serial-request.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/19-serial-request.md @@ -1,19 +1,18 @@ ---- -title: (vue)串行请求hook -sidebar_position: 190 ---- - -import SerialRequest from '@site/example-links/SerialRequest'; - -> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); - - - -:::info 示例说明 - -本示例主要通过`useSerialRequest`展示更优雅地进行串行请求。 - -[useSerialRequest](/tutorial/strategy/useSerialRequest) -[useSerialWatcher](/tutorial/strategy/useSerialWatcher) - -::: +--- +title: 串行请求hook +--- + +import SerialRequest from '@site/example-links/SerialRequest'; + +> 示例以 vue3 为例,但你还可以在 react、svelte 中使用 alova,详细请阅读 [入门指南](/tutorial/getting-started); + + + +:::info 示例说明 + +本示例主要通过`useSerialRequest`展示更优雅地进行串行请求。 + +[useSerialRequest](/tutorial/strategy/useSerialRequest) +[useSerialWatcher](/tutorial/strategy/useSerialWatcher) + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/02-quick-start.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/02-quick-start.md index 3f13c341d..cf68baa8d 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/02-quick-start.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/02-quick-start.md @@ -1,115 +1,114 @@ ---- -title: 快速开始 -sidebar_position: 20 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; -import EmbedSandpack from "@site/src/components/EmbedSandpack"; - -import quickStartGET from '!!raw-loader!@site/codesandbox/01-getting-started/02-first-request/get.js'; -import quickStartPOST from '!!raw-loader!@site/codesandbox/01-getting-started/02-first-request/post.js'; - -:::tip 示例提示 - -如果你还未了解 alova,推荐你先阅读 [alova 概述](/tutorial/getting-started)。 - -::: - -## 安装 - - - - -```bash -npm install alova --save -``` - - - - -```bash -yarn add alova -``` - - - - -```bash -pnpm add alova -``` - - - - -```bash -bun add alova -``` - - - - -> 你也可以[通过 CDN 使用 alova](/tutorial/others/use-in-static) - -## 创建 alova 实例 - -在 alova 中需要通过 alova 实例发起请求,我们先创建一个。在创建 alova 实例时需要指定请求适配器,在这里推荐使用`GlobalFetch`请求适配器, 它是基于`fetch API`的封装。 - - - - -```javascript -import { createAlova } from 'alova'; -import GlobalFetch from 'alova/GlobalFetch'; - -const alovaInstance = createAlova({ - requestAdapter: GlobalFetch() -}); -``` - - - - -```javascript -const { createAlova } = require('alova'); -const GlobalFetch = require('alova/GlobalFetch'); - -const alova = createAlova({ - requestAdapter: GlobalFetch(); -}); -``` - -> 在 nodejs 中使用 GlobalFetch 时,nodejs 版本要求`v17.5`,或者你可以使用[axios 请求适配器](/tutorial/request-adapter/alova-adapter-axios/)。 - - - - -```javascript -import { createAlova } from 'npm:alova'; -import GlobalFetch from 'npm:alova/GlobalFetch'; - -const alova = createAlova({ - requestAdapter: GlobalFetch(); -}); -``` - - - - -## GET 请求 - -通过 `alovaInstance.Get` 发送一个请求,由于使用了`GlobalFetch`请求适配器,将会接收到一个`Response`实例,这很简单。 - - - -在异步函数中,你也可以使用`await alovaInstance.Get`等待响应。 - -## POST 请求 - -通过 `alovaInstance.Post`提交数据,这同样很简单。 - - - -## 接下来要做什么? - -实际上,这只是一个最简单的请求示例,在接下来的章节中将会了解更多功能,让我们开始学习吧。 +--- +title: 快速开始 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import EmbedSandpack from "@site/src/components/EmbedSandpack"; + +import quickStartGET from '!!raw-loader!@site/codesandbox/01-getting-started/02-first-request/get.js'; +import quickStartPOST from '!!raw-loader!@site/codesandbox/01-getting-started/02-first-request/post.js'; + +:::tip 示例提示 + +如果你还未了解 alova,推荐你先阅读 [alova 概述](/tutorial/getting-started)。 + +::: + +## 安装 + + + + +```bash +npm install alova --save +``` + + + + +```bash +yarn add alova +``` + + + + +```bash +pnpm add alova +``` + + + + +```bash +bun add alova +``` + + + + +> 你也可以[通过 CDN 使用 alova](/tutorial/others/use-in-static) + +## 创建 alova 实例 + +在 alova 中需要通过 alova 实例发起请求,我们先创建一个。在创建 alova 实例时需要指定请求适配器,在这里推荐使用`GlobalFetch`请求适配器, 它是基于`fetch API`的封装。 + + + + +```javascript +import { createAlova } from 'alova'; +import GlobalFetch from 'alova/GlobalFetch'; + +const alovaInstance = createAlova({ + requestAdapter: GlobalFetch() +}); +``` + + + + +```javascript +const { createAlova } = require('alova'); +const GlobalFetch = require('alova/GlobalFetch'); + +const alova = createAlova({ + requestAdapter: GlobalFetch(); +}); +``` + +> 在 nodejs 中使用 GlobalFetch 时,nodejs 版本要求`v17.5`,或者你可以使用[axios 请求适配器](/tutorial/request-adapter/alova-adapter-axios/)。 + + + + +```javascript +import { createAlova } from 'npm:alova'; +import GlobalFetch from 'npm:alova/GlobalFetch'; + +const alova = createAlova({ + requestAdapter: GlobalFetch(); +}); +``` + + + + +## GET 请求 + +通过 `alovaInstance.Get` 发送一个请求,由于使用了`GlobalFetch`请求适配器,将会接收到一个`Response`实例,这很简单。 + + + +在异步函数中,你也可以使用`await alovaInstance.Get`等待响应。 + +## POST 请求 + +通过 `alovaInstance.Post`提交数据,这同样很简单。 + + + +## 接下来要做什么? + +实际上,这只是一个最简单的请求示例,在接下来的章节中将会了解更多功能,让我们开始学习吧。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/03-method.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/03-method.md index c189526d3..66aba5658 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/03-method.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/03-method.md @@ -1,306 +1,305 @@ ---- -title: method详解 -sidebar_position: 30 ---- - -在上一个章节中我们尝试发送了请求,获取响应数据。实际上,`alovaInstance.Get(...)`并不是一个发起请求的函数,而是创建了一个 method 实例,它是一个 PromiseLike 实例,你可以通过`then、catch、finally`方法或`await`发送请求,就像 Promise 对象一样。 - -```javascript -const userMethodInstance = alovaInstance.Get('/api/user'); - -userMethodInstance.then(response => { - // ... -}); - -userMethodInstance.catch(error => { - // ... -}); - -userMethodInstance.finally(() => { - // ... -}); - -try { - await userMethodInstance; -} catch (error) { - // ... -} finally { - // ... -} -``` - -简便写法: - -```javascript -const response = await alovaInstance.Get('/api/user'); -``` - -每个 method 实例描述了每个请求的类型、请求 url、请求头、请求参数等。此外,你还可以在 method 实例上定义请求行为,来控制 method 以什么方式处理请求。 - -## 请求类型 - -alova 共提供了 GET、POST、PUT、DELETE、HEAD、OPTIONS、PATCH 7 种请求类型。 - -| 实例创建函数 | 参数 | -| ------------ | --------------------------------------------- | -| GET | `alovaInstance.Get(url[, config])` | -| POST | `alovaInstance.Post(url[, data[, config]])` | -| PUT | `alovaInstance.Put(url[, data[, config]])` | -| DELETE | `alovaInstance.Delete(url[, data[, config]])` | -| HEAD | `alovaInstance.Head(url[, config])` | -| OPTIONS | `alovaInstance.Options(url[, config])` | -| PATCH | `alovaInstance.Patch(url[, data[, config]])` | - -参数说明: - -- `url`是请求路径; -- `data`为请求体数据; -- `config`为请求配置对象,其中包含了请求头、params 参数等、请求行为参数等配置; - -你也可以自定义创建 method 实例,这在动态指定请求类型时很有用。 - -```javascript -import { Method } from 'alova'; - -const method = new Method('GET', alovaInstance, '/api/users', { - params: { - ID: 1 - } -}); -``` - -接下来我们先来看下如何定义请求参数,你应该会觉得很熟悉。 - -## 请求参数 - -### URL 参数 - -通过 params 传入 URL 参数,params 参数会在 url 后面以?的形式拼接。 - -```javascript -alovaInstance.Get('/todo/list', { - params: { - userId: 1 - } -}); -``` - -当然,你也可以直接拼接在 url 后面,效果是相同的。 - -```javascript -alovaInstance.Get('/todo/list?userId=1'); -``` - -### 请求体 - -当发送 **POST、PUT、DELETE、PATCH 请求** 时可以通过请求体发送数据,此时第二个参数传入的是请求体,值得注意的是,POST 请求也可以传入 params 参数。 - -```javascript -alovaInstance.Post( - '/todo', - // 第二个参数是请求体 - { - title: 'test todo', - time: '12:00' - }, - // 第三个参数是配置 - { - params: { - userId: 1 - } - } -); -``` - -### 请求头 - -通过 headers 指定请求头。 - -```javascript -alovaInstance.Get('/user', { - headers: { - 'Content-Type': 'application/json;charset=UTF-8' - } -}); -``` - -### 其他请求适配器支持的参数 - -除了请求头、params 参数等外,还支持配置对应请求适配器支持的参数,当使用`GlobalFetch`作为 alova 的请求适配器时,就可以在 method 实例上配置任何`fetch API`支持的参数,这些参数会在请求时传给`fetch`函数。 - -```javascript -alovaInstance.Get('/todo/list', { - // ... - // highlight-start - credentials: 'same-origin', - referrerPolicy: 'no-referrer', - mode: 'cors' - // highlight-end -}); -``` - -以上 method 实例在通过`fetch`发送请求时,将会以以下参数请求。 - -```javascript -fetch('/todo/list', { - // ... - // highlight-start - credentials: 'same-origin', - referrerPolicy: 'no-referrer', - mode: 'cors' - // highlight-end -}); -``` - -> 请求体除了可以传递 Object,还能传递请求适配器支持的请求体参数,例如 GlobalFetch 支持传递`string | FormData | Blob | ArrayBuffer | URLSearchParams | ReadableStream`参数。 - -如果你使用了其他的请求适配器,也可以传递它们支持的参数。 - -## 请求行为 - -在[RSM](/tutorial/others/RSM)中,请求行为用于描述将以怎样的方式处理请求。 - -### 超时时间 - -设置请求超时时间。 - -```javascript -// 请求级别的请求超时时间 -alovaInstance.Get('/todo/list', { - // ... - // highlight-start - timeout: 10000 - // highlight-end -}); -``` - -### 请求共享 - -我们总会遇到这种情况,当一个请求发出但还未响应时,又发起了相同请求,就造成了请求浪费,或者重复提交问题,例如以下三种场景: - -1. 一个组件在创建时会获取初始化数据,当一个页面同时渲染多个此组件时,将会同时发出多次相同请求; -2. 提交按钮未被禁用,用户点击了多次提交按钮; -3. 当预加载还未完成时进入了预加载页面,将会发起多次相同请求; -4. 在 react 的 StrictMode 下防止重复发送请求; - -共享请求就是用来解决这些问题的,它不仅可以提升应用流畅性,还能降低服务端压力。 - -```mermaid -flowchart LR - classDef response fill:#a8bcff - R1[请求1] --> S1[发送请求] --> W1[等待响应]:::response --> RE1[接收数据1] - R2[与请求1相同的请求] --> W1[等待响应]:::response --> RE2[接收数据1] -``` - -请求共享默认开启,如果你希望在特定请求上关闭共享请求,可以这样做: - -```javascript -alovaInst.Get('/todo', { - // ... - // highlight-start - shareRequest: false - // highlight-end -}); -``` - -:::warning 如何识别相同请求 - -通过 method 实例的请求方法、请求 url、请求头、url 参数、请求体组合作为唯一标识,标识相同即表示为相同请求,而不是对比 method 实例的引用地址。 - -::: - -### 转换响应数据 - -有时候我们需要统一转换响应数据,我们可以为 method 实例设置`transformData`函数将响应数据转换成需要的结构。 - -```javascript -alovaInstance.Get('/todo/list', { - // 函数接受响应数据和响应头数据,并要求将转换后的数据返回。 - transformData(rawData, headers) { - return rawData.list.map(item => { - return { - ...item, - statusText: item.done ? '已完成' : '进行中' - }; - }); - } -}); -``` - -### 响应缓存 - -响应缓存让你可以更好地多次利用服务端数据,而不需要每次请求时都发送请求获取数据。GET 请求将默认设置 5 分钟的内存缓存时间,我们将在后面的[响应缓存](/tutorial/cache/mode)章节中详细说明。 - -## 中断请求 - -`[2.6.2+]` 调用 method 实例的`abort`中断请求。 - -```javascript -const userMethod = alovaInstance.Get('/api/user'); -userMethod.then(res => { - // ... -}); - -const handleCancel = () => { - // highlight-start - userMethod.abort(); - // highlight-end -}; -``` - -## 监听上传下载进度 - -**[v2.17.0+]** 通过 method 实例的`onUpload`绑定上传进度事件,`onDownload`绑定下载进度事件,它将返回解绑函数。 - -```javascript -const uploadMethod = alovaInstance.Post('/todo/uploadfile', formData); -const offUploadEvent = uploadMethod.onUpload(event => { - console.log('文件大小:',event.total); - console.log('已上传:',event.loaded); -}); - -uploadMethod.then(res => { - // ... -}); - -// 解绑上传回调 -const handleOffEvent = () => { - offUploadEvent(); -}; -``` - -```javascript -const downloadMethod = alovaInstance.Get('/todo/downloadfile'); -const offDownloadEvent = downloadMethod.onDownload(event => { - console.log('文件大小:',event.total); - console.log('已下载:',event.loaded); -}); - -downloadMethod.then(res => { - // ... -}); - -// 解绑下载回调 -const handleOffEvent = () => { - offDownloadEvent(); -}; -``` - -:::warning 使用`GlobalFetch`适配器需注意 - -因 fetch api 限制,alova 提供的 **GlobalFetch** 适配器不支持上传进度,如果需要上传进度,请使用[XMLHttpRequest 适配器](/tutorial/request-adapter/alova-adapter-xhr)或[axios 适配器](/tutorial/request-adapter/alova-adapter-axios)。 - -也可以自行编写请求适配器,详见 [编写请求适配器](/tutorial/custom/custom-http-adapter)。 - -::: - -**上传/下载状态类型** - -```typescript -type Progress = { - /** 上传或下载的数据总数据量 */ - total: number; - /** 已完成的数据 */ - loaded: number; -}; -``` +--- +title: method详解 +--- + +在上一个章节中我们尝试发送了请求,获取响应数据。实际上,`alovaInstance.Get(...)`并不是一个发起请求的函数,而是创建了一个 method 实例,它是一个 PromiseLike 实例,你可以通过`then、catch、finally`方法或`await`发送请求,就像 Promise 对象一样。 + +```javascript +const userMethodInstance = alovaInstance.Get('/api/user'); + +userMethodInstance.then(response => { + // ... +}); + +userMethodInstance.catch(error => { + // ... +}); + +userMethodInstance.finally(() => { + // ... +}); + +try { + await userMethodInstance; +} catch (error) { + // ... +} finally { + // ... +} +``` + +简便写法: + +```javascript +const response = await alovaInstance.Get('/api/user'); +``` + +每个 method 实例描述了每个请求的类型、请求 url、请求头、请求参数等。此外,你还可以在 method 实例上定义请求行为,来控制 method 以什么方式处理请求。 + +## 请求类型 + +alova 共提供了 GET、POST、PUT、DELETE、HEAD、OPTIONS、PATCH 7 种请求类型。 + +| 实例创建函数 | 参数 | +| ------------ | --------------------------------------------- | +| GET | `alovaInstance.Get(url[, config])` | +| POST | `alovaInstance.Post(url[, data[, config]])` | +| PUT | `alovaInstance.Put(url[, data[, config]])` | +| DELETE | `alovaInstance.Delete(url[, data[, config]])` | +| HEAD | `alovaInstance.Head(url[, config])` | +| OPTIONS | `alovaInstance.Options(url[, config])` | +| PATCH | `alovaInstance.Patch(url[, data[, config]])` | + +参数说明: + +- `url`是请求路径; +- `data`为请求体数据; +- `config`为请求配置对象,其中包含了请求头、params 参数等、请求行为参数等配置; + +你也可以自定义创建 method 实例,这在动态指定请求类型时很有用。 + +```javascript +import { Method } from 'alova'; + +const method = new Method('GET', alovaInstance, '/api/users', { + params: { + ID: 1 + } +}); +``` + +接下来我们先来看下如何定义请求参数,你应该会觉得很熟悉。 + +## 请求参数 + +### URL 参数 + +通过 params 传入 URL 参数,params 参数会在 url 后面以?的形式拼接。 + +```javascript +alovaInstance.Get('/todo/list', { + params: { + userId: 1 + } +}); +``` + +当然,你也可以直接拼接在 url 后面,效果是相同的。 + +```javascript +alovaInstance.Get('/todo/list?userId=1'); +``` + +### 请求体 + +当发送 **POST、PUT、DELETE、PATCH 请求** 时可以通过请求体发送数据,此时第二个参数传入的是请求体,值得注意的是,POST 请求也可以传入 params 参数。 + +```javascript +alovaInstance.Post( + '/todo', + // 第二个参数是请求体 + { + title: 'test todo', + time: '12:00' + }, + // 第三个参数是配置 + { + params: { + userId: 1 + } + } +); +``` + +### 请求头 + +通过 headers 指定请求头。 + +```javascript +alovaInstance.Get('/user', { + headers: { + 'Content-Type': 'application/json;charset=UTF-8' + } +}); +``` + +### 其他请求适配器支持的参数 + +除了请求头、params 参数等外,还支持配置对应请求适配器支持的参数,当使用`GlobalFetch`作为 alova 的请求适配器时,就可以在 method 实例上配置任何`fetch API`支持的参数,这些参数会在请求时传给`fetch`函数。 + +```javascript +alovaInstance.Get('/todo/list', { + // ... + // highlight-start + credentials: 'same-origin', + referrerPolicy: 'no-referrer', + mode: 'cors' + // highlight-end +}); +``` + +以上 method 实例在通过`fetch`发送请求时,将会以以下参数请求。 + +```javascript +fetch('/todo/list', { + // ... + // highlight-start + credentials: 'same-origin', + referrerPolicy: 'no-referrer', + mode: 'cors' + // highlight-end +}); +``` + +> 请求体除了可以传递 Object,还能传递请求适配器支持的请求体参数,例如 GlobalFetch 支持传递`string | FormData | Blob | ArrayBuffer | URLSearchParams | ReadableStream`参数。 + +如果你使用了其他的请求适配器,也可以传递它们支持的参数。 + +## 请求行为 + +在[RSM](/tutorial/others/RSM)中,请求行为用于描述将以怎样的方式处理请求。 + +### 超时时间 + +设置请求超时时间。 + +```javascript +// 请求级别的请求超时时间 +alovaInstance.Get('/todo/list', { + // ... + // highlight-start + timeout: 10000 + // highlight-end +}); +``` + +### 请求共享 + +我们总会遇到这种情况,当一个请求发出但还未响应时,又发起了相同请求,就造成了请求浪费,或者重复提交问题,例如以下三种场景: + +1. 一个组件在创建时会获取初始化数据,当一个页面同时渲染多个此组件时,将会同时发出多次相同请求; +2. 提交按钮未被禁用,用户点击了多次提交按钮; +3. 当预加载还未完成时进入了预加载页面,将会发起多次相同请求; +4. 在 react 的 StrictMode 下防止重复发送请求; + +共享请求就是用来解决这些问题的,它不仅可以提升应用流畅性,还能降低服务端压力。 + +```mermaid +flowchart LR + classDef response fill:#a8bcff + R1[请求1] --> S1[发送请求] --> W1[等待响应]:::response --> RE1[接收数据1] + R2[与请求1相同的请求] --> W1[等待响应]:::response --> RE2[接收数据1] +``` + +请求共享默认开启,如果你希望在特定请求上关闭共享请求,可以这样做: + +```javascript +alovaInst.Get('/todo', { + // ... + // highlight-start + shareRequest: false + // highlight-end +}); +``` + +:::warning 如何识别相同请求 + +通过 method 实例的请求方法、请求 url、请求头、url 参数、请求体组合作为唯一标识,标识相同即表示为相同请求,而不是对比 method 实例的引用地址。 + +::: + +### 转换响应数据 + +有时候我们需要统一转换响应数据,我们可以为 method 实例设置`transformData`函数将响应数据转换成需要的结构。 + +```javascript +alovaInstance.Get('/todo/list', { + // 函数接受响应数据和响应头数据,并要求将转换后的数据返回。 + transformData(rawData, headers) { + return rawData.list.map(item => { + return { + ...item, + statusText: item.done ? '已完成' : '进行中' + }; + }); + } +}); +``` + +### 响应缓存 + +响应缓存让你可以更好地多次利用服务端数据,而不需要每次请求时都发送请求获取数据。GET 请求将默认设置 5 分钟的内存缓存时间,我们将在后面的[响应缓存](/tutorial/cache/mode)章节中详细说明。 + +## 中断请求 + +`[2.6.2+]` 调用 method 实例的`abort`中断请求。 + +```javascript +const userMethod = alovaInstance.Get('/api/user'); +userMethod.then(res => { + // ... +}); + +const handleCancel = () => { + // highlight-start + userMethod.abort(); + // highlight-end +}; +``` + +## 监听上传下载进度 + +**[v2.17.0+]** 通过 method 实例的`onUpload`绑定上传进度事件,`onDownload`绑定下载进度事件,它将返回解绑函数。 + +```javascript +const uploadMethod = alovaInstance.Post('/todo/uploadfile', formData); +const offUploadEvent = uploadMethod.onUpload(event => { + console.log('文件大小:',event.total); + console.log('已上传:',event.loaded); +}); + +uploadMethod.then(res => { + // ... +}); + +// 解绑上传回调 +const handleOffEvent = () => { + offUploadEvent(); +}; +``` + +```javascript +const downloadMethod = alovaInstance.Get('/todo/downloadfile'); +const offDownloadEvent = downloadMethod.onDownload(event => { + console.log('文件大小:',event.total); + console.log('已下载:',event.loaded); +}); + +downloadMethod.then(res => { + // ... +}); + +// 解绑下载回调 +const handleOffEvent = () => { + offDownloadEvent(); +}; +``` + +:::warning 使用`GlobalFetch`适配器需注意 + +因 fetch api 限制,alova 提供的 **GlobalFetch** 适配器不支持上传进度,如果需要上传进度,请使用[XMLHttpRequest 适配器](/tutorial/request-adapter/alova-adapter-xhr)或[axios 适配器](/tutorial/request-adapter/alova-adapter-axios)。 + +也可以自行编写请求适配器,详见 [编写请求适配器](/tutorial/custom/custom-http-adapter)。 + +::: + +**上传/下载状态类型** + +```typescript +type Progress = { + /** 上传或下载的数据总数据量 */ + total: number; + /** 已完成的数据 */ + loaded: number; +}; +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/04-alova.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/04-alova.md index ae5e8c4ae..4d0f89837 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/04-alova.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/04-alova.md @@ -1,67 +1,66 @@ ---- -title: alova详解 -sidebar_position: 40 ---- - -alova 实例不但可以创建不同类型的 method 实例,还可以设置全局参数,创建的 method 实例都会继承这个 alova 实例的参数。当 alova 实例的参数与 method 实例设置了相同的参数时,例如 `timeout`、`shareRequest`等,将优先使用 method 实例的参数。 - -接下来我们看下 alova 的全局参数。 - -## baseURL - -设置 baseURL 后,你可以不再需要为每个请求都添加相同的 url 前缀。 - -```javascript -const alovaInstance = createAlova({ - baseURL: 'https://api.alovajs.dev' - // ... -}); -``` - -此时,创建 method 实例时只需要指定 pathname 即可。 - -```javascript -alovaInstance.Get('/todo/list'); -``` - -## 全局的超时时间 - -以下为设置全局的请求超时时间。 - -```javascript -// 全局设置请求超时时间 -const alovaInstance = createAlova({ - // ... - // highlight-start - // 请求超时时间,单位为毫秒,默认为0,表示永不超时 - timeout: 50000 - // highlight-end -}); -``` - -## 全局的共享请求 - -在全局设置共享请求。 - -```javascript -const alovaInstance = createAlova({ - // ... - // highlight-start - shareRequest: false - // highlight-end -}); -``` - -## 请求适配器 - -在之前的章节中我们已经配置了`GlobalFetch`请求适配器,由这个 alova 实例发起的请求都将使用它发送请求。实际上,我们针对不同的 JS 环境,还提供了各种请求适配器。 - -- [模拟请求适配器](/tutorial/request-adapter/alova-mock) -- [XMLHttpRequest 适配器](/tutorial/request-adapter/alova-adapter-xhr) -- [axios 适配器](/tutorial/request-adapter/alova-adapter-axios) -- [uniapp 适配器](/tutorial/request-adapter/alova-adapter-uniapp) -- [taro 适配器](/tutorial/request-adapter/alova-adapter-taro) - -## 全局的响应缓存 - -你还可以全局设置响应缓存,我们将在后面的[响应缓存](/tutorial/cache/mode)章节中详细说明。 +--- +title: alova详解 +--- + +alova 实例不但可以创建不同类型的 method 实例,还可以设置全局参数,创建的 method 实例都会继承这个 alova 实例的参数。当 alova 实例的参数与 method 实例设置了相同的参数时,例如 `timeout`、`shareRequest`等,将优先使用 method 实例的参数。 + +接下来我们看下 alova 的全局参数。 + +## baseURL + +设置 baseURL 后,你可以不再需要为每个请求都添加相同的 url 前缀。 + +```javascript +const alovaInstance = createAlova({ + baseURL: 'https://api.alovajs.dev' + // ... +}); +``` + +此时,创建 method 实例时只需要指定 pathname 即可。 + +```javascript +alovaInstance.Get('/todo/list'); +``` + +## 全局的超时时间 + +以下为设置全局的请求超时时间。 + +```javascript +// 全局设置请求超时时间 +const alovaInstance = createAlova({ + // ... + // highlight-start + // 请求超时时间,单位为毫秒,默认为0,表示永不超时 + timeout: 50000 + // highlight-end +}); +``` + +## 全局的共享请求 + +在全局设置共享请求。 + +```javascript +const alovaInstance = createAlova({ + // ... + // highlight-start + shareRequest: false + // highlight-end +}); +``` + +## 请求适配器 + +在之前的章节中我们已经配置了`GlobalFetch`请求适配器,由这个 alova 实例发起的请求都将使用它发送请求。实际上,我们针对不同的 JS 环境,还提供了各种请求适配器。 + +- [模拟请求适配器](/tutorial/request-adapter/alova-mock) +- [XMLHttpRequest 适配器](/tutorial/request-adapter/alova-adapter-xhr) +- [axios 适配器](/tutorial/request-adapter/alova-adapter-axios) +- [uniapp 适配器](/tutorial/request-adapter/alova-adapter-uniapp) +- [taro 适配器](/tutorial/request-adapter/alova-adapter-taro) + +## 全局的响应缓存 + +你还可以全局设置响应缓存,我们将在后面的[响应缓存](/tutorial/cache/mode)章节中详细说明。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/05-global-interceptor.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/05-global-interceptor.md index 72a617f58..430476482 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/05-global-interceptor.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/05-global-interceptor.md @@ -1,137 +1,136 @@ ---- -title: 全局拦截器 -sidebar_position: 50 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -## 全局的请求拦截器 - -通常,我们需要让所有请求都用上相同的配置,例如添加 token、timestamp 到请求头,此时我们可以设置一个全局的请求拦截器,它将在所有请求前被触发,我们可以在此拦截器中统一设置请求参数。 - -```mermaid -flowchart LR - R1[请求1] --> beforeRequest - R2[请求2] --> beforeRequest - R3[请求3] --> beforeRequest - RN[请求N] --> beforeRequest - beforeRequest --> S1[发送请求] -``` - -```javascript -const alovaInstance = createAlova({ - // ... - // 函数参数为一个method实例,包含如url、params、data、headers等请求数据 - // 你可以自由修改这些数据 - // highlight-start - beforeRequest(method) { - // 假设我们需要添加token到请求头 - method.config.headers.token = 'token'; - } - // highlight-end -}); -``` - -你也可以将 beforeRequest 设置为异步函数。 - -```javascript -const alovaInstance = createAlova({ - // ... - // highlight-start - async beforeRequest(method) { - // 执行一些异步任务 - // ... - } - // highlight-end -}); -``` - -## 全局的响应拦截器 - -当我们希望统一解析响应数据、统一处理错误,以及统一处理请求完成时,此时可以在创建 alova 实例时指定全局的响应拦截器,响应拦截器包括请求成功的拦截器、请求失败的拦截器,和请求完成的拦截器。 - -```mermaid -flowchart LR - classDef error fill:#f96,stroke:#f00,stroke-width:2px; - - R1[请求1成功] --> responded.onSuccess - R2[请求2成功] --> responded.onSuccess - RN[请求N成功] --> responded.onSuccess - R4[请求4失败]:::error --> responded.onError:::error - R5[请求M失败]:::error --> responded.onError:::error - responded.onSuccess --> responded.onComplete - responded.onError --> responded.onComplete -``` - -```javascript -const alovaInstance = createAlova({ - // ... - // 使用数组的两个项,分别指定请求成功的拦截器和请求失败的拦截器 - responded: { - // highlight-start - // 请求成功的拦截器 - // 当使用GlobalFetch请求适配器时,第一个参数接收Response对象 - // 第二个参数为当前请求的method实例,你可以用它同步请求前后的配置信息 - onSuccess: async (response, method) => { - if (response.status >= 400) { - throw new Error(response.statusText); - } - const json = await response.json(); - if (json.code !== 200) { - // 抛出错误或返回reject状态的Promise实例时,此请求将抛出错误 - throw new Error(json.message); - } - - // 解析的响应数据将传给method实例的transformData钩子函数,这些函数将在后续讲解 - return json.data; - }, - // highlight-end - - // highlight-start - // 请求失败的拦截器 - // 请求错误时将会进入该拦截器。 - // 第二个参数为当前请求的method实例,你可以用它同步请求前后的配置信息 - onError: (err, method) => { - alert(error.message); - }, - // highlight-end - - // highlight-start - // 请求完成的拦截器 - // 当你需要在请求不论是成功、失败、还是命中缓存都需要执行的逻辑时,可以在创建alova实例时指定全局的`onComplete`拦截器,例如关闭请求 loading 状态。 - // 接收当前请求的method实例 - onComplete: async method => { - // 处理请求完成逻辑 - } - // highlight-end - } -}); -``` - -如果不需要设置请求失败或完成的拦截器,可以直接传入请求成功的拦截器函数,而不再需要通过对象来设置回调。 - -```javascript -const alovaInstance = createAlova({ - // ... - // highlight-start - async responded(response, method) { - // 请求成功的拦截器 - } - // highlight-end -}); -``` - -:::info 拦截器触发说明 - -当你使用`GlobalFetch`请求适配器时,由于`window.fetch`的特点,只有在连接超时或连接中断时才会触发`onError`拦截器,其他情况均会触发`onSuccess`拦截器,[详情请查看这边](https://developer.mozilla.org/docs/Web/API/fetch) - -::: - -:::warning 特别注意 - -1. `onSuccess`、`onError`和`onComplete`均可以设为同步函数和异步函数。 -2. `onError` 回调是请求错误的捕获函数,`onSuccess` 中抛出错误不会触发 `onError`。当捕获错误但没有抛出错误或返回 reject 状态的 Promise 实例,将认为请求是成功的,且不会获得响应数据。 -3. 在 2.0.x 及以前的版本中将`responded`错误地拼写为了`responsed`,在 2.1.0 中已将两者做了兼容处理,建议在后续版本中使用`responded`代替`responsed`。 - -::: +--- +title: 全局拦截器 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## 全局的请求拦截器 + +通常,我们需要让所有请求都用上相同的配置,例如添加 token、timestamp 到请求头,此时我们可以设置一个全局的请求拦截器,它将在所有请求前被触发,我们可以在此拦截器中统一设置请求参数。 + +```mermaid +flowchart LR + R1[请求1] --> beforeRequest + R2[请求2] --> beforeRequest + R3[请求3] --> beforeRequest + RN[请求N] --> beforeRequest + beforeRequest --> S1[发送请求] +``` + +```javascript +const alovaInstance = createAlova({ + // ... + // 函数参数为一个method实例,包含如url、params、data、headers等请求数据 + // 你可以自由修改这些数据 + // highlight-start + beforeRequest(method) { + // 假设我们需要添加token到请求头 + method.config.headers.token = 'token'; + } + // highlight-end +}); +``` + +你也可以将 beforeRequest 设置为异步函数。 + +```javascript +const alovaInstance = createAlova({ + // ... + // highlight-start + async beforeRequest(method) { + // 执行一些异步任务 + // ... + } + // highlight-end +}); +``` + +## 全局的响应拦截器 + +当我们希望统一解析响应数据、统一处理错误,以及统一处理请求完成时,此时可以在创建 alova 实例时指定全局的响应拦截器,响应拦截器包括请求成功的拦截器、请求失败的拦截器,和请求完成的拦截器。 + +```mermaid +flowchart LR + classDef error fill:#f96,stroke:#f00,stroke-width:2px; + + R1[请求1成功] --> responded.onSuccess + R2[请求2成功] --> responded.onSuccess + RN[请求N成功] --> responded.onSuccess + R4[请求4失败]:::error --> responded.onError:::error + R5[请求M失败]:::error --> responded.onError:::error + responded.onSuccess --> responded.onComplete + responded.onError --> responded.onComplete +``` + +```javascript +const alovaInstance = createAlova({ + // ... + // 使用数组的两个项,分别指定请求成功的拦截器和请求失败的拦截器 + responded: { + // highlight-start + // 请求成功的拦截器 + // 当使用GlobalFetch请求适配器时,第一个参数接收Response对象 + // 第二个参数为当前请求的method实例,你可以用它同步请求前后的配置信息 + onSuccess: async (response, method) => { + if (response.status >= 400) { + throw new Error(response.statusText); + } + const json = await response.json(); + if (json.code !== 200) { + // 抛出错误或返回reject状态的Promise实例时,此请求将抛出错误 + throw new Error(json.message); + } + + // 解析的响应数据将传给method实例的transformData钩子函数,这些函数将在后续讲解 + return json.data; + }, + // highlight-end + + // highlight-start + // 请求失败的拦截器 + // 请求错误时将会进入该拦截器。 + // 第二个参数为当前请求的method实例,你可以用它同步请求前后的配置信息 + onError: (err, method) => { + alert(error.message); + }, + // highlight-end + + // highlight-start + // 请求完成的拦截器 + // 当你需要在请求不论是成功、失败、还是命中缓存都需要执行的逻辑时,可以在创建alova实例时指定全局的`onComplete`拦截器,例如关闭请求 loading 状态。 + // 接收当前请求的method实例 + onComplete: async method => { + // 处理请求完成逻辑 + } + // highlight-end + } +}); +``` + +如果不需要设置请求失败或完成的拦截器,可以直接传入请求成功的拦截器函数,而不再需要通过对象来设置回调。 + +```javascript +const alovaInstance = createAlova({ + // ... + // highlight-start + async responded(response, method) { + // 请求成功的拦截器 + } + // highlight-end +}); +``` + +:::info 拦截器触发说明 + +当你使用`GlobalFetch`请求适配器时,由于`window.fetch`的特点,只有在连接超时或连接中断时才会触发`onError`拦截器,其他情况均会触发`onSuccess`拦截器,[详情请查看这边](https://developer.mozilla.org/docs/Web/API/fetch) + +::: + +:::warning 特别注意 + +1. `onSuccess`、`onError`和`onComplete`均可以设为同步函数和异步函数。 +2. `onError` 回调是请求错误的捕获函数,`onSuccess` 中抛出错误不会触发 `onError`。当捕获错误但没有抛出错误或返回 reject 状态的 Promise 实例,将认为请求是成功的,且不会获得响应数据。 +3. 在 2.0.x 及以前的版本中将`responded`错误地拼写为了`responsed`,在 2.1.0 中已将两者做了兼容处理,建议在后续版本中使用`responded`代替`responsed`。 + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/06-method-metadata.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/06-method-metadata.md index a493ee4aa..e03991c04 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/06-method-metadata.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/06-method-metadata.md @@ -1,157 +1,156 @@ ---- -title: method元数据 -sidebar_position: 60 ---- - -:::info 版本要求 - -v2.7.0+ - -::: - -method 实例是贯穿 alova 的整个请求生命周期的,并且,在项目中会大量存在不同的 method 实例,有时候我们需要对特定的 method 实例添加附加信息,以便于对它们进行身份标识或额外的信息传递等,此时,我们就需要使用 method 元数据。 - -## 使用元数据标识身份 - -### 在请求前使用身份标识 - -例如,你的项目中大部分接口在每次请求时需附带`token`,但还存在一些接口无需验证的,可能你会在全局的`beforeRequest`函数中统一处理它们。 - -```javascript -const nonvalidateRequiredApi = [ - '/api/url1', - '/api/url2', - '/api/url3' - // ... -]; - -createAlova({ - beforeRequest(method) { - if (!nonvalidateRequiredApi.includes(method.url)) { - method.config.headers.token = '...'; - } - } -}); -``` - -这将导致以下两个问题: - -1. 信息没有与 method 实例聚合,可维护性更差; -2. 编码更麻烦; - -为解决这两个问题,我们将使用元数据的方式,在创建特定的 method 实例时对它进行标识。 - -**第一步:在创建 method 实例时定义元数据** - -```javascript -const loginAPI = (username, password) => { - const methodInstance = alovaInst.Post('/login', { - username, - password - }); - methodInstance.meta = { - ignoreToken: true - }; - return methodInstance; -}; -``` - -**[2.18.0+]** 你也可以直接在 config 中传入 meta 数据 - -```javascript -const loginAPI = (username, password) => { - return alovaInst.Post( - '/login', - { - username, - password - }, - { - meta: { - ignoreToken: true - } - } - ); -}; -``` - -**第二步:在`beforeRequest`中通过元数据作为判断依据** - -```javascript -createAlova({ - // ... - beforeRequest(method) { - if (!method.meta?.ignoreToken) { - method.config.headers.token = '...'; - } - } -}); -``` - -### 在响应后使用标识身份 - -这种方式还可以用于在全局的`responded`中,例如,在绝大部分情况下,请求 api 都将返回 json 数据,但可能存在文件下载接口,它将返回二进制数据流,在这种情况下,你可以在`responded`中使用不同的元数据分别处理不同的响应。 - -**第一步:创建 method 实例时同样需要分配一个元数据** - -```javascript -const downloadAPI = filePath => { - const methodInstance = alovaInst.Post('/download_file', { - filePath - }); - methodInstance.meta = { - isDownload: true - }; - return methodInstance; -}; -``` - -**第二步:在`responded`中通过元数据作为判断依据** - -```javascript -createAlova({ - // ... - responded: - onSuccess: (response, method) => method.meta?.isDownload ? response.blob() : response.json() - onError: (error, method) => { - // 在响应错误时也可以访问method实例的元数据 - } - } -}); -``` - -## 使用元数据传递信息 - -在某种情况下,如果你希望为不同的 method 实例添加附加信息,以便在其他地方使用,也可以使用元数据进行保存。以统一生成不同的 method 实例 id 为例。 - -```javascript -createAlova({ - beforeRequest(method) { - if (!method.meta.generateId) { - method.meta.uid = generateUUID(); - } - }, - - responded: { - onSuccess(response, method) { - // 在请求成功中访问当前method生成的meta数据 - const currentMethodUID = method.meta.uid; - }, - onError(error, method) { - // 在请求失败中访问当前method生成的meta数据 - const currentMethodUID = method.meta.uid; - } - } -}); -``` - -## 非 typescript 项目提示 - -在非 typescript 环境下,你可以使用任意一个属性作为信息载体,而不局限于`meta`属性。 - -```javascript -methodInstance.showResponseMsg = true; -methodInstance.others = 'abc'; -``` - -只是在 typescript 环境下,任意的属性名将会报`不存在属性“$0”。ts(2339)`,因此在类型中我们指定了`meta`属性作为信息载体。 +--- +title: method元数据 +--- + +:::info 版本要求 + +v2.7.0+ + +::: + +method 实例是贯穿 alova 的整个请求生命周期的,并且,在项目中会大量存在不同的 method 实例,有时候我们需要对特定的 method 实例添加附加信息,以便于对它们进行身份标识或额外的信息传递等,此时,我们就需要使用 method 元数据。 + +## 使用元数据标识身份 + +### 在请求前使用身份标识 + +例如,你的项目中大部分接口在每次请求时需附带`token`,但还存在一些接口无需验证的,可能你会在全局的`beforeRequest`函数中统一处理它们。 + +```javascript +const nonvalidateRequiredApi = [ + '/api/url1', + '/api/url2', + '/api/url3' + // ... +]; + +createAlova({ + beforeRequest(method) { + if (!nonvalidateRequiredApi.includes(method.url)) { + method.config.headers.token = '...'; + } + } +}); +``` + +这将导致以下两个问题: + +1. 信息没有与 method 实例聚合,可维护性更差; +2. 编码更麻烦; + +为解决这两个问题,我们将使用元数据的方式,在创建特定的 method 实例时对它进行标识。 + +**第一步:在创建 method 实例时定义元数据** + +```javascript +const loginAPI = (username, password) => { + const methodInstance = alovaInst.Post('/login', { + username, + password + }); + methodInstance.meta = { + ignoreToken: true + }; + return methodInstance; +}; +``` + +**[2.18.0+]** 你也可以直接在 config 中传入 meta 数据 + +```javascript +const loginAPI = (username, password) => { + return alovaInst.Post( + '/login', + { + username, + password + }, + { + meta: { + ignoreToken: true + } + } + ); +}; +``` + +**第二步:在`beforeRequest`中通过元数据作为判断依据** + +```javascript +createAlova({ + // ... + beforeRequest(method) { + if (!method.meta?.ignoreToken) { + method.config.headers.token = '...'; + } + } +}); +``` + +### 在响应后使用标识身份 + +这种方式还可以用于在全局的`responded`中,例如,在绝大部分情况下,请求 api 都将返回 json 数据,但可能存在文件下载接口,它将返回二进制数据流,在这种情况下,你可以在`responded`中使用不同的元数据分别处理不同的响应。 + +**第一步:创建 method 实例时同样需要分配一个元数据** + +```javascript +const downloadAPI = filePath => { + const methodInstance = alovaInst.Post('/download_file', { + filePath + }); + methodInstance.meta = { + isDownload: true + }; + return methodInstance; +}; +``` + +**第二步:在`responded`中通过元数据作为判断依据** + +```javascript +createAlova({ + // ... + responded: + onSuccess: (response, method) => method.meta?.isDownload ? response.blob() : response.json() + onError: (error, method) => { + // 在响应错误时也可以访问method实例的元数据 + } + } +}); +``` + +## 使用元数据传递信息 + +在某种情况下,如果你希望为不同的 method 实例添加附加信息,以便在其他地方使用,也可以使用元数据进行保存。以统一生成不同的 method 实例 id 为例。 + +```javascript +createAlova({ + beforeRequest(method) { + if (!method.meta.generateId) { + method.meta.uid = generateUUID(); + } + }, + + responded: { + onSuccess(response, method) { + // 在请求成功中访问当前method生成的meta数据 + const currentMethodUID = method.meta.uid; + }, + onError(error, method) { + // 在请求失败中访问当前method生成的meta数据 + const currentMethodUID = method.meta.uid; + } + } +}); +``` + +## 非 typescript 项目提示 + +在非 typescript 环境下,你可以使用任意一个属性作为信息载体,而不局限于`meta`属性。 + +```javascript +methodInstance.showResponseMsg = true; +methodInstance.others = 'abc'; +``` + +只是在 typescript 环境下,任意的属性名将会报`不存在属性“$0”。ts(2339)`,因此在类型中我们指定了`meta`属性作为信息载体。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/02-use-request.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/02-use-request.md index 7483c09f9..1eed709b4 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/02-use-request.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/02-use-request.md @@ -1,6 +1,5 @@ --- title: 自动管理请求状态 -sidebar_position: 20 --- import Tabs from '@theme/Tabs'; diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/03-use-watcher.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/03-use-watcher.md index d366fed37..dbeea48ec 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/03-use-watcher.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/03-use-watcher.md @@ -1,324 +1,331 @@ ---- -title: 监听请求 -sidebar_position: 30 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; -import EmbedSandpack from "@site/src/components/EmbedSandpack"; -import CodeBlock from '@theme/CodeBlock'; -import useWatcherSearchVue from '!!raw-loader!@site/codesandbox/03-learning/04-use-watcher/vueComposition-search.zh.vue'; -import useWatcherSearchReact from '!!raw-loader!@site/codesandbox/03-learning/04-use-watcher/react-search.zh.jsx'; -import useWatcherSearchSvelte from '!!raw-loader!@site/codesandbox/03-learning/04-use-watcher/svelte-search.zh.svelte'; -import useWatcherSearchVueOptions from '!!raw-loader!@site/codesandbox/03-learning/04-use-watcher/vueOptions-search.zh.vue'; - -在一些需要随数据变化而重新请求的场景下,如分页、数据筛选、模糊搜索,可以使用`useWatcher` 来监听指定的状态变化时立即发送请求。 - -:::info 提醒 - -在使用 useWatcher 前,请确保已[设置 statesHook](/tutorial/combine-framework)。 - -::: - -## 关键字搜索 - -接下来我们以搜索 todo 项为例,尝试改变选择框中的选项,看看 todo 列表是如何变化的。 - - - - - - - - - - - - - -{useWatcherSearchSvelte} - - - - - - - - - -## 分页 - -以 todo 列表分页请求为例,你可以这样做。 - - - - -```html - - - -``` - - - - -```jsx -import { useState } from 'react'; - -// method实例创建函数 -const getTodoList = currentPage => { - return alovaInstance.Get('/todo/list', { - params: { - currentPage, - pageSize: 10 - } - }); -}; - -const App = () => { - const [currentPage, setCurrentPage] = useState(1); - const { - loading, - data, - error - - // 第一个参数为返回method实例的函数,而非method实例本身 - } = useWatcher( - () => getTodoList(currentPage), - // 被监听的状态数组,这些状态变化将会触发一次请求 - [currentPage], - { - // ⚠️调用useWatcher默认不触发,注意和useRequest的区别 - // 手动设置immediate为true可以初始获取第1页数据 - immediate: true - } - ); - - return { - /* ... */ - }; -}; -``` - - - - -```html - - - -``` - - - - -```html - - - -``` - - - - -## 立即发送请求 - -与`useRequest`不同的是,`useWatcher`的`immediate`属性默认是`false`。 - -```javascript -const { send } = useWatcher(() => getTodoList(currentPage), [currentPage], { - // highlight-start - immediate: true - // highlight-end -}); -send(); -``` - -## 请求防抖 - -通常我们都会在频繁触发的事件层面编写防抖代码,这次我们在请求层面实现了防抖功能,这意味着你再也不用在模糊搜索功能中自己实现防抖了,用法也非常简单。 - -:::info 什么是防抖 - -防抖(debounce),就是指触发事件后,在 n 秒内函数只能执行一次,如果触发事件后在 n 秒内又触发了事件,则会重新计算函数延执行时间(在这里和节流区分一下,节流是在触发完事件之后的一段时间之内不能再次触发事件) - -::: - -### 设置所有监听状态的防抖时间 - -```javascript -const { loading, data, error } = useWatcher(() => filterTodoList(keyword, date), [keyword, date], { - // highlight-start - // 设置debounce为数字时表示为所有监听状态的防抖时间,单位为毫秒 - // 如这边表示当状态keyword、date的一个或多个变化时,将在500ms后才发送请求 - debounce: 500 - // highlight-end -}); -``` - -### 为单个监听状态设置防抖时间 - -很多场景下,我们只需要对某几个频繁变化的监听状态进行防抖,如文本框的`onInput`触发的状态变化,可以这样做: - -```javascript -const { loading, data, error } = useWatcher(() => filterTodoList(keyword, date), [keyword, date], { - // highlight-start - // 以监听状态的数组顺序分别设置防抖时间,0或不传表示不防抖 - // 这边监听状态的顺序是[keyword, date],防抖数组设置的是[500, 0],表示只对keyword单独设置防抖 - debounce: [500, 0] - // 也可以这么按如下设置: - // debounce: [500], - // highlight-end -}); -``` - -## 状态改变时阻止请求 - -有时候你希望在监听的状态改变时不发送请求,你可以通过 Hook 配置中的 sendable 属性来控制监听的状态改变时是否发送请求,sendable 属性为一个函数,它的参数为`AlovaEvent`事件对象,包含`send`函数传入的参数所组成的数组`sendArgs`,以及当前请求的 method 实例,并且该函数返回一个`truthy/falsy`值来判断本次状态改变时是否需要触发请求(默认为`true`),**抛出错误也表示不触发请求**。 - -```javascript -useWatcher( - () => getTodoList($currentPage), - // 被监听的状态数组,这些状态变化将会触发一次请求 - [state], - { - // highlight-start - sendable: ({ sendArgs, method }) => { - // do something - // 仅当 state 为 1 时发送请求 - return state === 1; - } - // highlight-end - } -); -``` - -## 请求时序 - -有时候当`useWatcher`监听的状态发生连续的改变导致连续的请求的发起时,后一次的请求先于前一次的请求获得响应,但是当前一次请求获得响应时,会覆盖后一次请求的响应,导致获取到与状态不匹配的响应;例如说有个状态`state`改变后发出了请求`1`,然后在请求`1`还未响应时又改变了`state`值,并发出了请求`2`,如果请求`1`后于请求`2`返回,最终的响应数据会维持在请求`1`。 -所以我们设计了`abortLast`参数,它用于标记当下一次请求发出时,是否中断上一次的未响应请求,默认为`true`,这样`useWatcher`所发出的请求只有最后一次有效。 - -```mermaid -sequenceDiagram - participant U as 用户 - participant S as 服务器 - U ->> U: 监听state状态 - U ->> S: state改变发起请求1 - U ->> S: state改变发起请求2 - S ->> U: 请求2先响应 - S ->> U: 请求1后响应 - U ->> U: 请求2的响应被覆盖 -``` - -```javascript -useWatcher( - () => getTodoList($currentPage), - // 被监听的状态数组,这些状态变化将会触发一次请求 - [state], - { - // highlight-start - abortLast: true // 是否中断上一次的未响应请求,默认为true - // highlight-end - } -); -``` - -:::warning 注意事项 - -`abortLast`默认为`true`,在正常情况下你不需要关注这个参数,如果修改为`false`,可能会导致状态与响应不匹配的问题。 - -::: - -## API - -完整的 API 文档请查看[核心 useHooks](/api/core-hooks#usewatcher)。 +--- +title: 监听请求 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import EmbedSandpack from "@site/src/components/EmbedSandpack"; +import CodeBlock from '@theme/CodeBlock'; +import useWatcherSearchVue from '!!raw-loader!@site/codesandbox/03-learning/04-use-watcher/vueComposition-search.zh.vue'; +import useWatcherSearchReact from '!!raw-loader!@site/codesandbox/03-learning/04-use-watcher/react-search.zh.jsx'; +import useWatcherSearchSvelte from '!!raw-loader!@site/codesandbox/03-learning/04-use-watcher/svelte-search.zh.svelte'; +import useWatcherSearchVueOptions from '!!raw-loader!@site/codesandbox/03-learning/04-use-watcher/vueOptions-search.zh.vue'; + +在一些需要随数据变化而重新请求的场景下,如分页、数据筛选、模糊搜索,可以使用`useWatcher` 来监听指定的状态变化时立即发送请求。 + +:::info 提醒 + +在使用 useWatcher 前,请确保已[设置 statesHook](/tutorial/combine-framework)。 + +::: + +## 关键字搜索 + +接下来我们以搜索 todo 项为例,尝试改变选择框中的选项,看看 todo 列表是如何变化的。 + + + + + + + + + + + + + +{useWatcherSearchSvelte} + + + + + + + + + +## 分页 + +以 todo 列表分页请求为例,你可以这样做。 + + + + +```html + + + +``` + + + + +```jsx +import { useState } from 'react'; + +// method实例创建函数 +const getTodoList = currentPage => { + return alovaInstance.Get('/todo/list', { + params: { + currentPage, + pageSize: 10 + } + }); +}; + +const App = () => { + const [currentPage, setCurrentPage] = useState(1); + const { + loading, + data, + error + + // 第一个参数为返回method实例的函数,而非method实例本身 + } = useWatcher( + () => getTodoList(currentPage), + // 被监听的状态数组,这些状态变化将会触发一次请求 + [currentPage], + { + // ⚠️调用useWatcher默认不触发,注意和useRequest的区别 + // 手动设置immediate为true可以初始获取第1页数据 + immediate: true + } + ); + + return { + /* ... */ + }; +}; +``` + + + + +```html + + + +``` + + + + +```html + + + +``` + + + + +## 立即发送请求 + +与`useRequest`不同的是,`useWatcher`的`immediate`属性默认是`false`。 + +```javascript +const { send } = useWatcher(() => getTodoList(currentPage), [currentPage], { + // highlight-start + immediate: true + // highlight-end +}); +send(); +``` + +## 请求防抖 + +通常我们都会在频繁触发的事件层面编写防抖代码,这次我们在请求层面实现了防抖功能,这意味着你再也不用在模糊搜索功能中自己实现防抖了,用法也非常简单。 + +:::info 什么是防抖 + +防抖(debounce),就是指触发事件后,在 n 秒内函数只能执行一次,如果触发事件后在 n 秒内又触发了事件,则会重新计算函数延执行时间(在这里和节流区分一下,节流是在触发完事件之后的一段时间之内不能再次触发事件) + +::: + +### 设置所有监听状态的防抖时间 + +```javascript +const { loading, data, error } = useWatcher( + () => filterTodoList(keyword, date), + [keyword, date], + { + // highlight-start + // 设置debounce为数字时表示为所有监听状态的防抖时间,单位为毫秒 + // 如这边表示当状态keyword、date的一个或多个变化时,将在500ms后才发送请求 + debounce: 500 + // highlight-end + } +); +``` + +### 为单个监听状态设置防抖时间 + +很多场景下,我们只需要对某几个频繁变化的监听状态进行防抖,如文本框的`onInput`触发的状态变化,可以这样做: + +```javascript +const { loading, data, error } = useWatcher( + () => filterTodoList(keyword, date), + [keyword, date], + { + // highlight-start + // 以监听状态的数组顺序分别设置防抖时间,0或不传表示不防抖 + // 这边监听状态的顺序是[keyword, date],防抖数组设置的是[500, 0],表示只对keyword单独设置防抖 + debounce: [500, 0] + // 也可以这么按如下设置: + // debounce: [500], + // highlight-end + } +); +``` + +## 状态改变时阻止请求 + +有时候你希望在监听的状态改变时不发送请求,你可以通过 Hook 配置中的 sendable 属性来控制监听的状态改变时是否发送请求,sendable 属性为一个函数,它的参数为`AlovaEvent`事件对象,包含`send`函数传入的参数所组成的数组`sendArgs`,以及当前请求的 method 实例,并且该函数返回一个`truthy/falsy`值来判断本次状态改变时是否需要触发请求(默认为`true`),**抛出错误也表示不触发请求**。 + +```javascript +useWatcher( + () => getTodoList($currentPage), + // 被监听的状态数组,这些状态变化将会触发一次请求 + [state], + { + // highlight-start + sendable: ({ sendArgs, method }) => { + // do something + // 仅当 state 为 1 时发送请求 + return state === 1; + } + // highlight-end + } +); +``` + +## 请求时序 + +有时候当`useWatcher`监听的状态发生连续的改变导致连续的请求的发起时,后一次的请求先于前一次的请求获得响应,但是当前一次请求获得响应时,会覆盖后一次请求的响应,导致获取到与状态不匹配的响应;例如说有个状态`state`改变后发出了请求`1`,然后在请求`1`还未响应时又改变了`state`值,并发出了请求`2`,如果请求`1`后于请求`2`返回,最终的响应数据会维持在请求`1`。 +所以我们设计了`abortLast`参数,它用于标记当下一次请求发出时,是否中断上一次的未响应请求,默认为`true`,这样`useWatcher`所发出的请求只有最后一次有效。 + +```mermaid +sequenceDiagram + participant U as 用户 + participant S as 服务器 + U ->> U: 监听state状态 + U ->> S: state改变发起请求1 + U ->> S: state改变发起请求2 + S ->> U: 请求2先响应 + S ->> U: 请求1后响应 + U ->> U: 请求2的响应被覆盖 +``` + +```javascript +useWatcher( + () => getTodoList($currentPage), + // 被监听的状态数组,这些状态变化将会触发一次请求 + [state], + { + // highlight-start + abortLast: true // 是否中断上一次的未响应请求,默认为true + // highlight-end + } +); +``` + +:::warning 注意事项 + +`abortLast`默认为`true`,在正常情况下你不需要关注这个参数,如果修改为`false`,可能会导致状态与响应不匹配的问题。 + +::: + +## API + +完整的 API 文档请查看[核心 useHooks](/api/core-hooks#usewatcher)。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/04-initial-data.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/04-initial-data.md index 0e424219e..a379e4efc 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/04-initial-data.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/04-initial-data.md @@ -1,6 +1,5 @@ --- title: 初始数据 -sidebar_position: 40 --- 当使用 `useRequest` 和`useWatcher`时,data 在请求成功前默认为 `undefined`,但有时候我们需要 data 在请求成功前也有初始值,例如在请求列表时通常需要将它初始化为`[]`,否则在渲染视图时会因为无法循环渲染而导致报错。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/05-response.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/05-response.md index d690bce2d..cc90664c6 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/05-response.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/05-response.md @@ -1,117 +1,116 @@ ---- -title: 处理响应 -sidebar_position: 50 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -请求完成后,响应数据会经过多个流程的处理,最终才会在发送请求的位置获得最终数据,流程如下: - -```mermaid -flowchart LR - classDef condition fill:#a8bcff - - R1[响应成功] --> global.onSuccess - global.onSuccess --> global.onComplete - global.onSuccess --> throw{是否抛出错误?}:::condition - throw -->|否| method.transformData - method.transformData --> useHook.onSuccess - throw -->|是| useHook.onError - - method.transformData --> throw2{是否抛出错误?}:::condition - throw2 -->|否| useHook.onSuccess - throw2 -->|是| useHook.onError - - useHook.onSuccess --> throw3{是否抛出错误?}:::condition - throw3 -->|是| useHook.onError - - R2[响应错误] --> global.onError - global.onError --> global.onComplete - global.onError --> throw4{是否抛出错误?}:::condition - throw4 -->|是| useHook.onError - throw4 -->|否| method.transformData -``` - -当没有抛出错误时,下一个节点会接收到上一个节点的返回值。 - -## 转换响应数据 - -在[method 详解](/tutorial/getting-started/method)中,我们已经了解过`transformData`了,这在 useHook 中使用也非常有用,它可以让 useHook 的 data 接收到转换后的数据,而不用再转换。 - -```javascript -const todoListGetter = alovaInstance.Get('/todo/list', { - // 函数接受未加工的数据和响应头对象,并要求将转换后的数据返回,它将会被赋值给data状态。 - // 注意:rawData是全局响应拦截器(如果有设置)过滤后的数据,响应拦截器的配置可以参考[设置全局响应拦截器]章节。 - transformData(rawData, headers) { - return rawData.list.map(item => ({ - ...item, - statusText: item.done ? '已完成' : '进行中' - }); - } -}); -``` - -```javascript -const { data } = useRequest(todoListGetter); -const { data } = useWatcher(() => todoListGetter, [userInfo]); -``` - -data 值将接收到转换后的数据格式。 - -```typescript -type data = { - // ... - statusText: '已完成' | '进行中'; -}[]; -``` - -:::warning 注意 - -在 usehooks 中使用时,在`transformData`中抛出错误也会触发`onError`; - -::: - -## 绑定响应回调 - -如需设置请求回调,你还可以在 useHooks 的返回参数中接收回调的设置函数,如下: - -```javascript -const { - // ... - - // 成功回调绑定 - onSuccess, - - // 失败回调绑定 - onError, - - // 完成回调绑定,回调在成功或失败都会调用 - onComplete -} = useRequest(todoListGetter); // 也适用useWatcher -onSuccess(event => { - console.log('请求成功,响应数据为:', event.data); - console.log('本次请求的method实例为:', event.method); - console.log('响应数据是否来自缓存:', event.fromCache); -}); -onError(event => { - console.log('请求失败,错误信息为:', event.error); - console.log('本次请求的method实例为:', event.method); -}); -onComplete(event => { - // event.status在成功时为success,失败时为error - console.log('请求完成,状态为:', event.status); - console.log('本次请求的method实例为:', event.method); - console.log('响应数据是否来自缓存:', event.fromCache); - if (event.data) { - console.log('请求数据:',event.data) - } else if (event.error) { - console.log('错误信息:',event.error) - } -}); -``` - -:::warning 注意 -在`onSuccess`中抛出错误将会触发`onError`。 - -::: +--- +title: 处理响应 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +请求完成后,响应数据会经过多个流程的处理,最终才会在发送请求的位置获得最终数据,流程如下: + +```mermaid +flowchart LR + classDef condition fill:#a8bcff + + R1[响应成功] --> global.onSuccess + global.onSuccess --> global.onComplete + global.onSuccess --> throw{是否抛出错误?}:::condition + throw -->|否| method.transformData + method.transformData --> useHook.onSuccess + throw -->|是| useHook.onError + + method.transformData --> throw2{是否抛出错误?}:::condition + throw2 -->|否| useHook.onSuccess + throw2 -->|是| useHook.onError + + useHook.onSuccess --> throw3{是否抛出错误?}:::condition + throw3 -->|是| useHook.onError + + R2[响应错误] --> global.onError + global.onError --> global.onComplete + global.onError --> throw4{是否抛出错误?}:::condition + throw4 -->|是| useHook.onError + throw4 -->|否| method.transformData +``` + +当没有抛出错误时,下一个节点会接收到上一个节点的返回值。 + +## 转换响应数据 + +在[method 详解](/tutorial/getting-started/method)中,我们已经了解过`transformData`了,这在 useHook 中使用也非常有用,它可以让 useHook 的 data 接收到转换后的数据,而不用再转换。 + +```javascript +const todoListGetter = alovaInstance.Get('/todo/list', { + // 函数接受未加工的数据和响应头对象,并要求将转换后的数据返回,它将会被赋值给data状态。 + // 注意:rawData是全局响应拦截器(如果有设置)过滤后的数据,响应拦截器的配置可以参考[设置全局响应拦截器]章节。 + transformData(rawData, headers) { + return rawData.list.map(item => ({ + ...item, + statusText: item.done ? '已完成' : '进行中' + }); + } +}); +``` + +```javascript +const { data } = useRequest(todoListGetter); +const { data } = useWatcher(() => todoListGetter, [userInfo]); +``` + +data 值将接收到转换后的数据格式。 + +```typescript +type data = { + // ... + statusText: '已完成' | '进行中'; +}[]; +``` + +:::warning 注意 + +在 usehooks 中使用时,在`transformData`中抛出错误也会触发`onError`; + +::: + +## 绑定响应回调 + +如需设置请求回调,你还可以在 useHooks 的返回参数中接收回调的设置函数,如下: + +```javascript +const { + // ... + + // 成功回调绑定 + onSuccess, + + // 失败回调绑定 + onError, + + // 完成回调绑定,回调在成功或失败都会调用 + onComplete +} = useRequest(todoListGetter); // 也适用useWatcher +onSuccess(event => { + console.log('请求成功,响应数据为:', event.data); + console.log('本次请求的method实例为:', event.method); + console.log('响应数据是否来自缓存:', event.fromCache); +}); +onError(event => { + console.log('请求失败,错误信息为:', event.error); + console.log('本次请求的method实例为:', event.method); +}); +onComplete(event => { + // event.status在成功时为success,失败时为error + console.log('请求完成,状态为:', event.status); + console.log('本次请求的method实例为:', event.method); + console.log('响应数据是否来自缓存:', event.fromCache); + if (event.data) { + console.log('请求数据:',event.data) + } else if (event.error) { + console.log('错误信息:',event.error) + } +}); +``` + +:::warning 注意 +在`onSuccess`中抛出错误将会触发`onError`。 + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/06-abort-request.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/06-abort-request.md index 7906b3399..ce11097ab 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/06-abort-request.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/06-abort-request.md @@ -1,6 +1,5 @@ --- title: 中断请求 -sidebar_position: 60 --- 通过 useHook 接收`abort`用于手动中断请求。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/07-download-upload-progress.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/07-download-upload-progress.md index df431364f..f60c25f0f 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/07-download-upload-progress.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/07-download-upload-progress.md @@ -1,207 +1,206 @@ ---- -title: 下载和上传进度 -sidebar_position: 70 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -useHook 提供了`downloading`和`uploading`用于直接在视图中展示进度信息。 - -## 下载进度 - -为了性能考虑,默认情况下`downloading`中没有进度信息,需要将 method 实例的`enableDownload`设置为`true`,就会在下载过程中持续更新`downloading`状态。 - - - - -```html - - - -``` - - - - -```jsx -const downloadGetter = alovaInstance.Get('/todo/downloadfile', { - // highlight-start - // 开启下载进度 - enableDownload: true - // highlight-end -}); - -const App = () => { - const { downloading } = useRequest(downloadGetter); - return ( - <> -
文件大小:{downloading.total}B
-
已下载:{downloading.loaded}B
-
进度:{(downloading.loaded / downloading.total) * 100}%
- - ); -}; -``` - -
- - -```html - - -
文件大小:{$downloading.total}B
-
已下载:{$downloading.loaded}B
-
进度:{$downloading.loaded / $downloading.total * 100}%
-``` - -
- - -```html - - - -``` - - -
- -## 上传进度 - -使用上传进度状态与下载进度使用方法相同,先通过`enableUpload`开启上传进度信息,再通过接收`uploading`响应式状态接收。 - - - - -```html - - - -``` - - - - -```jsx -const uploadPoster = alovaInstance.Post('/todo/uploadfile', formData, { - // highlight-start - // 开启上传进度 - enableUpload: true - // highlight-end -}); - -const App = () => { - const { uploading } = useRequest(uploadPoster); - return ( - <> -
文件大小:{uploading.total}B
-
已上传:{uploading.loaded}B
-
进度:{(uploading.loaded / uploading.total) * 100}%
- - ); -}; -``` - -
- - -```html - - -
文件大小:{$uploading.total}B
-
已上传:{$uploading.loaded}B
-
进度:{$uploading.loaded / $uploading.total * 100}%
-``` - -
- - -```html - - - -``` - - -
+--- +title: 下载和上传进度 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +useHook 提供了`downloading`和`uploading`用于直接在视图中展示进度信息。 + +## 下载进度 + +为了性能考虑,默认情况下`downloading`中没有进度信息,需要将 method 实例的`enableDownload`设置为`true`,就会在下载过程中持续更新`downloading`状态。 + + + + +```html + + + +``` + + + + +```jsx +const downloadGetter = alovaInstance.Get('/todo/downloadfile', { + // highlight-start + // 开启下载进度 + enableDownload: true + // highlight-end +}); + +const App = () => { + const { downloading } = useRequest(downloadGetter); + return ( + <> +
文件大小:{downloading.total}B
+
已下载:{downloading.loaded}B
+
进度:{(downloading.loaded / downloading.total) * 100}%
+ + ); +}; +``` + +
+ + +```html + + +
文件大小:{$downloading.total}B
+
已下载:{$downloading.loaded}B
+
进度:{$downloading.loaded / $downloading.total * 100}%
+``` + +
+ + +```html + + + +``` + + +
+ +## 上传进度 + +使用上传进度状态与下载进度使用方法相同,先通过`enableUpload`开启上传进度信息,再通过接收`uploading`响应式状态接收。 + + + + +```html + + + +``` + + + + +```jsx +const uploadPoster = alovaInstance.Post('/todo/uploadfile', formData, { + // highlight-start + // 开启上传进度 + enableUpload: true + // highlight-end +}); + +const App = () => { + const { uploading } = useRequest(uploadPoster); + return ( + <> +
文件大小:{uploading.total}B
+
已上传:{uploading.loaded}B
+
进度:{(uploading.loaded / uploading.total) * 100}%
+ + ); +}; +``` + +
+ + +```html + + +
文件大小:{$uploading.total}B
+
已上传:{$uploading.loaded}B
+
进度:{$uploading.loaded / $uploading.total * 100}%
+``` + +
+ + +```html + + + +``` + + +
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/08-receive-params.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/08-receive-params.md index 065b73139..1ec37e9a3 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/08-receive-params.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/08-receive-params.md @@ -1,6 +1,5 @@ --- title: 接收参数 -sidebar_position: 80 --- 在`useRequest`和`useWatcher`中我们都可以调用`send`函数手动触发请求,send 函数触发请求时候,可以传入任意多个参数,这些参数其实可以分别被以下 3 个位置接收。 @@ -32,7 +31,9 @@ send(2); 在事件回调函数中通过`event.sendArgs`接收,它是一个包含了 send 函数的所有参数的数组。 ```javascript -const { send, onSuccess, onError, onComplete } = useRequest(newTodo => alovaInstance.Post('/todo', newTodo)); +const { send, onSuccess, onError, onComplete } = useRequest(newTodo => + alovaInstance.Post('/todo', newTodo) +); onSuccess(event => { // sendArgs的值为[1] console.log(event.sendArgs); diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/10-typescript.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/10-typescript.md index 5fa312aaf..f6c935453 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/10-typescript.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/10-typescript.md @@ -1,159 +1,158 @@ ---- -title: Typescript -sidebar_position: 100 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -在 Typescript 方面,我们确实花了很大的精力优化,为的就是提供更好的使用体验,我们尽力地使用自动推断类型来减少你定义类型的麻烦。 - -## 自动推断 alova useHooks 状态类型 - -在 createAlova 创建 alova 实例时会根据传入的`statesHook`自动推断出`useRequest`、`useWatcher`、`useFetcher`所创建的状态类型。 - -> `useFetcher`是一个用于数据拉取的 useHook,详情请阅读[进阶-数据拉取章节](/tutorial/advanced/use-fetcher)。 - -以下为预设中,useHooks 返回的状态类型。 - - - - -```typescript -const vueAlova = createAlova({ - statesHook: VueHook - // ... -}); -const { - loading, // Ref - data, // Ref<{ data: any }> - error // Ref -} = useRequest(vueAlova.Get<{ data: any }>('/todo/list')); -``` - - - - -```typescript -const reactAlova = createAlova({ - statesHook: ReactHook - // ... -}); -const { - loading, // boolean - data, // { data: any } - error // Error -} = useRequest(reactAlova.Get<{ data: any }>('/todo/list')); -``` - - - - -```typescript -const svelteAlova = createAlova({ - statesHook: SvelteHook - // ... -}); -const { - loading, // Writable - data, // Writable<{ data: any }> - error // Writable -} = useRequest(svelteAlova.Get<{ data: any }>('/todo/list')); -``` - - - - -data 的类型将会根据不同的 Method 实例中指定的响应数据类型而不同,我们继续往下看。 - -## 设置响应数据的类型 - -当你为一个数据接口指定类型时,需要分为两种情况。 - -### 情况 1 - -当响应数据不需要再调用`transformData`转换,直接通过泛型指定类型 - -```typescript -interface Todo { - title: string; - time: string; - done: boolean; -} -const Get = alovaInstance.Get('/todo/list'); -const { data } = useRequest(Get); -// vue: data的类型为Ref -// react: data的类型为Todo[] -// svelte: data的类型为Writable -``` - -### 情况 2 - -当响应数据需要再调用`transformData`转换,那就需要在转换函数参数中指定类型,然后它的返回值类型将会作为响应数据类型。 - -```typescript -interface Todo { - title: string; - time: string; - done: boolean; -} -const Get = alovaInstance.Get('/todo/list', { - // 将类型写到data参数中,而headers会自动推断,可以不用指定类型 - transformData(data: Todo[], headers) { - return data.map(item => ({ - ...item, - status: item.done ? '已完成' : '未完成' - })); - } -}); - -const { data } = useRequest(Get); -// vue: data的类型为Ref<(Todo & { status: string })[]> -// react: data的类型为(Todo & { status: string })[] -// svelte: data的类型为Writable<(Todo & { status: string })[]> -``` - -:::warning 注意 - -响应数据是经过全局响应拦截器转换后的,因此设置类型时也应该设置为转换后的类型。 - -::: - -## 根据请求适配器推断的类型 - -因为 alova 支持自定义请求适配器,而不同的适配器的请求配置对象、响应对象、响应头都可能不同,因此全局的`beforeRequest`、`responded`拦截器,以及 method 实例创建时的配置对象的类型,都会根据请求适配器提供的类型自动推断,我们先来看这几个类型。 - -如果你正在使用 [**GlobalFetch**](https://github.com/alovajs/alova/blob/main/src/predefine/GlobalFetch.ts),alova 将会使用`fetch api`的类型自动推断,fetch api 的类型如下。 - -```typescript -declare function fetch(input: RequestInfo | URL, init?: RequestInit): Promise; -``` - -### method 实例的配置类型 - -method 配置类型将被自动推断为: - -```typescript -// AlovaMethodCommonConfig为统一的请求参数和行为参数 -// highlight-start -const methodConfig: AlovaMethodCommonConfig & RequestInit = { - // highlight-end - // ... -}; -alovaInstance.Get('/api/user', methodConfig); -``` - -### 全局响应拦截器参数类型 - -responded 拦截器的类型将被自动推断为: - -```typescript -createAlova({ - // ... - // highlight-start - responded: (response: Response, method: Method) => { - // highlight-end - // ... - } -}); -``` +--- +title: Typescript +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +在 Typescript 方面,我们确实花了很大的精力优化,为的就是提供更好的使用体验,我们尽力地使用自动推断类型来减少你定义类型的麻烦。 + +## 自动推断 alova useHooks 状态类型 + +在 createAlova 创建 alova 实例时会根据传入的`statesHook`自动推断出`useRequest`、`useWatcher`、`useFetcher`所创建的状态类型。 + +> `useFetcher`是一个用于数据拉取的 useHook,详情请阅读[进阶-数据拉取章节](/tutorial/advanced/use-fetcher)。 + +以下为预设中,useHooks 返回的状态类型。 + + + + +```typescript +const vueAlova = createAlova({ + statesHook: VueHook + // ... +}); +const { + loading, // Ref + data, // Ref<{ data: any }> + error // Ref +} = useRequest(vueAlova.Get<{ data: any }>('/todo/list')); +``` + + + + +```typescript +const reactAlova = createAlova({ + statesHook: ReactHook + // ... +}); +const { + loading, // boolean + data, // { data: any } + error // Error +} = useRequest(reactAlova.Get<{ data: any }>('/todo/list')); +``` + + + + +```typescript +const svelteAlova = createAlova({ + statesHook: SvelteHook + // ... +}); +const { + loading, // Writable + data, // Writable<{ data: any }> + error // Writable +} = useRequest(svelteAlova.Get<{ data: any }>('/todo/list')); +``` + + + + +data 的类型将会根据不同的 Method 实例中指定的响应数据类型而不同,我们继续往下看。 + +## 设置响应数据的类型 + +当你为一个数据接口指定类型时,需要分为两种情况。 + +### 情况 1 + +当响应数据不需要再调用`transformData`转换,直接通过泛型指定类型 + +```typescript +interface Todo { + title: string; + time: string; + done: boolean; +} +const Get = alovaInstance.Get('/todo/list'); +const { data } = useRequest(Get); +// vue: data的类型为Ref +// react: data的类型为Todo[] +// svelte: data的类型为Writable +``` + +### 情况 2 + +当响应数据需要再调用`transformData`转换,那就需要在转换函数参数中指定类型,然后它的返回值类型将会作为响应数据类型。 + +```typescript +interface Todo { + title: string; + time: string; + done: boolean; +} +const Get = alovaInstance.Get('/todo/list', { + // 将类型写到data参数中,而headers会自动推断,可以不用指定类型 + transformData(data: Todo[], headers) { + return data.map(item => ({ + ...item, + status: item.done ? '已完成' : '未完成' + })); + } +}); + +const { data } = useRequest(Get); +// vue: data的类型为Ref<(Todo & { status: string })[]> +// react: data的类型为(Todo & { status: string })[] +// svelte: data的类型为Writable<(Todo & { status: string })[]> +``` + +:::warning 注意 + +响应数据是经过全局响应拦截器转换后的,因此设置类型时也应该设置为转换后的类型。 + +::: + +## 根据请求适配器推断的类型 + +因为 alova 支持自定义请求适配器,而不同的适配器的请求配置对象、响应对象、响应头都可能不同,因此全局的`beforeRequest`、`responded`拦截器,以及 method 实例创建时的配置对象的类型,都会根据请求适配器提供的类型自动推断,我们先来看这几个类型。 + +如果你正在使用 [**GlobalFetch**](https://github.com/alovajs/alova/blob/main/src/predefine/GlobalFetch.ts),alova 将会使用`fetch api`的类型自动推断,fetch api 的类型如下。 + +```typescript +declare function fetch(input: RequestInfo | URL, init?: RequestInit): Promise; +``` + +### method 实例的配置类型 + +method 配置类型将被自动推断为: + +```typescript +// AlovaMethodCommonConfig为统一的请求参数和行为参数 +// highlight-start +const methodConfig: AlovaMethodCommonConfig & RequestInit = { + // highlight-end + // ... +}; +alovaInstance.Get('/api/user', methodConfig); +``` + +### 全局响应拦截器参数类型 + +responded 拦截器的类型将被自动推断为: + +```typescript +createAlova({ + // ... + // highlight-start + responded: (response: Response, method: Method) => { + // highlight-end + // ... + } +}); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/01-mode.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/01-mode.md index 62da7b2db..74865ad45 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/01-mode.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/01-mode.md @@ -1,218 +1,217 @@ ---- -title: 缓存模式 -sidebar_position: 10 ---- - -import MemoryCache from '@site/example-links/MemoryCache'; -import StoragePlaceholder from '@site/example-links/StoragePlaceholder'; -import StorageRestore from '@site/example-links/StorageRestore'; - -缓存模式可在全局或请求级等不同粒度下设置。全局设置时,所有由相同 alova 实例创建的 method 实例都会继承该设置。 - -:::info 注意 - -是否使用缓存模式,以及使用哪种缓存模式需要根据场景而定,下面在单独介绍不同缓存模式时将会提及它们的使用场景。 - -::: - -## 内存模式(默认) - -内存模式将缓存放在内存中,这意味着刷新页面缓存即失效,是最常用的缓存模式。 - -内存模式一般用于解决短时间内(几分钟或几秒钟)频繁请求相同数据带来的性能消耗,例如当你在写 todo 详情页的时候,你可能会想到用户会频繁在 todo 列表中点击查看详情,如果用户重复查看某条详情时不再重复请求接口,并且能立即返回数据,提升了响应速度的同时也减小了服务器压力。此时我们就可以为某个 todo 详情 method 实例设置响应数据缓存。 - -```javascript -alovaInstance.GET('/todo/list', { - // ... - // highlight-start - localCache: { - // 设置缓存模式为内存模式 - mode: 'memory', - - // 单位为毫秒 - // 当设置为`Infinity`,表示数据永不过期,设置为0或负数时表示不缓存 - expire: 60 * 10 * 1000 - } - // highlight-end -}); -``` - -内存模式为默认模式,你可以这样简写 - -```javascript -alovaInstance.GET('/todo/list', { - // ... - // highlight-start - localCache: 60 * 10 * 1000 - // highlight-end -}); -``` - -> GET 请求将默认设置 300000ms(5 分钟)的内存缓存时间,开发者也可以自定义设置。 - -> 如果你需要全局统一设置缓存模式,见本节底部的 [全局设置缓存模式](#全局设置缓存模式) - -### 内存缓存模式示例 - - - -## 缓存占位模式 - -这个缓存模式用于,当你不希望应用每次进入时都显示 Loading 图标,而希望使用旧数据替代时,你可以使用缓存占位模式,它的体验比 Loading 更好。 - -缓存占位模式下,`data`将立即被赋值为上次缓存的旧数据,你可以判断如果有旧数据则使用它替代 Loading 展示,同时它将发送请求获取最新数据并更新缓存,这样就达到了既快速展示实际数据,又获取了最新的数据。 - -在 method 实例上设置: - -```javascript -const todoListGetter = alovaInstance.Get('/todo/list', { - // ... - // highlight-start - localCache: { - // 设置缓存模式为持久化占位模式 - mode: 'placeholder', - // 缓存时间 - expire: 60 * 10 * 1000 - } - // highlight-end -}); -``` - -> 如果你需要全局统一设置缓存模式,见本节底部的 [全局设置缓存模式](#全局设置缓存模式) - -### 缓存占位模式示例 - - - -## 恢复模式 - -此模式下,服务端缓存数据将持久化,如果过期时间未到即使刷新页面缓存也不会失效,它一般用于一些需要服务端管理,但基本不变的数据,如每年的节假日具体日期有所不同,但不会再变动,这种场景下我们只需设置缓存过期时间为今年的最后一刻即可。 - -在 method 实例上设置: - -```javascript -const todoListGetter = alovaInstance.Get('/todo/list', { - // ... - // highlight-start - localCache: { - // 设置缓存模式为持久化模式 - mode: 'restore', - // 缓存时间 - expire: 60 * 10 * 1000 - } - // highlight-end -}); -``` - -:::warning 注意 - -当 request body 是**FormData**、**Blob**、**ArrayBuffer**、**URLSearchParams**、**ReadableStream**等特殊数据时,将会被认为你是有意图和服务端通信的,在这种情况下不会进行缓存。 - -::: - -> 如果你需要全局统一设置缓存模式,见本节底部的 [全局设置缓存模式](#全局设置缓存模式) - -### 恢复模式示例 - - - -### 恢复模式下数据有变怎么办? - -当设置了恢复模式的 method 实例,可能由于接口数据变动,或前端处理响应数据的逻辑变动,此时需要在发布应用后让用户重新缓存变动后的数据,此时你可以通过`tag`属性设置缓存标签,每一份持久化数据都包含一个`tag`标识,当`tag`改变后原有的持久化数据将会失效,并重新获取新的数据,并用新的`tag`进行标识。 - -```javascript -const todoListGetter = alovaInstance.Get('/todo/list', { - // ... - localCache: { - mode: 'restore', - expire: 60 * 10 * 1000, - - // highlight-start - // 新增或修改tag参数,已缓存的数据将失效 - // 建议使用版本号的形式管理 - tag: 'v1' - // highlight-end - } -}); -``` - -## 全局设置缓存模式 - -:::info 版本要求 - -v1.3.0+ - -::: - -以上设置均是在`Method`上单独设置缓存模式的,如果你需要全局设置缓存模式,可以按如下方式做: - -```javascript -const alovaInstance = createAlova({ - // ... - // highlight-start - localCache: { - // 统一设置POST的缓存模式 - POST: { - mode: 'placeholder', - expire: 60 * 10 * 1000 - }, - // 统一设置HEAD请求的缓存模式 - HEAD: 60 * 10 * 1000 - } - // highlight-end -}); -``` - -此后,通过`alovaInstance`实例创建的 method 实例,都将默认使用这份缓存设置,同时也可以在 method 实例中覆盖它。 - -> 注意:当全局设置了缓存模式后,原有的 5 分钟 GET 缓存模式将被覆盖。 - -## 全局关闭缓存模式 - -如果在你的项目中不希望使用任何请求缓存,可以在全局将它关闭,如果希望只在特定的几个请求中使用,也可以全局关闭它,并在指定的 method 实例中设置即可。 - -```javascript -const alovaInstance = createAlova({ - // ... - // highlight-start - // 设置为null即可全局关闭全部请求缓存 - localCache: null - // highlight-end -}); -``` - -## 过期时间类型 - -过期时间有两种类型可供选择,分别为 **相对时间** 和 **绝对时间** - -### 相对时间 - -即在保存缓存数据时开始,过期的时长,以 **毫秒** 为单位,以上示例均为此类型。 - -```javascript -localCache: 60 * 10 * 1000; -``` - -```javascript -localCache: { - expire: 60 * 10 * 1000, -} -``` - -### 绝对时间 - -以一个具体时间点为过期时间,缓存将在设定的时间点过期 - -```javascript -localCache: new Date('2030-01-01'); -``` - -```javascript -localCache: { - expire: new Date('2030-01-01'); -} -``` - -## 响应自动维护说明 - -响应数据缓存的 key 是由 method 实例的请求方法(method)、请求地址(url)、请求头参数(headers)、url 参数(params)、请求体参数(requestBody)组合作为唯一标识,任意一个信息或位置不同都将被当做不同的 key。 +--- +title: 缓存模式 +--- + +import MemoryCache from '@site/example-links/MemoryCache'; +import StoragePlaceholder from '@site/example-links/StoragePlaceholder'; +import StorageRestore from '@site/example-links/StorageRestore'; + +缓存模式可在全局或请求级等不同粒度下设置。全局设置时,所有由相同 alova 实例创建的 method 实例都会继承该设置。 + +:::info 注意 + +是否使用缓存模式,以及使用哪种缓存模式需要根据场景而定,下面在单独介绍不同缓存模式时将会提及它们的使用场景。 + +::: + +## 内存模式(默认) + +内存模式将缓存放在内存中,这意味着刷新页面缓存即失效,是最常用的缓存模式。 + +内存模式一般用于解决短时间内(几分钟或几秒钟)频繁请求相同数据带来的性能消耗,例如当你在写 todo 详情页的时候,你可能会想到用户会频繁在 todo 列表中点击查看详情,如果用户重复查看某条详情时不再重复请求接口,并且能立即返回数据,提升了响应速度的同时也减小了服务器压力。此时我们就可以为某个 todo 详情 method 实例设置响应数据缓存。 + +```javascript +alovaInstance.GET('/todo/list', { + // ... + // highlight-start + localCache: { + // 设置缓存模式为内存模式 + mode: 'memory', + + // 单位为毫秒 + // 当设置为`Infinity`,表示数据永不过期,设置为0或负数时表示不缓存 + expire: 60 * 10 * 1000 + } + // highlight-end +}); +``` + +内存模式为默认模式,你可以这样简写 + +```javascript +alovaInstance.GET('/todo/list', { + // ... + // highlight-start + localCache: 60 * 10 * 1000 + // highlight-end +}); +``` + +> GET 请求将默认设置 300000ms(5 分钟)的内存缓存时间,开发者也可以自定义设置。 + +> 如果你需要全局统一设置缓存模式,见本节底部的 [全局设置缓存模式](#全局设置缓存模式) + +### 内存缓存模式示例 + + + +## 缓存占位模式 + +这个缓存模式用于,当你不希望应用每次进入时都显示 Loading 图标,而希望使用旧数据替代时,你可以使用缓存占位模式,它的体验比 Loading 更好。 + +缓存占位模式下,`data`将立即被赋值为上次缓存的旧数据,你可以判断如果有旧数据则使用它替代 Loading 展示,同时它将发送请求获取最新数据并更新缓存,这样就达到了既快速展示实际数据,又获取了最新的数据。 + +在 method 实例上设置: + +```javascript +const todoListGetter = alovaInstance.Get('/todo/list', { + // ... + // highlight-start + localCache: { + // 设置缓存模式为持久化占位模式 + mode: 'placeholder', + // 缓存时间 + expire: 60 * 10 * 1000 + } + // highlight-end +}); +``` + +> 如果你需要全局统一设置缓存模式,见本节底部的 [全局设置缓存模式](#全局设置缓存模式) + +### 缓存占位模式示例 + + + +## 恢复模式 + +此模式下,服务端缓存数据将持久化,如果过期时间未到即使刷新页面缓存也不会失效,它一般用于一些需要服务端管理,但基本不变的数据,如每年的节假日具体日期有所不同,但不会再变动,这种场景下我们只需设置缓存过期时间为今年的最后一刻即可。 + +在 method 实例上设置: + +```javascript +const todoListGetter = alovaInstance.Get('/todo/list', { + // ... + // highlight-start + localCache: { + // 设置缓存模式为持久化模式 + mode: 'restore', + // 缓存时间 + expire: 60 * 10 * 1000 + } + // highlight-end +}); +``` + +:::warning 注意 + +当 request body 是**FormData**、**Blob**、**ArrayBuffer**、**URLSearchParams**、**ReadableStream**等特殊数据时,将会被认为你是有意图和服务端通信的,在这种情况下不会进行缓存。 + +::: + +> 如果你需要全局统一设置缓存模式,见本节底部的 [全局设置缓存模式](#全局设置缓存模式) + +### 恢复模式示例 + + + +### 恢复模式下数据有变怎么办? + +当设置了恢复模式的 method 实例,可能由于接口数据变动,或前端处理响应数据的逻辑变动,此时需要在发布应用后让用户重新缓存变动后的数据,此时你可以通过`tag`属性设置缓存标签,每一份持久化数据都包含一个`tag`标识,当`tag`改变后原有的持久化数据将会失效,并重新获取新的数据,并用新的`tag`进行标识。 + +```javascript +const todoListGetter = alovaInstance.Get('/todo/list', { + // ... + localCache: { + mode: 'restore', + expire: 60 * 10 * 1000, + + // highlight-start + // 新增或修改tag参数,已缓存的数据将失效 + // 建议使用版本号的形式管理 + tag: 'v1' + // highlight-end + } +}); +``` + +## 全局设置缓存模式 + +:::info 版本要求 + +v1.3.0+ + +::: + +以上设置均是在`Method`上单独设置缓存模式的,如果你需要全局设置缓存模式,可以按如下方式做: + +```javascript +const alovaInstance = createAlova({ + // ... + // highlight-start + localCache: { + // 统一设置POST的缓存模式 + POST: { + mode: 'placeholder', + expire: 60 * 10 * 1000 + }, + // 统一设置HEAD请求的缓存模式 + HEAD: 60 * 10 * 1000 + } + // highlight-end +}); +``` + +此后,通过`alovaInstance`实例创建的 method 实例,都将默认使用这份缓存设置,同时也可以在 method 实例中覆盖它。 + +> 注意:当全局设置了缓存模式后,原有的 5 分钟 GET 缓存模式将被覆盖。 + +## 全局关闭缓存模式 + +如果在你的项目中不希望使用任何请求缓存,可以在全局将它关闭,如果希望只在特定的几个请求中使用,也可以全局关闭它,并在指定的 method 实例中设置即可。 + +```javascript +const alovaInstance = createAlova({ + // ... + // highlight-start + // 设置为null即可全局关闭全部请求缓存 + localCache: null + // highlight-end +}); +``` + +## 过期时间类型 + +过期时间有两种类型可供选择,分别为 **相对时间** 和 **绝对时间** + +### 相对时间 + +即在保存缓存数据时开始,过期的时长,以 **毫秒** 为单位,以上示例均为此类型。 + +```javascript +localCache: 60 * 10 * 1000; +``` + +```javascript +localCache: { + expire: 60 * 10 * 1000, +} +``` + +### 绝对时间 + +以一个具体时间点为过期时间,缓存将在设定的时间点过期 + +```javascript +localCache: new Date('2030-01-01'); +``` + +```javascript +localCache: { + expire: new Date('2030-01-01'); +} +``` + +## 响应自动维护说明 + +响应数据缓存的 key 是由 method 实例的请求方法(method)、请求地址(url)、请求头参数(headers)、url 参数(params)、请求体参数(requestBody)组合作为唯一标识,任意一个信息或位置不同都将被当做不同的 key。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/02-auto-invalidate.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/02-auto-invalidate.md index fbdf57836..efb9e7e6f 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/02-auto-invalidate.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/02-auto-invalidate.md @@ -1,91 +1,90 @@ ---- -title: 自动失效 -sidebar_position: 20 ---- - -有这样一个场景,当用户点开 todo 列表中的某一项,进入 todo 详情页并对它执行了编辑,此时我们希望上一页中的 todo 列表数据也更新为编辑后的内容,通常的做法是通过事件来触发上一页的内容更新,这样增加了维护成本。而`alova`提供了 3 种方式,可以很优雅地达到这个目的: - -1. 使用`useFetcher`立即重新请求最新的数据,它将在[数据拉取](/tutorial/advanced/use-fetcher)章节中讲解; -2. 更新缓存,这种方式将在后面的[缓存设置与查询](/tutorial/cache/set-and-query)章节中详细讲解; -3. 让这个响应缓存失效,当再次请求时将会因缓存失效而重新请求数据。这也是这个章节要讲解的内容。 - -自动失效缓存是在目标缓存中设置失效源规则,只要匹配规则都可以让目标缓存自动失效,这在很多时候省去了手动清除缓存的麻烦。 - -## 使用场景 - -当目标缓存与失效源是一对一或一对多时,设置自动失效规则会很方便。 - -```mermaid -flowchart - M1[method1失效源指向] --> T1[目标缓存] - M11[method1失效源指向] --> T2[目标缓存] - M2[method2失效源指向] --> T2[目标缓存] - MN[methodN失效源指向] --> T2[目标缓存] -``` - -## 设置自动失效规则 - -设置这个规则很简单,你可以在创建一个带缓存的 Method 实例时,为它设置`hitSource`参数即可。 - -### 失效源设置为 method 实例 - -以一个固定的 method 实例作为失效源,只要此 method 实例或它的克隆实例请求成功,目标缓存将被自动清除。 - -```javascript -alova.Get('/todo/1', { - // ... - hitSource: alova.Post('/todo', {}) -}); -``` - -### 通过 method 名称匹配失效源 - -和 method 匹配器一样,你可以在 hitSource 中指定 method 的名称来匹配失效源,多个失效源可以设置为同一个名称,带有这个名称的 method 实例请求成功时,目标缓存将被自动清除。 - -```javascript -const methodSubmitTodo = data => - alova.Post('/todo', data, { - name: 'submitTodo' - }); - -alova.Get('/todo/1', { - // ... - // 匹配method实例名称为submitTodo的失效源 - hitSource: 'submitTodo' -}); -``` - -### 通过 method 名称正则表达式匹配失效源 - -如果 method 实例名称不固定时,你可以在 hitSource 中指定一个正则表达式来匹配 method 名称,被匹配的 method 实例在请求成功时,目标缓存将被自动清除。 - -```javascript -const methodSubmitTodo = data => - alova.Post('/todo', data, { - name: 'prefix-submitTodo' - }); - -alova.Get('/todo/1', { - // ... - // 匹配method实例名称为prefix开头的所有实例 - hitSource: /^prefix/ -}); -``` - -### 组合设置失效源 - -如果你希望使用以上的多种规则匹配失效源,可以将 hitSource 指定为一个数组,数组项为以上 3 种规则的任意一种,满足数组任意一项规则的 method 实例将被匹配。 - -```javascript -alova.Get('/todo/1', { - // ... - // 满足数组中任意一项匹配规则的method实例请求成功时,此缓存将失效 - hitSource: [alova.Post('/todo', {}), 'submitTodo', /^prefix/] -}); -``` - -## hitSource 数据类型 - -```typescript -type hitSource = string | RegExp | Method | (string | RegExp | Method)[]; -``` +--- +title: 自动失效 +--- + +有这样一个场景,当用户点开 todo 列表中的某一项,进入 todo 详情页并对它执行了编辑,此时我们希望上一页中的 todo 列表数据也更新为编辑后的内容,通常的做法是通过事件来触发上一页的内容更新,这样增加了维护成本。而`alova`提供了 3 种方式,可以很优雅地达到这个目的: + +1. 使用`useFetcher`立即重新请求最新的数据,它将在[数据拉取](/tutorial/advanced/use-fetcher)章节中讲解; +2. 更新缓存,这种方式将在后面的[缓存设置与查询](/tutorial/cache/set-and-query)章节中详细讲解; +3. 让这个响应缓存失效,当再次请求时将会因缓存失效而重新请求数据。这也是这个章节要讲解的内容。 + +自动失效缓存是在目标缓存中设置失效源规则,只要匹配规则都可以让目标缓存自动失效,这在很多时候省去了手动清除缓存的麻烦。 + +## 使用场景 + +当目标缓存与失效源是一对一或一对多时,设置自动失效规则会很方便。 + +```mermaid +flowchart + M1[method1失效源指向] --> T1[目标缓存] + M11[method1失效源指向] --> T2[目标缓存] + M2[method2失效源指向] --> T2[目标缓存] + MN[methodN失效源指向] --> T2[目标缓存] +``` + +## 设置自动失效规则 + +设置这个规则很简单,你可以在创建一个带缓存的 Method 实例时,为它设置`hitSource`参数即可。 + +### 失效源设置为 method 实例 + +以一个固定的 method 实例作为失效源,只要此 method 实例或它的克隆实例请求成功,目标缓存将被自动清除。 + +```javascript +alova.Get('/todo/1', { + // ... + hitSource: alova.Post('/todo', {}) +}); +``` + +### 通过 method 名称匹配失效源 + +和 method 匹配器一样,你可以在 hitSource 中指定 method 的名称来匹配失效源,多个失效源可以设置为同一个名称,带有这个名称的 method 实例请求成功时,目标缓存将被自动清除。 + +```javascript +const methodSubmitTodo = data => + alova.Post('/todo', data, { + name: 'submitTodo' + }); + +alova.Get('/todo/1', { + // ... + // 匹配method实例名称为submitTodo的失效源 + hitSource: 'submitTodo' +}); +``` + +### 通过 method 名称正则表达式匹配失效源 + +如果 method 实例名称不固定时,你可以在 hitSource 中指定一个正则表达式来匹配 method 名称,被匹配的 method 实例在请求成功时,目标缓存将被自动清除。 + +```javascript +const methodSubmitTodo = data => + alova.Post('/todo', data, { + name: 'prefix-submitTodo' + }); + +alova.Get('/todo/1', { + // ... + // 匹配method实例名称为prefix开头的所有实例 + hitSource: /^prefix/ +}); +``` + +### 组合设置失效源 + +如果你希望使用以上的多种规则匹配失效源,可以将 hitSource 指定为一个数组,数组项为以上 3 种规则的任意一种,满足数组任意一项规则的 method 实例将被匹配。 + +```javascript +alova.Get('/todo/1', { + // ... + // 满足数组中任意一项匹配规则的method实例请求成功时,此缓存将失效 + hitSource: [alova.Post('/todo', {}), 'submitTodo', /^prefix/] +}); +``` + +## hitSource 数据类型 + +```typescript +type hitSource = string | RegExp | Method | (string | RegExp | Method)[]; +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/03-manually-invalidate.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/03-manually-invalidate.md index 512468e41..c8cf7f77e 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/03-manually-invalidate.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/03-manually-invalidate.md @@ -1,102 +1,101 @@ ---- -title: 手动失效 -sidebar_position: 30 ---- - -通常,自动失效缓存更加简洁,并且推荐优先使用它来失效缓存,当自动失效缓存不满足需求时,你还可以通过调用`invalidateCache`来失效缓存。 - -## 使用 method 实例失效缓存 - -在 `invalidateCache` 函数中传入一个 method 实例,它将查找此实例下的缓存进行失效。 - -在下面的例子中,当提交成功后,将使这条 todo 详情数据缓存失效。 - -```javascript -// 获取 id 为 1 的 todo 详情数据 -const getTodoDetail = id => - alovaInstance.Get(`/todo/${id}`, { - localCache: 1000000 - }); -const { loading, data } = useRequest(getTodoDetail(1)); -``` - -```javascript -// 提交数据并让 id 为 1 的 todo 详情数据失效。 -const { - // ... - send, - onSuccess -} = useRequest(createTodoPoster, { immediate: false }); - -// highlight-start -// 提交成功后失效缓存 -onSuccess(() => { - invalidateCache(getTodoDetail(1)); -}); -// highlight-end - -const handleSubmit = () => { - send({ - title: 'new todo', - content: 'new todo content' - }); -}; -``` - -## 批量失效缓存 - -在下面的例子中,我们通过指定缓存的名称或名称的正则表达式来批量失效缓存。 - -```javascript -// 名称为todoList的method的缓存将失效 -invalidateCache('todoList'); - -// 名称符合以下正则表达式的method的缓存将失效 -invalidateCache(/^todoList/); -``` - -## 动态失效缓存 - -可能有时候你并不确定需要失效哪个缓存数据,我们可以使用 [method 匹配器](/tutorial/advanced/method-matcher) 来动态查找对应的 method 实例。以下例子展示了如何让名称为 todoList 的前 5 个 method 实例的缓存失效。 - -```javascript -const getTodoList = currentPage => { - return alovaInstance.Get('/todo/list', { - // highlight-start - // 先为method实例设置名称,用于在无法直接指定Method实例时,过滤出需要的Method实例 - name: 'todoList', - // highlight-end - params: { - currentPage, - pageSize: 10 - } - }); -}; - -const { - // ... - send, - onSuccess -} = useRequest(createTodoPoster, { immediate: false }); -// 提交成功后,固定使第一页的todo数据缓存失效 -onSuccess(() => { - // highlight-start - // 失效名称为todoList的前5个Method实例的缓存 - invalidateCache({ - name: 'todoList', - filter: (method, index, ary) => { - return index < 5; - } - }); - // highlight-end -}); -``` - -> 更多 method 匹配器的使用方法见 [method 匹配器](/tutorial/advanced/method-matcher) - -## 失效所有缓存 - -```javascript -// 当不传任何参数时,失效所有响应缓存 -invalidateCache(); -``` +--- +title: 手动失效 +--- + +通常,自动失效缓存更加简洁,并且推荐优先使用它来失效缓存,当自动失效缓存不满足需求时,你还可以通过调用`invalidateCache`来失效缓存。 + +## 使用 method 实例失效缓存 + +在 `invalidateCache` 函数中传入一个 method 实例,它将查找此实例下的缓存进行失效。 + +在下面的例子中,当提交成功后,将使这条 todo 详情数据缓存失效。 + +```javascript +// 获取 id 为 1 的 todo 详情数据 +const getTodoDetail = id => + alovaInstance.Get(`/todo/${id}`, { + localCache: 1000000 + }); +const { loading, data } = useRequest(getTodoDetail(1)); +``` + +```javascript +// 提交数据并让 id 为 1 的 todo 详情数据失效。 +const { + // ... + send, + onSuccess +} = useRequest(createTodoPoster, { immediate: false }); + +// highlight-start +// 提交成功后失效缓存 +onSuccess(() => { + invalidateCache(getTodoDetail(1)); +}); +// highlight-end + +const handleSubmit = () => { + send({ + title: 'new todo', + content: 'new todo content' + }); +}; +``` + +## 批量失效缓存 + +在下面的例子中,我们通过指定缓存的名称或名称的正则表达式来批量失效缓存。 + +```javascript +// 名称为todoList的method的缓存将失效 +invalidateCache('todoList'); + +// 名称符合以下正则表达式的method的缓存将失效 +invalidateCache(/^todoList/); +``` + +## 动态失效缓存 + +可能有时候你并不确定需要失效哪个缓存数据,我们可以使用 [method 匹配器](/tutorial/advanced/method-matcher) 来动态查找对应的 method 实例。以下例子展示了如何让名称为 todoList 的前 5 个 method 实例的缓存失效。 + +```javascript +const getTodoList = currentPage => { + return alovaInstance.Get('/todo/list', { + // highlight-start + // 先为method实例设置名称,用于在无法直接指定Method实例时,过滤出需要的Method实例 + name: 'todoList', + // highlight-end + params: { + currentPage, + pageSize: 10 + } + }); +}; + +const { + // ... + send, + onSuccess +} = useRequest(createTodoPoster, { immediate: false }); +// 提交成功后,固定使第一页的todo数据缓存失效 +onSuccess(() => { + // highlight-start + // 失效名称为todoList的前5个Method实例的缓存 + invalidateCache({ + name: 'todoList', + filter: (method, index, ary) => { + return index < 5; + } + }); + // highlight-end +}); +``` + +> 更多 method 匹配器的使用方法见 [method 匹配器](/tutorial/advanced/method-matcher) + +## 失效所有缓存 + +```javascript +// 当不传任何参数时,失效所有响应缓存 +invalidateCache(); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/04-force-request.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/04-force-request.md index ef4926f78..9b26627ec 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/04-force-request.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/04-force-request.md @@ -1,6 +1,5 @@ --- title: 强制请求 -sidebar_position: 40 --- 强制请求是指绕过缓存的检查触发请求发送的机制,当需要在一定条件下获取最新的数据时很有用。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/05-set-and-query.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/05-set-and-query.md index 26d210dfb..ff4ed259a 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/05-set-and-query.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/05-set-and-query.md @@ -1,284 +1,295 @@ ---- -title: 更新与查找缓存 -sidebar_position: 50 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -缓存也支持更新和查找,在[缓存模式](/tutorial/cache/mode)中我们提到过,每份缓存数据是以发送请求的 method 实例作为 key 进行保存的,因此在手动更新缓存时也将使用 method 实例来查找对应的缓存数据。 - -## 更新静态缓存数据 - - - - -```html - - -``` - - - - -```jsx -import { setCache } from 'alova'; -import { useState } from 'react'; - -const getTodoListByDate = dateList => - alovaInstance.Get('/todo/list/dates', { - params: { dateList } - }); - -const App = () => { - // 初始化时批量获取5天的数据 - const [dates, setDates] = useState(['2022-05-01', '2022-05-02', '2022-05-03', '2022-05-04', '2022-05-05']); - const { - // ... - onSuccess - } = useWatcher(() => getTodoListByDate(dates.join()), [dates], { - immediate: true - }); - onSuccess(({ data: todoListDates }) => { - if (todoListDates.length <= 1) { - return; - } - - // highlight-start - // 默认情况下,这5天的数据会一起缓存到一个key中 - // 为了让后续请求某一天的数据时也能命中缓存,我们可以将5天的数据拆解为按天,并通过setCache一一手动设置响应缓存 - // setCache的第一个参数为method实例对象,它用于指定缓存的key - // 第二个参数为缓存数据 - todoListDates.forEach(todoDate => { - setCache(getTodoListByDate(todoDate.date), [todoDate]); - }); - // highlight-end - }); - - // highlight-start - const handleTodolistToggle = () => { - // 此时再在切换日期为5月1日时,它将会命中我们手动设置的响应缓存。 - // dates值正在被useWatcher监听,因此改变它就可以自动触发请求 - setDates(['2022-05-01']); - }; - // highlight-end - - return ; -}; -``` - - - - -```html - - -``` - - - - -```html - - -``` - - - - -## 动态更新缓存数据 - -你也可以在`setCache`中传入一个回调函数来动态计算缓存数据,并返回需要更新的缓存数据。 - -```javascript -setCache(getTodoListByDate('2022-10-01'), oldCache => { - // 返回需要缓存的数据 - return { - ...oldCache, - expire: isAfter('2022-10-01', new Date()) - }; -}); -``` - -同样的,你也可以通过 [method 匹配器](/tutorial/advanced/method-matcher) 动态查找 method 实例。 - -```javascript -setCache( - { - name: 'todoList', - filter: (method, index, ary) => { - return index < 5; - } - }, - 'newCache' -); -``` - -## 中断缓存更新 - -有时候你需要动态判断是否需要更新缓存,如果在`setCache`的回调函数中未返回数据,或返回了`undefined`,此时将不更新原缓存数据 - -```javascript -setCache(getTodoListByDate('2022-10-01'), oldCache => { - const isExpired = isAfter('2022-10-01', new Date()); - if (!isExpired) { - return; // 中断缓存更新 - } - return null; // 将缓存更新为null -}); -``` - -## 查询缓存 - -同时,我们也提供了缓存查询方法。 - -```javascript -import { queryCache } from 'alova'; - -const cacheData = queryCache(getTodoListByDate('2022-10-01')); -``` - -你也可以通过 [method 匹配器](/tutorial/advanced/method-matcher) 动态查找 method 实例。 - -```javascript -const cacheData = queryCache({ - name: 'todoList', - filter: (method, index, ary) => { - return index < 5; - } -}); -``` +--- +title: 更新与查找缓存 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +缓存也支持更新和查找,在[缓存模式](/tutorial/cache/mode)中我们提到过,每份缓存数据是以发送请求的 method 实例作为 key 进行保存的,因此在手动更新缓存时也将使用 method 实例来查找对应的缓存数据。 + +## 更新静态缓存数据 + + + + +```html + + +``` + + + + +```jsx +import { setCache } from 'alova'; +import { useState } from 'react'; + +const getTodoListByDate = dateList => + alovaInstance.Get('/todo/list/dates', { + params: { dateList } + }); + +const App = () => { + // 初始化时批量获取5天的数据 + const [dates, setDates] = useState([ + '2022-05-01', + '2022-05-02', + '2022-05-03', + '2022-05-04', + '2022-05-05' + ]); + const { + // ... + onSuccess + } = useWatcher(() => getTodoListByDate(dates.join()), [dates], { + immediate: true + }); + onSuccess(({ data: todoListDates }) => { + if (todoListDates.length <= 1) { + return; + } + + // highlight-start + // 默认情况下,这5天的数据会一起缓存到一个key中 + // 为了让后续请求某一天的数据时也能命中缓存,我们可以将5天的数据拆解为按天,并通过setCache一一手动设置响应缓存 + // setCache的第一个参数为method实例对象,它用于指定缓存的key + // 第二个参数为缓存数据 + todoListDates.forEach(todoDate => { + setCache(getTodoListByDate(todoDate.date), [todoDate]); + }); + // highlight-end + }); + + // highlight-start + const handleTodolistToggle = () => { + // 此时再在切换日期为5月1日时,它将会命中我们手动设置的响应缓存。 + // dates值正在被useWatcher监听,因此改变它就可以自动触发请求 + setDates(['2022-05-01']); + }; + // highlight-end + + return ; +}; +``` + + + + +```html + + +``` + + + + +```html + + +``` + + + + +## 动态更新缓存数据 + +你也可以在`setCache`中传入一个回调函数来动态计算缓存数据,并返回需要更新的缓存数据。 + +```javascript +setCache(getTodoListByDate('2022-10-01'), oldCache => { + // 返回需要缓存的数据 + return { + ...oldCache, + expire: isAfter('2022-10-01', new Date()) + }; +}); +``` + +同样的,你也可以通过 [method 匹配器](/tutorial/advanced/method-matcher) 动态查找 method 实例。 + +```javascript +setCache( + { + name: 'todoList', + filter: (method, index, ary) => { + return index < 5; + } + }, + 'newCache' +); +``` + +## 中断缓存更新 + +有时候你需要动态判断是否需要更新缓存,如果在`setCache`的回调函数中未返回数据,或返回了`undefined`,此时将不更新原缓存数据 + +```javascript +setCache(getTodoListByDate('2022-10-01'), oldCache => { + const isExpired = isAfter('2022-10-01', new Date()); + if (!isExpired) { + return; // 中断缓存更新 + } + return null; // 将缓存更新为null +}); +``` + +## 查询缓存 + +同时,我们也提供了缓存查询方法。 + +```javascript +import { queryCache } from 'alova'; + +const cacheData = queryCache(getTodoListByDate('2022-10-01')); +``` + +你也可以通过 [method 匹配器](/tutorial/advanced/method-matcher) 动态查找 method 实例。 + +```javascript +const cacheData = queryCache({ + name: 'todoList', + filter: (method, index, ary) => { + return index < 5; + } +}); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/06-controlled-cache.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/06-controlled-cache.md index 37e6c3ed3..5af127be0 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/06-controlled-cache.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/06-controlled-cache.md @@ -1,83 +1,82 @@ ---- -title: 受控的缓存 -sidebar_position: 60 ---- - -:::info 版本要求 - -v2.1.0+ - -::: - -在发送一个请求时,默认会先检查是否存在匹配的缓存数据,匹配到则会使用它作为响应数据进行返回,如果在一些场景下,用户需要使用自定义的缓存就必须先使用`setCache`同步设置缓存数据才能行得通,无疑加大了用户的负担,这是一种不受控的缓存。 - -如果你想要在不受控的缓存下使用**IndexedDB**自定义管理缓存数据,你可能会先为即将发送的请求预先设置命中的缓存,像这样: - -```javascript -const getFile = fileName => { - const fileGetter = alovaInstance.GET(`/file/${fileName}`); - const tx = db.transaction(['files']); - const getRequest = tx.objectStore('files').get(fileName); - getRequest.onsuccess = ({ result }) => { - setCache(fileGetter, result); - }; - return fileGetter; -}; -``` - -**❌ 但并不推荐以上的写法**,原因如下: - -1. 每次调用`getFile`都会设置一次缓存,但 fileGetter 不一定用于发送请求; -2. IndexedDB 是异步接口,如果匹配缓存的步骤发生在 IndexedDB 触发 onsuccess 之前,那么就不会匹配到缓存数据,它们的顺序是不可预知的; -3. 自定义的缓存管理任务和 method 是分开的,但实际上它们应该聚合在一起; - -在这种情况下,你可以使用受控的缓存来解决上面的问题,使用受控缓存也很简单,可以在 method 中的 localCache 设置为异步或同步函数,在这个函数中返回自定义数据作为命中的缓存数据。 - -```javascript -const getFile = fileName => - alovaInstance.GET(`/file/${fileName}`, { - // 受控缓存函数支持异步和同步函数 - localCache() { - return new Promise((resolve, reject) => { - const tx = db.transaction(['files']); - const getRequest = tx.objectStore('files').get(fileName); - getRequest.onsuccess = resolve; - getRequest.onerror = reject; - }); - } - }); -``` - -## 不使用缓存 - -如果你希望继续发送请求,可以在`localCache`中返回 `undefined` 或不返回任何数据,这在自定义管理缓存时未命中缓存的情况下很有用。 - -## 使用 transformData 设置缓存 - -由于 `transformData` 函数具有以下两个特性: - -- 只有在响应时才被触发,而命中响应缓存时不会触发; -- 支持异步函数; - -因此,你还可以配合它保存自定义的缓存,例如以文件为响应数据的缓存场景下,可以配合 IndexedDB 进行文件数据的缓存。 - -```javascript -const fileGetter = alovaInstance.Get('/file/file_name', { - // 使用IndexedDB缓存文件 - async transformData(fileBlob) { - await new Promise((resolve, reject) => { - const tx = db.transaction(['files'], 'readwrite'); - const putRequest = tx.objectStore('files').put({ - file: fileBlob - }); - putRequest.onsuccess = resolve; - putRequest.onerror = reject; - }); - return fileBlob; - } -}); -``` - -## 注意事项 - -在 usehooks 中使用时,在`localCache`函数中抛出错误将会触发`onError`,使用 method 实例直接发起请求时,将会返回一个 reject 状态的 promise 实例。 +--- +title: 受控的缓存 +--- + +:::info 版本要求 + +v2.1.0+ + +::: + +在发送一个请求时,默认会先检查是否存在匹配的缓存数据,匹配到则会使用它作为响应数据进行返回,如果在一些场景下,用户需要使用自定义的缓存就必须先使用`setCache`同步设置缓存数据才能行得通,无疑加大了用户的负担,这是一种不受控的缓存。 + +如果你想要在不受控的缓存下使用**IndexedDB**自定义管理缓存数据,你可能会先为即将发送的请求预先设置命中的缓存,像这样: + +```javascript +const getFile = fileName => { + const fileGetter = alovaInstance.GET(`/file/${fileName}`); + const tx = db.transaction(['files']); + const getRequest = tx.objectStore('files').get(fileName); + getRequest.onsuccess = ({ result }) => { + setCache(fileGetter, result); + }; + return fileGetter; +}; +``` + +**❌ 但并不推荐以上的写法**,原因如下: + +1. 每次调用`getFile`都会设置一次缓存,但 fileGetter 不一定用于发送请求; +2. IndexedDB 是异步接口,如果匹配缓存的步骤发生在 IndexedDB 触发 onsuccess 之前,那么就不会匹配到缓存数据,它们的顺序是不可预知的; +3. 自定义的缓存管理任务和 method 是分开的,但实际上它们应该聚合在一起; + +在这种情况下,你可以使用受控的缓存来解决上面的问题,使用受控缓存也很简单,可以在 method 中的 localCache 设置为异步或同步函数,在这个函数中返回自定义数据作为命中的缓存数据。 + +```javascript +const getFile = fileName => + alovaInstance.GET(`/file/${fileName}`, { + // 受控缓存函数支持异步和同步函数 + localCache() { + return new Promise((resolve, reject) => { + const tx = db.transaction(['files']); + const getRequest = tx.objectStore('files').get(fileName); + getRequest.onsuccess = resolve; + getRequest.onerror = reject; + }); + } + }); +``` + +## 不使用缓存 + +如果你希望继续发送请求,可以在`localCache`中返回 `undefined` 或不返回任何数据,这在自定义管理缓存时未命中缓存的情况下很有用。 + +## 使用 transformData 设置缓存 + +由于 `transformData` 函数具有以下两个特性: + +- 只有在响应时才被触发,而命中响应缓存时不会触发; +- 支持异步函数; + +因此,你还可以配合它保存自定义的缓存,例如以文件为响应数据的缓存场景下,可以配合 IndexedDB 进行文件数据的缓存。 + +```javascript +const fileGetter = alovaInstance.Get('/file/file_name', { + // 使用IndexedDB缓存文件 + async transformData(fileBlob) { + await new Promise((resolve, reject) => { + const tx = db.transaction(['files'], 'readwrite'); + const putRequest = tx.objectStore('files').put({ + file: fileBlob + }); + putRequest.onsuccess = resolve; + putRequest.onerror = reject; + }); + return fileBlob; + } +}); +``` + +## 注意事项 + +在 usehooks 中使用时,在`localCache`函数中抛出错误将会触发`onError`,使用 method 实例直接发起请求时,将会返回一个 reject 状态的 promise 实例。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/02-virtual-data.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/02-virtual-data.md index 96dbcef9e..9c584444c 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/02-virtual-data.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/02-virtual-data.md @@ -1,177 +1,176 @@ ---- -title: 虚拟数据 -sidebar_position: 20 ---- - -实际上,虚拟数据是一个拥有唯一 id 的引用对象,其追查机制就是通过先生成虚拟数据 id 和响应数据之间的映射,再通过虚拟数据 id 查找并替换为实际值来实现的。 - -当原始值为引用类型时,表现和原始值相同,但基本类型的虚拟数据使用的是`Number, String, Boolean`封装类,以及自定义的`Undefined, Null`封装类,它们的表现形式与原始值有一些偏差,以下列出了虚拟数据的特性,以及虚拟数据的辅助函数的使用,辅助函数详情将在章节末尾介绍。 - -## 字符串拼接 - -当虚拟数据进行字符串拼接时,将被转换为虚拟数据 id 进行拼接。 - -```javascript -const virtualData = createVirtualData(1); -'a' + virtualData; // a[vd:xxxxxx] -1 + virtualData; // 1[vd:xxxxxx] -``` - -## 数据比较 - -虚拟数据无法直接用于比较,但实际场景中经常使用虚拟数据和实际数据混合比较,此时可使用`equals`比较。 - -```javascript -import { equals } from '@alova/scene-*'; - -equals('a', 'a'); // true - -const virtualData1 = createVirtualData(1); -const virtualData2 = virtualData1.clone(); // 克隆虚拟数据 -equals(virtualData1, virtualData2); // virtualData1和virtualData2的id相同时为true -equals(virtualData1, '[vd:xxxxxx]'); // virtualData1的id也为[vd:xxxxxx]时为true -``` - -## 参与运算 - -参与运算如`+-*/%`,数值比较、以及位运算时,无法自动转换为原始值,可通过`dehydrateVData`转换为原始值再进行计算。 - -```javascript -import { dehydrateVData } from '@alova/scene-*'; - -const virtualData = createVirtualData(1); -dehydrateVData(virtualData) + 1; // 2 -dehydrateVData(virtualData) > 0; // true -``` - -## 类型操作符 - -因为虚拟数据在基本数据类型上使用封装类实现,使用`typeof`获取类型时将始终会返回`object`,也可通过`dehydrateVData`转换为原始值再获取类型 - -```javascript -const vNum = createVirtualResponse(1); -typeof vNum === 'object'; // true -const vUndef = createVirtualResponse(undefined); -typeof vUndef === 'object'; // true - -typeof dehydrateVData(vNum) === 'number'; // true -typeof dehydrateVData(vUndef) === 'undefined'; // true -``` - -为了解决这个问题,你可以使用虚拟数据辅助函数**dehydrateVData**,它可以获取一个虚拟数据的原始值,当遇到非虚拟数据时将原样返回 - -```javascript -const vNum = createVirtualResponse(1); -typeof dehydrateVData(vNum) === 'number'; // true -dehydrateVData(vNum) === 1; // true -dehydrateVData('string') === 'string'; // true -``` - -## 视图展示 - -默认情况下,虚拟数据展示到视图中时将隐式调用`toString`,但有时候也会遇到显示错乱的问题,以及在**react**中渲染一个对象将会报以下错误: - -``` -Uncaught Error: Objects are not valid as a React child (found: object with keys {}). If you meant to render a collection of children, use an array instead. -``` - -因此建议在显示时使用`dehydrateVData`转换为原始数据进行展示。 - -## 虚拟数据辅助函数 - -### dehydrateVData - -将虚拟数据脱水,返回它的原始值,如 target 为非虚拟数据则原样返回。 - -```typescript -// type -function dehydrateVData(target: any): any; - -// example -dehydrateVData(1); // 1 -const virtualData = createVirtualData(1); -dehydrateVData(virtualData); // 1 -``` - -### stringifyVData - -虚拟数据字符串化,返回虚拟数据 id,当 returnOriginalIfNotVData 设置为 false 时,非虚拟数据将原样返回。 - -```typescript -// type -function stringifyVData(target: any, returnOriginalIfNotVData?: boolean): any; - -// example -stringifyVData(1); // 1 -stringifyVData(1, false); // undefined - -const virtualData = createVirtualData(1); -stringifyVData(virtualData); // [vd:xxxxxx] -``` - -### equals - -以兼容虚拟数据的方式判断两个值是否相等,当没有虚拟数据参与比较时将严格对比,否则将对比虚拟数据 id 是否相同,如果比较数据可能存在虚拟数据参与,则建议使用此函数比较。 - -```typescript -// type -function equals(prevValue: any, nextValue: any): boolean; - -// example -equals('a', 'a'); // true -const virtualData1 = createVirtualData(1); -const virtualData2 = virtualData1.clone(); // 克隆虚拟数据 -equals(virtualData1, virtualData2); // virtualData1和virtualData2的id相同时为true -equals(virtualData1, '[vd:xxxxxx]'); // virtualData1的id也为[vd:xxxxxx]时为true -``` - -### isVData - -判断目标数据是否为虚拟数据 - -```typescript -// type -function isVData(target: any): boolean; - -// example -isVData(1); // false -const virtualData = createVirtualData(1); -isVData(virtualData); // true -isVData('[vd:xxxxxx]'); //true -``` - -## 虚拟数据的替换限制 - -虚拟数据的追查机制只能深度遍历相关数据,然后将具有虚拟数据标识的数据替换为实际,如果一些数据依赖虚拟数据生成的,在虚拟数据替换为实际数据后不会重新计算。 - -以下情况,即使 virtualId 替换为实际数据了,这个请求的 id 也不会重新再计算,因此如果需要替换,需要直接将 virtualId 作为请求参数,示例如下: - -```javascript -const deleteTodo = virtualId => { - return alova.Delete('/deleteTodo', { - id: dehydrateVData(virtualId) === null ? 1 : 2 - }); -}; -``` - -但如果将虚拟数据作为字符串拼接时,它将自动转换为虚拟数据 id 参与字符串拼接,这种情况将会有效。以下情况,请求 id 在开始时的值为`id_[vd:xxxxxx]`,当 virtualId 被替换为响应值后(假设替换为 1),它将自动更新为`id_1`。 - -```javascript -const deleteTodo = virtualId => { - return alova.Delete('/deleteTodo', { - id: 'id'_virtualId - }); -}; -``` - -## 接下来 - -虽然实现无感交互已经足够简单了,但相比保守请求还是有一些额外的处理,具体实现大致分为以下几个步骤。 - -1. 以保守请求方式实现功能; -2. 手动更新列表数据,实现本地化数据补偿; -3. 记录数据操作,以便在还有未提交修改时手动补充到最新记录; -4. 将未提交的数据手动补偿到列表,以便即使数据未提交也能展示最新状态; -5. 修改未提交数据时,拦截带虚拟数据的请求; - -接下来,我们将以一个简单的示例进行演示。 +--- +title: 虚拟数据 +--- + +实际上,虚拟数据是一个拥有唯一 id 的引用对象,其追查机制就是通过先生成虚拟数据 id 和响应数据之间的映射,再通过虚拟数据 id 查找并替换为实际值来实现的。 + +当原始值为引用类型时,表现和原始值相同,但基本类型的虚拟数据使用的是`Number, String, Boolean`封装类,以及自定义的`Undefined, Null`封装类,它们的表现形式与原始值有一些偏差,以下列出了虚拟数据的特性,以及虚拟数据的辅助函数的使用,辅助函数详情将在章节末尾介绍。 + +## 字符串拼接 + +当虚拟数据进行字符串拼接时,将被转换为虚拟数据 id 进行拼接。 + +```javascript +const virtualData = createVirtualData(1); +'a' + virtualData; // a[vd:xxxxxx] +1 + virtualData; // 1[vd:xxxxxx] +``` + +## 数据比较 + +虚拟数据无法直接用于比较,但实际场景中经常使用虚拟数据和实际数据混合比较,此时可使用`equals`比较。 + +```javascript +import { equals } from '@alova/scene-*'; + +equals('a', 'a'); // true + +const virtualData1 = createVirtualData(1); +const virtualData2 = virtualData1.clone(); // 克隆虚拟数据 +equals(virtualData1, virtualData2); // virtualData1和virtualData2的id相同时为true +equals(virtualData1, '[vd:xxxxxx]'); // virtualData1的id也为[vd:xxxxxx]时为true +``` + +## 参与运算 + +参与运算如`+-*/%`,数值比较、以及位运算时,无法自动转换为原始值,可通过`dehydrateVData`转换为原始值再进行计算。 + +```javascript +import { dehydrateVData } from '@alova/scene-*'; + +const virtualData = createVirtualData(1); +dehydrateVData(virtualData) + 1; // 2 +dehydrateVData(virtualData) > 0; // true +``` + +## 类型操作符 + +因为虚拟数据在基本数据类型上使用封装类实现,使用`typeof`获取类型时将始终会返回`object`,也可通过`dehydrateVData`转换为原始值再获取类型 + +```javascript +const vNum = createVirtualResponse(1); +typeof vNum === 'object'; // true +const vUndef = createVirtualResponse(undefined); +typeof vUndef === 'object'; // true + +typeof dehydrateVData(vNum) === 'number'; // true +typeof dehydrateVData(vUndef) === 'undefined'; // true +``` + +为了解决这个问题,你可以使用虚拟数据辅助函数**dehydrateVData**,它可以获取一个虚拟数据的原始值,当遇到非虚拟数据时将原样返回 + +```javascript +const vNum = createVirtualResponse(1); +typeof dehydrateVData(vNum) === 'number'; // true +dehydrateVData(vNum) === 1; // true +dehydrateVData('string') === 'string'; // true +``` + +## 视图展示 + +默认情况下,虚拟数据展示到视图中时将隐式调用`toString`,但有时候也会遇到显示错乱的问题,以及在**react**中渲染一个对象将会报以下错误: + +``` +Uncaught Error: Objects are not valid as a React child (found: object with keys {}). If you meant to render a collection of children, use an array instead. +``` + +因此建议在显示时使用`dehydrateVData`转换为原始数据进行展示。 + +## 虚拟数据辅助函数 + +### dehydrateVData + +将虚拟数据脱水,返回它的原始值,如 target 为非虚拟数据则原样返回。 + +```typescript +// type +function dehydrateVData(target: any): any; + +// example +dehydrateVData(1); // 1 +const virtualData = createVirtualData(1); +dehydrateVData(virtualData); // 1 +``` + +### stringifyVData + +虚拟数据字符串化,返回虚拟数据 id,当 returnOriginalIfNotVData 设置为 false 时,非虚拟数据将原样返回。 + +```typescript +// type +function stringifyVData(target: any, returnOriginalIfNotVData?: boolean): any; + +// example +stringifyVData(1); // 1 +stringifyVData(1, false); // undefined + +const virtualData = createVirtualData(1); +stringifyVData(virtualData); // [vd:xxxxxx] +``` + +### equals + +以兼容虚拟数据的方式判断两个值是否相等,当没有虚拟数据参与比较时将严格对比,否则将对比虚拟数据 id 是否相同,如果比较数据可能存在虚拟数据参与,则建议使用此函数比较。 + +```typescript +// type +function equals(prevValue: any, nextValue: any): boolean; + +// example +equals('a', 'a'); // true +const virtualData1 = createVirtualData(1); +const virtualData2 = virtualData1.clone(); // 克隆虚拟数据 +equals(virtualData1, virtualData2); // virtualData1和virtualData2的id相同时为true +equals(virtualData1, '[vd:xxxxxx]'); // virtualData1的id也为[vd:xxxxxx]时为true +``` + +### isVData + +判断目标数据是否为虚拟数据 + +```typescript +// type +function isVData(target: any): boolean; + +// example +isVData(1); // false +const virtualData = createVirtualData(1); +isVData(virtualData); // true +isVData('[vd:xxxxxx]'); //true +``` + +## 虚拟数据的替换限制 + +虚拟数据的追查机制只能深度遍历相关数据,然后将具有虚拟数据标识的数据替换为实际,如果一些数据依赖虚拟数据生成的,在虚拟数据替换为实际数据后不会重新计算。 + +以下情况,即使 virtualId 替换为实际数据了,这个请求的 id 也不会重新再计算,因此如果需要替换,需要直接将 virtualId 作为请求参数,示例如下: + +```javascript +const deleteTodo = virtualId => { + return alova.Delete('/deleteTodo', { + id: dehydrateVData(virtualId) === null ? 1 : 2 + }); +}; +``` + +但如果将虚拟数据作为字符串拼接时,它将自动转换为虚拟数据 id 参与字符串拼接,这种情况将会有效。以下情况,请求 id 在开始时的值为`id_[vd:xxxxxx]`,当 virtualId 被替换为响应值后(假设替换为 1),它将自动更新为`id_1`。 + +```javascript +const deleteTodo = virtualId => { + return alova.Delete('/deleteTodo', { + id: 'id'_virtualId + }); +}; +``` + +## 接下来 + +虽然实现无感交互已经足够简单了,但相比保守请求还是有一些额外的处理,具体实现大致分为以下几个步骤。 + +1. 以保守请求方式实现功能; +2. 手动更新列表数据,实现本地化数据补偿; +3. 记录数据操作,以便在还有未提交修改时手动补充到最新记录; +4. 将未提交的数据手动补偿到列表,以便即使数据未提交也能展示最新状态; +5. 修改未提交数据时,拦截带虚拟数据的请求; + +接下来,我们将以一个简单的示例进行演示。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/03-start-silent-factory.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/03-start-silent-factory.md index bbf0b802b..e219a49c2 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/03-start-silent-factory.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/03-start-silent-factory.md @@ -1,107 +1,106 @@ ---- -title: 启动静默工厂 -sidebar_position: 30 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -我们将所有场景化的请求策略放在了叫做`@alova/scene-*`的 js 包中,使用前你需要先安装它。 - - - - -```bash -# npm -npm install @alova/scene-vue --save -# yarn -yarn add @alova/scene-vue - -``` - - - - -```bash -# npm -npm install @alova/scene-react --save -# yarn -yarn add @alova/scene-react - -``` - - - - - -```bash -# npm -npm install @alova/scene-svelte --save -# yarn -yarn add @alova/scene-svelte - -``` - - - - -静默队列默认不启动,需要我们指定启动参数进行初始化,一般情况下,在入口文件中调用`bootSilentFactory`来初始化静默工厂,它将通过指定的配置项来读取还未执行的请求到对应的静默队列中并启动这些队列。 - - - - -```javascript -import { bootSilentFactory } from '@alova/scene-vue'; -import { alovaInst } from '@/api'; - -bootSilentFactory({ - // 启动时指定 alova 实例,用于请求信息存储、请求发送 - alova: alovaInst, - - // 延迟启动的时间,单位毫秒,默认为2000ms,具体描述看后续说明 - delay: 1000 -}); -``` - - - - - -```javascript -import { bootSilentFactory } from '@alova/scene-react'; -import { alovaInst } from '@/api'; - -bootSilentFactory({ - // 启动时指定 alova 实例,用于请求信息存储、请求发送 - alova: alovaInst, - - // 延迟启动的时间,单位毫秒,默认为2000ms,具体描述看后续说明 - delay: 1000 -}); -``` - - - - - -```javascript -import { bootSilentFactory } from '@alova/scene-svelte'; -import { alovaInst } from '@/api'; - -bootSilentFactory({ - // 启动时指定 alova 实例,用于请求信息存储、请求发送 - alova: alovaInst, - - // 延迟启动的时间,单位毫秒,默认为2000ms,具体描述看后续说明 - delay: 1000 -}); -``` - - - - -:::warning `delay`参数说明 - -在实际场景下,进入当前页面时也会发送请求加载页面数据,为了保证用户可以更快地看到页面数据,需要将加载数据的请求前置到队列起始位置,否则可能会造成加载数据的请求后置到队列尾部,此时就需要等到前面的所有请求完成才会加载页面数据,这显然是不合适的,因此通过延迟一段时间初始化来让加载数据的请求先进入队列,达到“插队”的效果,具体的延迟时间需要根据页面渲染所需的时间而定。 - -::: +--- +title: 启动静默工厂 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +我们将所有场景化的请求策略放在了叫做`@alova/scene-*`的 js 包中,使用前你需要先安装它。 + + + + +```bash +# npm +npm install @alova/scene-vue --save +# yarn +yarn add @alova/scene-vue + +``` + + + + +```bash +# npm +npm install @alova/scene-react --save +# yarn +yarn add @alova/scene-react + +``` + + + + + +```bash +# npm +npm install @alova/scene-svelte --save +# yarn +yarn add @alova/scene-svelte + +``` + + + + +静默队列默认不启动,需要我们指定启动参数进行初始化,一般情况下,在入口文件中调用`bootSilentFactory`来初始化静默工厂,它将通过指定的配置项来读取还未执行的请求到对应的静默队列中并启动这些队列。 + + + + +```javascript +import { bootSilentFactory } from '@alova/scene-vue'; +import { alovaInst } from '@/api'; + +bootSilentFactory({ + // 启动时指定 alova 实例,用于请求信息存储、请求发送 + alova: alovaInst, + + // 延迟启动的时间,单位毫秒,默认为2000ms,具体描述看后续说明 + delay: 1000 +}); +``` + + + + + +```javascript +import { bootSilentFactory } from '@alova/scene-react'; +import { alovaInst } from '@/api'; + +bootSilentFactory({ + // 启动时指定 alova 实例,用于请求信息存储、请求发送 + alova: alovaInst, + + // 延迟启动的时间,单位毫秒,默认为2000ms,具体描述看后续说明 + delay: 1000 +}); +``` + + + + + +```javascript +import { bootSilentFactory } from '@alova/scene-svelte'; +import { alovaInst } from '@/api'; + +bootSilentFactory({ + // 启动时指定 alova 实例,用于请求信息存储、请求发送 + alova: alovaInst, + + // 延迟启动的时间,单位毫秒,默认为2000ms,具体描述看后续说明 + delay: 1000 +}); +``` + + + + +:::warning `delay`参数说明 + +在实际场景下,进入当前页面时也会发送请求加载页面数据,为了保证用户可以更快地看到页面数据,需要将加载数据的请求前置到队列起始位置,否则可能会造成加载数据的请求后置到队列尾部,此时就需要等到前面的所有请求完成才会加载页面数据,这显然是不合适的,因此通过延迟一段时间初始化来让加载数据的请求先进入队列,达到“插队”的效果,具体的延迟时间需要根据页面渲染所需的时间而定。 + +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/04-conservative-request.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/04-conservative-request.md index c52f7e7c2..04c284fbe 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/04-conservative-request.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/04-conservative-request.md @@ -1,136 +1,135 @@ ---- -title: 步骤1-以保守请求实现功能 -sidebar_position: 40 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -以 Todo 管理为示例,来实现 Todo 在无感交互模式下的创建、编辑、删除等功能,在接下来的章节中将提供请求相关的关键代码。 - -> 这里的[简单列表页示例](/tutorial/example/silent-submit-simple-list)包含了完整的代码,你可以进入体验。 - -在`@alova/scene-*`js 包中,将使用 **useSQRequest** 来替代 alova 提供的 **useRequest**,接下来先以最常见的保守请求模式来实现,再一步一步处理无感交互模式的兼容性。 - -## 创建 alova 实例和相关 method - -```javascript title="api.js" -import { createAlova } from 'alova'; - -export const alovaInst = createAlova({ - /*...*/ -}); - -/** 加载todo列表 */ -const todoList = () => alovaInst.Get('/todo'); - -/** 加载todo详情 */ -const todoDetail = id => - alovaInst.Get('/todo', { - params: { id } - }); - -/** 创建、编辑todo项 */ -const createOrEditTodo = (data, id) => - alovaInst.Post('/todo', { - data, - id - }); - -/** 删除todo项 */ -const deleteTodo = id => alovaInst.Delete('/todo', { id }); -``` - -## 启动静默工厂 - -```javascript title="main.js" -import { bootSilentFactory } from '@alova/scene-*'; -import { alovaInst } from './api.js'; - -bootSilentFactory({ - alova: alovaInst -}); -``` - -## 加载 Todo 列表 - -以最简单的方式加载并展示页面数据 - -```javascript -import { useSQRequest } from '@alova/scene-vue'; -import { todoList } from './api.js'; -const { data, loading, error } = useSQRequest(todoList, { - initialData: [] -}); -``` - -## 进入 Todo 创建/编辑页 - -创建 todo 项时,id 为空,不发送详情获取请求,编辑 todo 项时,id 有值,将获取详情数据。 - -```javascript -import { useSQRequest } from '@alova/scene-*'; -import { todoDetail } from './api.js'; - -const id = /* todo id */; -const { loading, data } = useSQRequest(() => todoDetail(id), { - initialData: { - title: '', - time: new Date() - }, - immediate: !!id -}); -``` - -## 创建/编辑 Todo 项 - -通过提交事件触发请求,提交成功后调用 fetch 重新拉取最新的列表数据,界面将自动展示最新数据。 - -```javascript -import { useFetcher } from 'alova'; -import { useSQRequest } from '@alova/scene-*'; -import { createOrEditTodo, todoList } from './api.js'; - -const id = /* todo id */; -const { loading, data, send, onSuccess } = useSQRequest(createOrEditTodo, { - immediate: false, -}); - -const { fetch } = useFetcher(); -onSuccess(() => { - // 重新拉取列表数据 - fetch(todoList); -}) - -// 提交事件回调函数 -const handleSubmit = newData => { - send(newData, id); -}; - -``` - -## 删除 Todo 项 - -通过 id 删除对应的 todo 项 - -```javascript -import { useSQRequest } from '@alova/scene-*'; -import { deleteTodo, todoList } from './api.js'; - -const { loading, data, send, onSuccess } = useSQRequest(deleteTodo, { - immediate: false -}); - -const { fetch } = useFetcher(); -onSuccess(() => { - // 重新拉取列表数据 - fetch(todoList); -}); - -// 事件回调触发删除请求 -const handleDelete = deletingId => { - send(deletingId); -}; -``` - -至此,一个简单的 Todo 列表管理的相关请求功能就完成了,接下来我们将开始对它进行改造,来兼容无感交互模式。 +--- +title: 步骤1-以保守请求实现功能 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +以 Todo 管理为示例,来实现 Todo 在无感交互模式下的创建、编辑、删除等功能,在接下来的章节中将提供请求相关的关键代码。 + +> 这里的[简单列表页示例](/tutorial/example/silent-submit-simple-list)包含了完整的代码,你可以进入体验。 + +在`@alova/scene-*`js 包中,将使用 **useSQRequest** 来替代 alova 提供的 **useRequest**,接下来先以最常见的保守请求模式来实现,再一步一步处理无感交互模式的兼容性。 + +## 创建 alova 实例和相关 method + +```javascript title="api.js" +import { createAlova } from 'alova'; + +export const alovaInst = createAlova({ + /*...*/ +}); + +/** 加载todo列表 */ +const todoList = () => alovaInst.Get('/todo'); + +/** 加载todo详情 */ +const todoDetail = id => + alovaInst.Get('/todo', { + params: { id } + }); + +/** 创建、编辑todo项 */ +const createOrEditTodo = (data, id) => + alovaInst.Post('/todo', { + data, + id + }); + +/** 删除todo项 */ +const deleteTodo = id => alovaInst.Delete('/todo', { id }); +``` + +## 启动静默工厂 + +```javascript title="main.js" +import { bootSilentFactory } from '@alova/scene-*'; +import { alovaInst } from './api.js'; + +bootSilentFactory({ + alova: alovaInst +}); +``` + +## 加载 Todo 列表 + +以最简单的方式加载并展示页面数据 + +```javascript +import { useSQRequest } from '@alova/scene-vue'; +import { todoList } from './api.js'; +const { data, loading, error } = useSQRequest(todoList, { + initialData: [] +}); +``` + +## 进入 Todo 创建/编辑页 + +创建 todo 项时,id 为空,不发送详情获取请求,编辑 todo 项时,id 有值,将获取详情数据。 + +```javascript +import { useSQRequest } from '@alova/scene-*'; +import { todoDetail } from './api.js'; + +const id = /* todo id */; +const { loading, data } = useSQRequest(() => todoDetail(id), { + initialData: { + title: '', + time: new Date() + }, + immediate: !!id +}); +``` + +## 创建/编辑 Todo 项 + +通过提交事件触发请求,提交成功后调用 fetch 重新拉取最新的列表数据,界面将自动展示最新数据。 + +```javascript +import { useFetcher } from 'alova'; +import { useSQRequest } from '@alova/scene-*'; +import { createOrEditTodo, todoList } from './api.js'; + +const id = /* todo id */; +const { loading, data, send, onSuccess } = useSQRequest(createOrEditTodo, { + immediate: false, +}); + +const { fetch } = useFetcher(); +onSuccess(() => { + // 重新拉取列表数据 + fetch(todoList); +}) + +// 提交事件回调函数 +const handleSubmit = newData => { + send(newData, id); +}; + +``` + +## 删除 Todo 项 + +通过 id 删除对应的 todo 项 + +```javascript +import { useSQRequest } from '@alova/scene-*'; +import { deleteTodo, todoList } from './api.js'; + +const { loading, data, send, onSuccess } = useSQRequest(deleteTodo, { + immediate: false +}); + +const { fetch } = useFetcher(); +onSuccess(() => { + // 重新拉取列表数据 + fetch(todoList); +}); + +// 事件回调触发删除请求 +const handleDelete = deletingId => { + send(deletingId); +}; +``` + +至此,一个简单的 Todo 列表管理的相关请求功能就完成了,接下来我们将开始对它进行改造,来兼容无感交互模式。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/05-modify-response.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/05-modify-response.md index 83d6f4c77..861049393 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/05-modify-response.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/05-modify-response.md @@ -1,258 +1,259 @@ ---- -title: 步骤2-调整响应处理 -sidebar_position: 50 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -在上一节的保守请求示例中,我们在 Todo 项的创建、编辑和删除请求响应后调用`fetch`重新拉取数据刷新的页面,为了能在操作后立即展示结果,我们需要进行以下调整: - -1. 将创建、编辑和删除请求的行为模式设置为`silent`,它们将会在请求时立即触发成功回调; -2. 手动更新列表,而不是拉取数据,使用虚拟数据占位服务端的响应数据; -3. 保存操作记录,用于在刷新页面时进行数据补偿; - -## 设置行为模式 - -通过配置参数`behavior`进行设置,可选参数为`queue`、`silent`、`static`,或者一个返回行为数据的函数实现动态设置行为模式,默认为`queue`。 - -以下为静态设置 behavior 参数。 - -```javascript -useSQRequest(createOrEditTodo, { - // highlight-start - behavior: 'silent', - // highlight-end - immediate: false -}); -``` - -以下为动态设置 behavior 参数。 - -```javascript -const { send } = useSQRequest(createOrEditTodo, { - // highlight-start - // arg参数可通过send函数传入 - behavior: arg => { - if (arg === 0) return 'silent'; - return 'queue'; - }, - // highlight-end - immediate: false -}); -``` - -> 当 behavior 设置为函数时,它将在每次发起请求时被调用,来确定本次请求以哪种行为来处理。 - -## 静默队列说明 - -将 behavior 参数设置为`queue`或`silent`后,请求将进入静默队列等待发起请求,默认情况下它们将进入名称为`default`的队列,你还可以指定其他队列来保存 silentMethod 实例,队列之间互不干扰。 - -```javascript -useSQRequest(createOrEditTodo, { - // highlight-start - // 指定请求信息进入名称为queue-2的队列中 - queue: 'queue-2', - // highlight-end - behavior: 'silent', - immediate: false -}); -``` - -## 在回调中手动更新列表 - -### 在新增/编辑后更新列表 - - - - -当列表页未被销毁,例如在当前页使用模态框操作,或使用了``(Vue)保留了页面组件,数据还会存在,此时我们使用**updateStateEffect**来更新列表数据,相比于 alova 导出的**updateState**,它具有追踪虚拟数据追踪功能,当获取到响应数据后,它将自动追踪列表数据内的虚拟数据,并替换为实际数据。 - -```javascript -import { useSQRequest, updateStateEffect } from '@alova/scene-*'; -import { createOrEditTodo, todoList } from './api.js'; - -const { onSuccess } = useSQRequest(createOrEditTodo, { - behavior: 'silent', - immediate: false, - - // highlight-start - // 在处理列表更新前,需要根据响应数据的结构先构造相同结构的虚拟响应数据 - // 例如,在创建 Todo 项时将返回这条数据的 id。 - silentDefaultResponse: () => { - return { - id: '--' - }; - } - // highlight-end -}); - -// highlight-start -onSuccess(({ data, silentMethod }) => { - // 构造列表数据项 - const editingItem = { - ...detail, - - // 当编辑时,使用原id,否则使用响应数据内的id - // 在静默提交时,data.id为虚拟数据,在static行为模式时,data.id为实际的id值 - id: id || data.id - }; - - // 使用updateStateEffect,而不是updateState - updateStateEffect(todoList(), todoListRaw => { - if (id) { - todoListRaw = todoListRaw.map(item => (item.id === id ? editingItem : item)); - } else { - todoListRaw.unshift(editingItem); - } - return todoListRaw; - }); -}); -// highlight-end -``` - -> updateStateEffect 的使用方法与[updateState](/tutorial/advanced/update-across-components)一致 - - - - -当列表页已被销毁,数据已被释放,例如跳转到了新页面,此时使用**setCache**更新缓存数据,当返回列表页时将重新发起请求并命中更新的缓存。 - -```javascript -import { useSQRequest, setCache, equals } from '@alova/scene-*'; -import { createOrEditTodo, todoList } from './api.js'; - -const urlParams = new URLSearchParams(window.location.search); -const id = urlParams.get('id') || ''; -const { onSuccess } = useSQRequest(createOrEditTodo, { - behavior: 'silent', - immediate: false, - - // highlight-start - // 在处理列表更新前,需要根据响应数据的结构先构造相同结构的虚拟响应数据 - // 例如,在创建 Todo 项时将返回这条数据的 id。 - silentDefaultResponse: () => { - return { - id: '--' - }; - } - // highlight-end -}); -// highlight-start -onSuccess(({ data, silentMethod }) => { - // 构造列表数据项 - const editingItem = { - ...detail, - - // 当编辑时,使用原id,否则使用响应数据内的id - // 在静默提交时,data.id为虚拟数据,在static行为模式时,data.id为实际的id值 - id: id || data.id - }; - - const methodTodoList = todoList(); - setCache(methodTodoList, todoListRaw => { - if (id) { - todoListRaw = todoListRaw.map(item => (equals(item.id, id) ? editingItem : item)); - } else { - todoListRaw.unshift(editingItem); - } - return todoListRaw; - }); - // 调用setUpdateState设置响应数据追踪,这样就实现了和updateStateEffect相同的延迟更新效果 - if (silentMethod) { - silentMethod.setUpdateState(methodTodoList); - silentMethod.save(); - } -}); -// highlight-end -``` - - - - -### 移除后更新列表 - -```javascript -import { useSQRequest, updateStateEffect } from '@alova/scene-*'; -import { deleteTodo, todoList } from './api.js'; - -const { loading, data, send, onSuccess } = useSQRequest(deleteTodo, { - immediate: false, - // highlight-start - behavior: 'silent' - // highlight-end -}); - -onSuccess(({ sendArgs: [deletingId] }) => { - updateStateEffect(todoList(), todoListRaw => todoListRaw.filter(item => item.id !== deletingId)); -}); - -// 事件回调触发删除请求 -const handleDelete = deletingId => { - send(deletingId); -}; -``` - -## 保存操作记录 - -仅仅完成手动更新列表还不够,我们还需要考虑当网络恢复后,当请求队列还存等待中的请求时,此时加载的列表数据还不包括未提交请求的部分,这会让用户造成一定的困惑: - -> “我明明已经新增了多条数据,为什么列表中没有?” - -因此我们需要在成功回调中记录操作,以及相关数据,以便再次加载列表数据时,将未提交的数据手动补偿到列表中,从而让列表数据始终保持最新状态。 - -保存操作记录也很简单,只需要将相关数据挂载到 silentMethod 实例上,它将随着实例一同被持久化。 - -### 创建/编辑成功回调 - -```javascript -// ... -onSuccess(({ silentMethod }) => { - // 构造列表数据项 - const editingItem = { - ...detail, - id: id || data.id - }; - // ... - // highlight-start - if (silentMethod) { - // 设置名称,以便后续查询 - // 如果editingItem.id是虚拟数据将自动转换为它的id - silentMethod.entity.setName('edit' + editingItem.id); - silentMethod.reviewData = { - operate: id ? 'edit' : 'add', - data: editingItem - }; - silentMethod.save(); - } - // highlight-end -}); -``` - -### 删除成功回调 - -```javascript -// ... -onSuccess(({ sendArgs: [deletingId], silentMethod }) => { - // ... - // highlight-start - if (silentMethod) { - silentMethod.reviewData = { - operate: 'delete', - data: { - id: deletingId - } - }; - silentMethod.save(); - } - // highlight-end -}); -``` - -### 注意事项 - -1. onSuccess 回调函数中,只有在`queue`和`silent`行为模式下 silentMethod 才有值; -2. 一般而言,你可以通过`silentMethod.a = ...`、或`silentMethod.b = ...`来保存操作记录,但在 typescript 中会报错,因此特别提供了*reviewData*作为静默提交操作记录的保存属性; -3. 修改 silentMethod 数据后,需要通过`silentMethod.save()`来保存修改; - -下一步将对静默提交请求设置重试参数。 +--- +title: 步骤2-调整响应处理 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +在上一节的保守请求示例中,我们在 Todo 项的创建、编辑和删除请求响应后调用`fetch`重新拉取数据刷新的页面,为了能在操作后立即展示结果,我们需要进行以下调整: + +1. 将创建、编辑和删除请求的行为模式设置为`silent`,它们将会在请求时立即触发成功回调; +2. 手动更新列表,而不是拉取数据,使用虚拟数据占位服务端的响应数据; +3. 保存操作记录,用于在刷新页面时进行数据补偿; + +## 设置行为模式 + +通过配置参数`behavior`进行设置,可选参数为`queue`、`silent`、`static`,或者一个返回行为数据的函数实现动态设置行为模式,默认为`queue`。 + +以下为静态设置 behavior 参数。 + +```javascript +useSQRequest(createOrEditTodo, { + // highlight-start + behavior: 'silent', + // highlight-end + immediate: false +}); +``` + +以下为动态设置 behavior 参数。 + +```javascript +const { send } = useSQRequest(createOrEditTodo, { + // highlight-start + // arg参数可通过send函数传入 + behavior: arg => { + if (arg === 0) return 'silent'; + return 'queue'; + }, + // highlight-end + immediate: false +}); +``` + +> 当 behavior 设置为函数时,它将在每次发起请求时被调用,来确定本次请求以哪种行为来处理。 + +## 静默队列说明 + +将 behavior 参数设置为`queue`或`silent`后,请求将进入静默队列等待发起请求,默认情况下它们将进入名称为`default`的队列,你还可以指定其他队列来保存 silentMethod 实例,队列之间互不干扰。 + +```javascript +useSQRequest(createOrEditTodo, { + // highlight-start + // 指定请求信息进入名称为queue-2的队列中 + queue: 'queue-2', + // highlight-end + behavior: 'silent', + immediate: false +}); +``` + +## 在回调中手动更新列表 + +### 在新增/编辑后更新列表 + + + + +当列表页未被销毁,例如在当前页使用模态框操作,或使用了``(Vue)保留了页面组件,数据还会存在,此时我们使用**updateStateEffect**来更新列表数据,相比于 alova 导出的**updateState**,它具有追踪虚拟数据追踪功能,当获取到响应数据后,它将自动追踪列表数据内的虚拟数据,并替换为实际数据。 + +```javascript +import { useSQRequest, updateStateEffect } from '@alova/scene-*'; +import { createOrEditTodo, todoList } from './api.js'; + +const { onSuccess } = useSQRequest(createOrEditTodo, { + behavior: 'silent', + immediate: false, + + // highlight-start + // 在处理列表更新前,需要根据响应数据的结构先构造相同结构的虚拟响应数据 + // 例如,在创建 Todo 项时将返回这条数据的 id。 + silentDefaultResponse: () => { + return { + id: '--' + }; + } + // highlight-end +}); + +// highlight-start +onSuccess(({ data, silentMethod }) => { + // 构造列表数据项 + const editingItem = { + ...detail, + + // 当编辑时,使用原id,否则使用响应数据内的id + // 在静默提交时,data.id为虚拟数据,在static行为模式时,data.id为实际的id值 + id: id || data.id + }; + + // 使用updateStateEffect,而不是updateState + updateStateEffect(todoList(), todoListRaw => { + if (id) { + todoListRaw = todoListRaw.map(item => (item.id === id ? editingItem : item)); + } else { + todoListRaw.unshift(editingItem); + } + return todoListRaw; + }); +}); +// highlight-end +``` + +> updateStateEffect 的使用方法与[updateState](/tutorial/advanced/update-across-components)一致 + + + + +当列表页已被销毁,数据已被释放,例如跳转到了新页面,此时使用**setCache**更新缓存数据,当返回列表页时将重新发起请求并命中更新的缓存。 + +```javascript +import { useSQRequest, setCache, equals } from '@alova/scene-*'; +import { createOrEditTodo, todoList } from './api.js'; + +const urlParams = new URLSearchParams(window.location.search); +const id = urlParams.get('id') || ''; +const { onSuccess } = useSQRequest(createOrEditTodo, { + behavior: 'silent', + immediate: false, + + // highlight-start + // 在处理列表更新前,需要根据响应数据的结构先构造相同结构的虚拟响应数据 + // 例如,在创建 Todo 项时将返回这条数据的 id。 + silentDefaultResponse: () => { + return { + id: '--' + }; + } + // highlight-end +}); +// highlight-start +onSuccess(({ data, silentMethod }) => { + // 构造列表数据项 + const editingItem = { + ...detail, + + // 当编辑时,使用原id,否则使用响应数据内的id + // 在静默提交时,data.id为虚拟数据,在static行为模式时,data.id为实际的id值 + id: id || data.id + }; + + const methodTodoList = todoList(); + setCache(methodTodoList, todoListRaw => { + if (id) { + todoListRaw = todoListRaw.map(item => (equals(item.id, id) ? editingItem : item)); + } else { + todoListRaw.unshift(editingItem); + } + return todoListRaw; + }); + // 调用setUpdateState设置响应数据追踪,这样就实现了和updateStateEffect相同的延迟更新效果 + if (silentMethod) { + silentMethod.setUpdateState(methodTodoList); + silentMethod.save(); + } +}); +// highlight-end +``` + + + + +### 移除后更新列表 + +```javascript +import { useSQRequest, updateStateEffect } from '@alova/scene-*'; +import { deleteTodo, todoList } from './api.js'; + +const { loading, data, send, onSuccess } = useSQRequest(deleteTodo, { + immediate: false, + // highlight-start + behavior: 'silent' + // highlight-end +}); + +onSuccess(({ sendArgs: [deletingId] }) => { + updateStateEffect(todoList(), todoListRaw => + todoListRaw.filter(item => item.id !== deletingId) + ); +}); + +// 事件回调触发删除请求 +const handleDelete = deletingId => { + send(deletingId); +}; +``` + +## 保存操作记录 + +仅仅完成手动更新列表还不够,我们还需要考虑当网络恢复后,当请求队列还存等待中的请求时,此时加载的列表数据还不包括未提交请求的部分,这会让用户造成一定的困惑: + +> “我明明已经新增了多条数据,为什么列表中没有?” + +因此我们需要在成功回调中记录操作,以及相关数据,以便再次加载列表数据时,将未提交的数据手动补偿到列表中,从而让列表数据始终保持最新状态。 + +保存操作记录也很简单,只需要将相关数据挂载到 silentMethod 实例上,它将随着实例一同被持久化。 + +### 创建/编辑成功回调 + +```javascript +// ... +onSuccess(({ silentMethod }) => { + // 构造列表数据项 + const editingItem = { + ...detail, + id: id || data.id + }; + // ... + // highlight-start + if (silentMethod) { + // 设置名称,以便后续查询 + // 如果editingItem.id是虚拟数据将自动转换为它的id + silentMethod.entity.setName('edit' + editingItem.id); + silentMethod.reviewData = { + operate: id ? 'edit' : 'add', + data: editingItem + }; + silentMethod.save(); + } + // highlight-end +}); +``` + +### 删除成功回调 + +```javascript +// ... +onSuccess(({ sendArgs: [deletingId], silentMethod }) => { + // ... + // highlight-start + if (silentMethod) { + silentMethod.reviewData = { + operate: 'delete', + data: { + id: deletingId + } + }; + silentMethod.save(); + } + // highlight-end +}); +``` + +### 注意事项 + +1. onSuccess 回调函数中,只有在`queue`和`silent`行为模式下 silentMethod 才有值; +2. 一般而言,你可以通过`silentMethod.a = ...`、或`silentMethod.b = ...`来保存操作记录,但在 typescript 中会报错,因此特别提供了*reviewData*作为静默提交操作记录的保存属性; +3. 修改 silentMethod 数据后,需要通过`silentMethod.save()`来保存修改; + +下一步将对静默提交请求设置重试参数。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/06-request-retry.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/06-request-retry.md index fb09fe811..7d847c4b1 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/06-request-retry.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/06-request-retry.md @@ -1,144 +1,143 @@ ---- -title: 步骤3-设置请求重试 -sidebar_position: 60 ---- - -当一个请求进入静默队列后是可以为它设置请求重试参数,来保证它的请求成功率,这在行为模式设置为 **queue** 和 **silent** 时都有效,不同的是,**silent** 行为下的请求默认是持久化的,请求成功前即使刷新也将继续发送请求,而 **queue** 行为下的请求不会被持久化,刷新后将被清除。 - -## 最大重试次数 - -设置最大重试次数,默认不重试。 - -```javascript -useSQRequest(createOrEditTodo, { - // ... - // highlight-start - // 重试次数为3次 - maxRetryTimes: 3 - // highlight-end -}); -``` - -## 请求延迟时间 - -默认情况下,每次间隔 1000ms 重试,我们可以在避让策略中自定义设置每次重试的延迟时间。 - -```javascript -useSQRequest(createOrEditTodo, { - // ... - maxRetryTimes: 3, - // highlight-start - // 每次延迟2000ms进行请求 - backoff: { - delay: 2000 - } - // highlight-end -}); -``` - -如果需要按规则增长的延迟时间,可以再为它设置一个增长倍数,延迟时间将按重试次数指数增长。 - -```javascript -useSQRequest(createOrEditTodo, { - // ... - maxRetryTimes: 3, - backoff: { - delay: 2000, - // highlight-start - // multiplier设置为2时,第一次重试延迟为2秒,第二次为4秒,第三次为8秒,以此类推 - multiplier: 2 - // highlight-end - } -}); -``` - -还不够?你甚至还可以为每次延迟时间增加随机抖动值,让它看着不那么规律 - -```javascript -useSQRequest(createOrEditTodo, { - // ... - maxRetryTimes: 3, - backoff: { - delay: 2000, - multiplier: 2, - // highlight-start - /** - * 延迟请求的抖动起始百分比值,范围为0-1 - * 当只设置了startQuiver时,endQuiver默认为1 - * 例如设置为0.5,它将在当前延迟时间上增加50%到100%的随机时间 - * 如果endQuiver有值,则延迟时间将增加startQuiver和endQuiver范围的随机值 - */ - startQuiver: 0.5, - - /** - * 延迟请求的抖动结束百分比值,范围为0-1 - * 当只设置了endQuiver时,startQuiver默认为0 - * 例如设置为0.8,它将在当前延迟时间上增加0%到80%的随机时间 - * 如果startQuiver有值,则延迟时间将增加startQuiver和endQuiver范围的随机值 - */ - endQuiver: 0.8; - // highlight-end - } -}); -``` - -## 重试判定规则 - -默认情况下,只要请求失败都将会重试,请求失败分为以下情况: - -1. 请求错误,并且未被全局的`onError`钩子捕获错误; -2. 请求成功,但在全局的`onSuccess`钩子中抛出了错误; - -但实际情况下,并不是所有的请求都需要重试,例如当遇到服务端错误,或网络断开时不应该重试,此时就需要设置重试判定规则。当请求失败时通常会获得一个 `Error` 实例,我们可以设置正则表达式对 `error.message` 或 `error.name` 进行匹配,匹配通过则不再重试。 - -```javascript -useSQRequest(createOrEditTodo, { - // ... - // highlight-start - // 当抛出的错误name为500,或者错误的message匹配network error时不再重试 - retryError: { - name: /^500$/, - message: /network error/i - } - // highlight-end -}); -``` - -你也可以设置其中一个匹配规则,当只设置 `message` 的匹配规则时,可以直接简写为正则表达式。 - -```javascript -// 只设置匹配错误的name -useSQRequest(createOrEditTodo, { - // ... - retryError: { - name: /^500$/ - } -}); - -// 只设置匹配错误的message -useSQRequest(createOrEditTodo, { - // ... - retryError: /network error/i -}); -``` - -为了不污染错误信息,通常情况下我们将会把服务端返回的错误代码放在 `error.name` 中,当然,你也可以拼接到 `error.message` 中,以 Response 的错误处理示例如下: - -```javascript -const alovaInst = createAlova({ - // ... - responded: { - onSuccess(response) { - // 500错误时抛出错误 - if (response.status === 500) { - const error = new Error(response.statusText); - error.name = response.status; - throw error; - } - return response.json(); - } - } -}); -``` - -下一步,将会使用到保存的操作记录,对列表数据进行数据补偿,来达到最新状态。 +--- +title: 步骤3-设置请求重试 +--- + +当一个请求进入静默队列后是可以为它设置请求重试参数,来保证它的请求成功率,这在行为模式设置为 **queue** 和 **silent** 时都有效,不同的是,**silent** 行为下的请求默认是持久化的,请求成功前即使刷新也将继续发送请求,而 **queue** 行为下的请求不会被持久化,刷新后将被清除。 + +## 最大重试次数 + +设置最大重试次数,默认不重试。 + +```javascript +useSQRequest(createOrEditTodo, { + // ... + // highlight-start + // 重试次数为3次 + maxRetryTimes: 3 + // highlight-end +}); +``` + +## 请求延迟时间 + +默认情况下,每次间隔 1000ms 重试,我们可以在避让策略中自定义设置每次重试的延迟时间。 + +```javascript +useSQRequest(createOrEditTodo, { + // ... + maxRetryTimes: 3, + // highlight-start + // 每次延迟2000ms进行请求 + backoff: { + delay: 2000 + } + // highlight-end +}); +``` + +如果需要按规则增长的延迟时间,可以再为它设置一个增长倍数,延迟时间将按重试次数指数增长。 + +```javascript +useSQRequest(createOrEditTodo, { + // ... + maxRetryTimes: 3, + backoff: { + delay: 2000, + // highlight-start + // multiplier设置为2时,第一次重试延迟为2秒,第二次为4秒,第三次为8秒,以此类推 + multiplier: 2 + // highlight-end + } +}); +``` + +还不够?你甚至还可以为每次延迟时间增加随机抖动值,让它看着不那么规律 + +```javascript +useSQRequest(createOrEditTodo, { + // ... + maxRetryTimes: 3, + backoff: { + delay: 2000, + multiplier: 2, + // highlight-start + /** + * 延迟请求的抖动起始百分比值,范围为0-1 + * 当只设置了startQuiver时,endQuiver默认为1 + * 例如设置为0.5,它将在当前延迟时间上增加50%到100%的随机时间 + * 如果endQuiver有值,则延迟时间将增加startQuiver和endQuiver范围的随机值 + */ + startQuiver: 0.5, + + /** + * 延迟请求的抖动结束百分比值,范围为0-1 + * 当只设置了endQuiver时,startQuiver默认为0 + * 例如设置为0.8,它将在当前延迟时间上增加0%到80%的随机时间 + * 如果startQuiver有值,则延迟时间将增加startQuiver和endQuiver范围的随机值 + */ + endQuiver: 0.8; + // highlight-end + } +}); +``` + +## 重试判定规则 + +默认情况下,只要请求失败都将会重试,请求失败分为以下情况: + +1. 请求错误,并且未被全局的`onError`钩子捕获错误; +2. 请求成功,但在全局的`onSuccess`钩子中抛出了错误; + +但实际情况下,并不是所有的请求都需要重试,例如当遇到服务端错误,或网络断开时不应该重试,此时就需要设置重试判定规则。当请求失败时通常会获得一个 `Error` 实例,我们可以设置正则表达式对 `error.message` 或 `error.name` 进行匹配,匹配通过则不再重试。 + +```javascript +useSQRequest(createOrEditTodo, { + // ... + // highlight-start + // 当抛出的错误name为500,或者错误的message匹配network error时不再重试 + retryError: { + name: /^500$/, + message: /network error/i + } + // highlight-end +}); +``` + +你也可以设置其中一个匹配规则,当只设置 `message` 的匹配规则时,可以直接简写为正则表达式。 + +```javascript +// 只设置匹配错误的name +useSQRequest(createOrEditTodo, { + // ... + retryError: { + name: /^500$/ + } +}); + +// 只设置匹配错误的message +useSQRequest(createOrEditTodo, { + // ... + retryError: /network error/i +}); +``` + +为了不污染错误信息,通常情况下我们将会把服务端返回的错误代码放在 `error.name` 中,当然,你也可以拼接到 `error.message` 中,以 Response 的错误处理示例如下: + +```javascript +const alovaInst = createAlova({ + // ... + responded: { + onSuccess(response) { + // 500错误时抛出错误 + if (response.status === 500) { + const error = new Error(response.statusText); + error.name = response.status; + throw error; + } + return response.json(); + } + } +}); +``` + +下一步,将会使用到保存的操作记录,对列表数据进行数据补偿,来达到最新状态。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/07-data-compensation.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/07-data-compensation.md index 6271f24f7..80cea98a7 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/07-data-compensation.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/07-data-compensation.md @@ -1,56 +1,55 @@ ---- -title: 步骤4-数据补偿 -sidebar_position: 70 ---- - -用户可能在断网环境下进行了一些数据操作,此时静默队列内将堆满未提交的请求,当网络恢复后,由于时序机制的限制,完成这些请求需要消耗一点时间,此时加载的列表数据还不包括未提交请求的部分,这会让用户造成一定的困惑: - -> “我明明已经新增了多条数据,为什么列表中没有?” - -因此我们需要将未提交的数据手动补偿到列表中,从而让列表数据始终保持最新状态,在这一步中将会使用到保存的操作记录,对列表数据进行数据补偿,它其实也很简单,我们只需要在列表请求成功后遍历相关队列的 silentMethod 实例,将上一步记录的操作记录更新到列表数据中。 - -```javascript -import { useSQRequest, filterSilentMethods, equals } from '@alova/scene-vue'; -import { todoList } from './api.js'; -const { data, loading, onSuccess } = useSQRequest(todoList, { - initialData: [] -}); - -onSuccess(() => { - // 获取default队列下的所有silentMethod实例 - const silentMethods = filterSilentMethods(); - silentMethods.forEach(({ reviewData }) => { - if (!reviewData) { - return; - } - const { operate, data } = reviewData; - const index = todoListData.findIndex(({ id }) => equals(id, data.id)); - if ((operate === 'edit' || operate === 'remove') && index >= 0) { - operate === 'edit' ? todoListData.splice(index, 1, data) : todoListData.splice(index, 1); - } else if (operate === 'add' && index < 0) { - // 在重新请求并命中缓存时将会有已添加的未提交项,这些需要过滤 - todoListData.unshift(data); - } - }); -}); -``` - -## 相关函数解释 - -### filterSilentMethods - -按 method 名称或正则表达式过滤出指定的 silentMethod 实例,它的定义如下: - -```typescript -function filterSilentMethods( - methodNameMatcher?: string | number | RegExp, - queueName?: string, - filterActive?: boolean -): SilentMethod[]; -``` - -**methodNameMatcher:**method 名称匹配器,如果是数字或字符串将过滤出完全匹配名称的结果,如果是正则表达式则过滤出匹配的结果,如果不传则过滤出所有结果; - -**queueName**:指定查找的队列,未传时默认查找*default*队列; - -**filterActive**:是否过滤掉激活状态的实例 +--- +title: 步骤4-数据补偿 +--- + +用户可能在断网环境下进行了一些数据操作,此时静默队列内将堆满未提交的请求,当网络恢复后,由于时序机制的限制,完成这些请求需要消耗一点时间,此时加载的列表数据还不包括未提交请求的部分,这会让用户造成一定的困惑: + +> “我明明已经新增了多条数据,为什么列表中没有?” + +因此我们需要将未提交的数据手动补偿到列表中,从而让列表数据始终保持最新状态,在这一步中将会使用到保存的操作记录,对列表数据进行数据补偿,它其实也很简单,我们只需要在列表请求成功后遍历相关队列的 silentMethod 实例,将上一步记录的操作记录更新到列表数据中。 + +```javascript +import { useSQRequest, filterSilentMethods, equals } from '@alova/scene-vue'; +import { todoList } from './api.js'; +const { data, loading, onSuccess } = useSQRequest(todoList, { + initialData: [] +}); + +onSuccess(() => { + // 获取default队列下的所有silentMethod实例 + const silentMethods = filterSilentMethods(); + silentMethods.forEach(({ reviewData }) => { + if (!reviewData) { + return; + } + const { operate, data } = reviewData; + const index = todoListData.findIndex(({ id }) => equals(id, data.id)); + if ((operate === 'edit' || operate === 'remove') && index >= 0) { + operate === 'edit' ? todoListData.splice(index, 1, data) : todoListData.splice(index, 1); + } else if (operate === 'add' && index < 0) { + // 在重新请求并命中缓存时将会有已添加的未提交项,这些需要过滤 + todoListData.unshift(data); + } + }); +}); +``` + +## 相关函数解释 + +### filterSilentMethods + +按 method 名称或正则表达式过滤出指定的 silentMethod 实例,它的定义如下: + +```typescript +function filterSilentMethods( + methodNameMatcher?: string | number | RegExp, + queueName?: string, + filterActive?: boolean +): SilentMethod[]; +``` + +**methodNameMatcher:**method 名称匹配器,如果是数字或字符串将过滤出完全匹配名称的结果,如果是正则表达式则过滤出匹配的结果,如果不传则过滤出所有结果; + +**queueName**:指定查找的队列,未传时默认查找*default*队列; + +**filterActive**:是否过滤掉激活状态的实例 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/08-edit-item.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/08-edit-item.md index 473c17e93..f044f76bb 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/08-edit-item.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/08-edit-item.md @@ -1,111 +1,110 @@ ---- -title: 步骤5-编辑数据 -sidebar_position: 80 ---- - -> 当用户在断网情况下需要编辑数据,怎么办? - -这时,需要分两种情况来说明: - -1. 列表数据可以满足编辑页的数据回显,此时可以将列表数据传递到编辑页,而不需要通过请求获取,此时所有列表数据在静默提交模式下都支持编辑; -2. 编辑页回显数据需要通过 api 获取的,只有在本地有缓存的列表项才可以正常回显数据,例如: - 1. 在网络断开前已经访问过的列表项,再次请求可以命中缓存; - 2. 通过静默提交模式创建,但还没有提交成功的列表项,提交数据还存在于 silentMethod 实例中; - -而在这边我们将重点讨论 **2-2** 的情况。 - -## 编辑静默提交项 - -在之前的章节中我们知道,当新建的数据项还没有成功提交时,将会使用虚拟数据作为 id 的占位符,通常,我们也是通过 id 来获取数据项的,此时我们在`useSQRequeset`上实现了虚拟数据拦截,一个请求如果附带了虚拟数据信息,它将在发送前被拦截并可以指定数据来替代响应数据,并且放弃这次请求。 - -还记得在 [步骤 2-调整响应处理](/tutorial/strategy/sensorless-data-interaction/modify-response) 中保存的 **silentMethod.reviewData** 吗? - -```javascript -onSuccess(({ silentMethod }) => { - // 构造列表数据项 - const editingItem = { - ...detail, - id: id || data.id - }; - // ... - if (silentMethod) { - // highlight-start - // 设置名称是为了在拦截时查找对应的silentMethod实例 - silentMethod.entity.setName('edit' + editingItem.id); - silentMethod.reviewData = { - operate: id ? 'edit' : 'add', - data: editingItem - }; - // 别忘了调用save - silentMethod.save(); - // highlight-end - } -}); -``` - -它不仅可以用于数据补偿,还可以用于在编辑页中回显数据。 - -```javascript -const { loading, data } = useSQRequest(id => todoDetail(id), { - initialData: { - title: '', - time: new Date() - }, - immediate: false, - - // highlight-start - // 设置拦截函数,当这个请求存在虚拟数据时函数将被调用 - // 如果返回了reviewData则会替代响应数据,并放弃本次请求,否则仍然发起请求 - vDataCaptured: () => { - const targetSM = filterSilentMethods('edit' + todoId).pop(); - if (targetSM?.reviewData) { - return { ...targetSM.reviewData.data }; - } - } - // highlight-end -}); -``` - -:::warning 注意 - -你可以在 **silentMethod.reviewData** 中保存足够多的数据,让它既可以满足列表数据补偿,也可以满足编辑页数据回显。 - -::: - -至此,通过静默提交模式创建的数据项也支持编辑了!还有什么问题呢,嗯...还有最后一个。 - -## 当正在编辑的数据项提交成功时 - -当用户正在编辑一条还未提交成功的数据项时,它突然提交成功了!这时我们需要将编辑页中使用到的虚拟数据替换为实际数据,例如将虚拟 id 替换为实际的 id,在接下来的编辑中使用实际的 id 进行提交,这也很简单,我们只需要监听静默提交成功事件即可完成,它将接收到虚拟数据和实际数据组成的数据集合。 - -```javascript -import { onSilentSubmitSuccess, stringifyVData } from '@alova/scene-*'; - -// ... -// id在初始化时是虚拟数据 -let id = /* todo virtual id */; - -// highlight-start -// 绑定监听静默提交成功事件来更新id,并返回解绑函数,别忘了在组件销毁时调用解绑函数 -const unbindEvent = onSilentSubmitSuccess(event => { - const vDataId = stringifyVData(id); - if (event.vDataResponse[vDataId]) { - id = event.vDataResponse[vDataId]; - - // 以下是将url中的虚拟id更改为实际的id - history.replaceState(null, '', '?id=' + currentId); - } -}); -// highlight-end -``` - -在这边,`event.vDataResponse` 值是由虚拟数据 id 和实际数据组成的集合,它的格式如下: - -```javascript -{ - '[vd:aaaaaa]': { id: 1 }, - '[vd:bbbbbb]': 1 -} -``` - -至此,我们已经完成了一个简单列表的无感交互的所有内容,但在其他应用场景下如编辑类应用、复杂列表管理等,将可能遇到更多不一样的需求,此时 alova 还有什么特性是我们能使用的呢?请再看下一章! +--- +title: 步骤5-编辑数据 +--- + +> 当用户在断网情况下需要编辑数据,怎么办? + +这时,需要分两种情况来说明: + +1. 列表数据可以满足编辑页的数据回显,此时可以将列表数据传递到编辑页,而不需要通过请求获取,此时所有列表数据在静默提交模式下都支持编辑; +2. 编辑页回显数据需要通过 api 获取的,只有在本地有缓存的列表项才可以正常回显数据,例如: + 1. 在网络断开前已经访问过的列表项,再次请求可以命中缓存; + 2. 通过静默提交模式创建,但还没有提交成功的列表项,提交数据还存在于 silentMethod 实例中; + +而在这边我们将重点讨论 **2-2** 的情况。 + +## 编辑静默提交项 + +在之前的章节中我们知道,当新建的数据项还没有成功提交时,将会使用虚拟数据作为 id 的占位符,通常,我们也是通过 id 来获取数据项的,此时我们在`useSQRequeset`上实现了虚拟数据拦截,一个请求如果附带了虚拟数据信息,它将在发送前被拦截并可以指定数据来替代响应数据,并且放弃这次请求。 + +还记得在 [步骤 2-调整响应处理](/tutorial/strategy/sensorless-data-interaction/modify-response) 中保存的 **silentMethod.reviewData** 吗? + +```javascript +onSuccess(({ silentMethod }) => { + // 构造列表数据项 + const editingItem = { + ...detail, + id: id || data.id + }; + // ... + if (silentMethod) { + // highlight-start + // 设置名称是为了在拦截时查找对应的silentMethod实例 + silentMethod.entity.setName('edit' + editingItem.id); + silentMethod.reviewData = { + operate: id ? 'edit' : 'add', + data: editingItem + }; + // 别忘了调用save + silentMethod.save(); + // highlight-end + } +}); +``` + +它不仅可以用于数据补偿,还可以用于在编辑页中回显数据。 + +```javascript +const { loading, data } = useSQRequest(id => todoDetail(id), { + initialData: { + title: '', + time: new Date() + }, + immediate: false, + + // highlight-start + // 设置拦截函数,当这个请求存在虚拟数据时函数将被调用 + // 如果返回了reviewData则会替代响应数据,并放弃本次请求,否则仍然发起请求 + vDataCaptured: () => { + const targetSM = filterSilentMethods('edit' + todoId).pop(); + if (targetSM?.reviewData) { + return { ...targetSM.reviewData.data }; + } + } + // highlight-end +}); +``` + +:::warning 注意 + +你可以在 **silentMethod.reviewData** 中保存足够多的数据,让它既可以满足列表数据补偿,也可以满足编辑页数据回显。 + +::: + +至此,通过静默提交模式创建的数据项也支持编辑了!还有什么问题呢,嗯...还有最后一个。 + +## 当正在编辑的数据项提交成功时 + +当用户正在编辑一条还未提交成功的数据项时,它突然提交成功了!这时我们需要将编辑页中使用到的虚拟数据替换为实际数据,例如将虚拟 id 替换为实际的 id,在接下来的编辑中使用实际的 id 进行提交,这也很简单,我们只需要监听静默提交成功事件即可完成,它将接收到虚拟数据和实际数据组成的数据集合。 + +```javascript +import { onSilentSubmitSuccess, stringifyVData } from '@alova/scene-*'; + +// ... +// id在初始化时是虚拟数据 +let id = /* todo virtual id */; + +// highlight-start +// 绑定监听静默提交成功事件来更新id,并返回解绑函数,别忘了在组件销毁时调用解绑函数 +const unbindEvent = onSilentSubmitSuccess(event => { + const vDataId = stringifyVData(id); + if (event.vDataResponse[vDataId]) { + id = event.vDataResponse[vDataId]; + + // 以下是将url中的虚拟id更改为实际的id + history.replaceState(null, '', '?id=' + currentId); + } +}); +// highlight-end +``` + +在这边,`event.vDataResponse` 值是由虚拟数据 id 和实际数据组成的集合,它的格式如下: + +```javascript +{ + '[vd:aaaaaa]': { id: 1 }, + '[vd:bbbbbb]': 1 +} +``` + +至此,我们已经完成了一个简单列表的无感交互的所有内容,但在其他应用场景下如编辑类应用、复杂列表管理等,将可能遇到更多不一样的需求,此时 alova 还有什么特性是我们能使用的呢?请再看下一章! diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/09-what-more.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/09-what-more.md index ccbf7e83c..6c9672a63 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/09-what-more.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/09-what-more.md @@ -1,342 +1,343 @@ ---- -title: 还有什么? -sidebar_position: 90 ---- - -## 虚拟数据的作用说明 - -在之前的章节中我们以虚拟数据作为 id 占位符,但它的作用不止于此,它可以占位任意的响应数据,例如在复杂列表中,当创建数据项时服务端需要计算产生额外的数据,此时就可以将这些额外数据也通过虚拟数据占位,但这要求额外的数据需要在创建数据项时一并返回。看以下示例: - -```javascript -const { onSuccess, send } = useSQRequest(createOrEditData, { - behavior: 'silent', - immediate: false, - - // 构造与响应数据相同的数据结构 - silentDefaultResponse: () => { - return { - id: '--', - extra1: '', - extra2: '' - }; - } -}); -onSuccess(event => { - event.data.id; // 虚拟数据 - event.data.extra1; //虚拟数据 - event.data.extra2; //虚拟数据 -}); -``` - -## useSQRequest 中新增的事件 - -为了更好地监听请求在队列中的行为,`useSQRequset` 还额外提供了以下 3 个事件监听函数,你可以通过以下方式获取绑定函数。 - -```javascript -const { onBeforePushQueue, onPushedQueue, onFallback } = useSQRequest(/* ... */); -``` - -### onBeforePushQueue - -silentMethod 进入请求队列前事件,当行为模式为`queue`或`silent`时有效,可以在这个事件回调中返回 `false` 来阻止当前 silentMethod 进入队列,例如你可能想当前 silentMethod 替换掉另外一个时,可以这样做: - -```javascript -// ... -onBeforePushQueue(event => { - // 每次替换指定id的旧silentMethod,减少请求次数 - const prevSumbmitMethod = getSilentMethod('temp' + id); - if (event.silentMethod && prevSumbmitMethod) { - prevSumbmitMethod.replace(event.silentMethod); - return false; - } -}); -``` - -### onPushedQueue - -silentMethod 进入队列后的事件,当行为模式为`queue`或`silent`时有效,如果在 **onBeforePushQueue** 事件中阻止了进入队列,则此函数不会触发。 - -### onFallback - -类似传统的 optimistic ui 的解决方案,我们也提供了请求回退事件,当请求达到最大重试次数或者重试判定失败时都将会触发这个时间,你可以使用它处理一些回退操作。 - -:::warning 警告 - -当绑定回退事件后,即使行为模式是`silent`的请求也不再被持久化,它将会在刷新页面后丢失,这是因为持久化的 silentMethod 通常都是需要确保完成的,而不是回退让用户重新处理的,在这种情况下不应该使用回退功能。 - -::: - -## 保存额外的操作数据 - -在创建或编辑数据项时,之前的章节仅保存了回显数据到`silentMethod.reviewData`中,如果有一些额外的数据需要记录,例如编辑页的菜单可选项等,我们同样需要记录它们以确保在断网情况下还可以选择到它们,此时将这些数据挂载到 silentMethod 实例上一起持久化。 - -一般而言,你可以以任意属性名保存持久化数据,但在 typescript 中会报错,因此为你指定了 `silentMethod.extraData` 属性作为额外数据的保存字段,记得通过 `silentMethod.save()` 持久化数据。 - -## 自定义序列化器 - -默认情况下,alova 是使用 localStorage 进行 silentMethod 的数据持久化的,因此在持久化时会调用`JSON.stringify`转换为字符串,但 json 数据只支持基本数据类型、纯对象以及数组,如果你希望序列化特殊的数据结构例如 Date 实例、RegExp 实例、函数以及自定义的类实例,alova 支持自定义的序列化器来处理它们,在存储时应该如何将它转换为 json 支持的数据结构,在取出时如何转换为原对象结构。 - -```javascript -const regExpSerializer = { - // forward在序列化时被调用 - // 需要判断data是否为目标值,如果不是目标值则返回undefined,表示不处理它 - forward: data => data instanceof RegExp ? data.source : undefined, - - // backward在反序列化时被调用,data为forward中返回的值 - backward: source => new RegExp(source); -}; - -bootSilentFactory({ - // ... - // 通过serializers使用这个序列化器 - serializers: { - customRegExp: regExpSerializer - } -}) -``` - -SilentFactory 内部默认提供了 Date 和 RegExp 的序列化器,你也可以使用相同的 key 来覆盖默认的序列化器 - -```javascript -const defaultSerializers = { - Date: dateSerializer, - RegExp: regExpSerializer -}; -``` - -[阅读 Date 序列化器源码](https://github.com/alovajs/scene/blob/main/src/hooks/silent/serializer/date.ts) - -## 操纵静默队列 - -静默队列用于保证请求时序问题,我们可以任意创建队列,进入队列的请求都将会以**SilentMethod**实例的形式保存在队列中,每个**SilentMethod**除了包含请求信息外,还包含静默提交的相关配置。静默队列是可以生成任意多个的,且支持查找、修改、删除队列中的 silentMethod 实例。 - -### 使用多个静默队列 - -行为模式设置为`queue`和`silent`的请求都会进入静默队列,默认情况下,silentMethod 实例将会被分配到**default**队列中,当需要分配到其他队列时可以在*useSQRequest*中指定`queue`参数。 - -```javascript -useSQRequest(createOrEditTodo, { - // ... - // 指定silentMethod实例进入名称为customQueue的队列中 - queue: 'customQueue', - behavior: 'silent' -}); -``` - -也可以将`queue`指定为函数,要求返回队列名称,这个函数将会在每次发起请求时被调用,函数参数来自于 send 函数。 - -```javascript -const { send } = useSQRequest(createOrEditTodo, { - // ... - // 根据useCustomQueue判断是否进入customQueue队列 - queue: useCustomQueue => (useCustomQueue ? 'customQueue' : 'default'), - behavior: 'silent', - immediate: false -}); -const handleClick = () => { - send(true); -}; -``` - -### 查找 silentMethod - -在之前的[数据补偿](/tutorial/strategy/sensorless-data-interaction/data-compensation)中,我们使用了 [filterSilentMethods](/tutorial/strategy/sensorless-data-interaction/data-compensation#filtersilentmethods) 查找指定队列的 silentMethod 实例,它将返回所有匹配的 silentMethod 实例,这里再介绍两种查找队列的方式: - -#### 查找个 silentMethod 实例 - -使用`getSilentMethod`查询匹配的第一个 silentMethod 实例,用法与 [filterSilentMethods](/tutorial/strategy/sensorless-data-interaction/data-compensation#filtersilentmethods) 相同。 - -```typescript -function filterSilentMethods( - methodNameMatcher?: string | number | RegExp, - queueName?: string, - filterActive?: boolean -): SilentMethod | undefined; -``` - -#### 自定义查找 - -通过导出的 `silentQueueMap` 队列集合自定义查找,`silentQueueMap` 的数据结构为: - -```javascript -const silentQueueMap = { - default: [silentMethod1, silentMethod2 /* ... */], - queueName1: [silentMethod3, silentMethod4 /* ... */], - queueName2: [silentMethod5, silentMethod6 /* ... */] - // ... -}; -``` - -### 更改队列中的 silentMethod - -当查找到你想要的 silentMethod 实例后,可以对这些等待中的 silentMethod 实例进行操纵。 - -#### 替换 silentMethod - -调用`silentMethod.replace`可以在队列中将一个 silentMethod 替换为另一个 silentMethod。 - -```javascript -oldSilentMethod.replace(newSilentMethod); -``` - -#### 移除 silentMethod - -调用`silentMethod.remove`可以将当前 silentMethod 在队列中移除。 - -```javascript -oldSilentMethod.remove(); -``` - -#### 使用 silentQueueMap 更改 silentMethod - -你还可以访问 `silentQueueMap` 自定义更改任意队列的任意数据。 - -```javascript -import { silentQueueMap } from '@alova/scene-*'; - -// 修改default队列中的所有silentMethod -silentQueueMap.default.forEach(silentMethodItem => { - // ... -}); -``` - -## 队列请求延迟 - -有些应用需要频繁地提交数据,例如编辑器类型应用,在编辑过程中进行实时保存,同时不打断用户使用,在这种类型的应用中使用静默提交时将会产生较多的请求信息,不仅会塞满前端缓存还会让服务端接收过多的请求,此时,我们可能不再需要将同步所有保存操作,而是在一段时间内发送一次操作,将有以下两种方案: - -1. 对编辑操作节流,在 n 秒内只发起一次提交,这种方案可能会丢失延迟时间段内的操作记录,导致刷新时只能获取最后一次提交时的状态; -2. 延迟队列内的保存请求,并在延迟时间内只保留最新一次的请求信息,这样就可以减少请求的同时保留最新的编辑状态; - -默认情况下,队列中的 silentMethod 会在上一个响应后立即发送请求,我们可以在启动时通过 `requestWait` 设置 silentMethod 的延迟发送时间,在这段时间里通过操纵队列保留最新的 silentMethod。 - -### 设置请求延迟 - -你可以对指定的队列设置延迟时间,也可以一次设置多个队列。 - -```javascript -bootSilentFactory({ - alova: alovaInst, - // highlight-start - requestWait: [ - // customQueue队列内的每个silentMethod将会延迟5000ms发起请求 - { queue: 'customQueue', wait: 5000 }, - - // 通过正则表达式对名称前缀为delay的队列统一设置3000ms延迟请求时间 - { queue: /^delay/, wait: 3000 } - ] - // highlight-end -}); -``` - -当 `requestWait` 直接设置为数字时默认对 default 队列有效。 - -```javascript -bootSilentFactory({ - alova: alovaInst, - // highlight-start - // default队列内的每个silentMethod将会延迟5000ms发起请求 - requestWait: 5000 - // highlight-end -}); -``` - -### 动态设置请求延迟 - -很多时候我们只希望队列内的特定 silentMethod 设置延迟请求,此时可以通过函数来动态设置请求延迟,指定队列的每个 silentMethod 在发起请求前都将会调用这个函数,来确定延迟的时间。 - -```javascript -bootSilentFactory({ - alova: alovaInst, - // highlight-start - requestWait: [ - { - queue: /^delay, - // 只对url为/edit的post请求延迟5000ms - wait: (silentMethod, queuName) => { - const { type, url, data } = silentMethod.entity; - if (type === 'POST' && url === '/edit') { - return 5000; - } - } - }, - ] - // highlight-end -}); -``` - -同样的,当 `requestWait` 直接设置为函数时默认对 default 队列有效。 - -```javascript -bootSilentFactory({ - alova: alovaInst, - // highlight-start - requestWait: (silentMethod, queuName) => { - const { type, url, data } = silentMethod.entity; - if (type === 'POST' && url === '/edit') { - return 5000; - } - } - // highlight-end -}); -``` - -## 动态设置 method 名称 - -为了便于查找,如果你需要动态设置 silentMethod 内 method 的名称,可以通过调用 setName。 - -```javascript -// 在请求成功时对silentMethod重新设置名称 -onSuccess(({ data, silentMethod }) => { - silentMethod.entity.setName('name' + data.id); -}); -``` - -## 全局的静默提交事件 - -在之前的章节中,我们接触了 `onSilentSubmitSuccess`,我们总共提供了 5 个全局的事件。 - -### onSilentSubmitBoot - -静默工厂启动事件,在静默工厂启动后触发。 - -```typescript -function onSilentSubmitBoot(handler: () => void): OffEventCallback; -``` - -### onBeforeSilentSubmit - -`behavior=silent`的 silentMethod 请求前触发。 - -```typescript -function onBeforeSilentSubmit(handler: (event: GlobalSQEvent)): OffEventCallback; -``` - -### onSilentSubmitSuccess - -`behavior=silent`的 silentMethod 请求成功时触发。 - -```typescript -function onSilentSubmitSuccess(handler: (event: GlobalSQSuccessEvent) => void): OffEventCallback; -``` - -### onSilentSubmitError - -`behavior=silent`的 silentMethod 请求失败,但还没有达到最大重试次数时触发。 - -```typescript -function onSilentSubmitError(handler: (event: GlobalSQErrorEvent) => void): OffEventCallback; -``` - -### onSilentSubmitFail - -`behavior=silent`的 silentMethod 在遇到请求失败时,以下 3 种情况将会触发此事件: - -1. 请求重试到达最大次数时触发; -2. 未设置重试次数时,首次请求失败将会触发; -3. 请求未到最大重试次数,但重试判定为不再重试时触发; - -```typescript -function onSilentSubmitFail(handler: (event: GlobalSQFailEvent) => void): OffEventCallback; -``` - -以上的事件绑定函数都将返回解绑函数,你可以在组件卸载前解绑事件。 +--- +title: 还有什么? +--- + +## 虚拟数据的作用说明 + +在之前的章节中我们以虚拟数据作为 id 占位符,但它的作用不止于此,它可以占位任意的响应数据,例如在复杂列表中,当创建数据项时服务端需要计算产生额外的数据,此时就可以将这些额外数据也通过虚拟数据占位,但这要求额外的数据需要在创建数据项时一并返回。看以下示例: + +```javascript +const { onSuccess, send } = useSQRequest(createOrEditData, { + behavior: 'silent', + immediate: false, + + // 构造与响应数据相同的数据结构 + silentDefaultResponse: () => { + return { + id: '--', + extra1: '', + extra2: '' + }; + } +}); +onSuccess(event => { + event.data.id; // 虚拟数据 + event.data.extra1; //虚拟数据 + event.data.extra2; //虚拟数据 +}); +``` + +## useSQRequest 中新增的事件 + +为了更好地监听请求在队列中的行为,`useSQRequset` 还额外提供了以下 3 个事件监听函数,你可以通过以下方式获取绑定函数。 + +```javascript +const { onBeforePushQueue, onPushedQueue, onFallback } = useSQRequest(/* ... */); +``` + +### onBeforePushQueue + +silentMethod 进入请求队列前事件,当行为模式为`queue`或`silent`时有效,可以在这个事件回调中返回 `false` 来阻止当前 silentMethod 进入队列,例如你可能想当前 silentMethod 替换掉另外一个时,可以这样做: + +```javascript +// ... +onBeforePushQueue(event => { + // 每次替换指定id的旧silentMethod,减少请求次数 + const prevSumbmitMethod = getSilentMethod('temp' + id); + if (event.silentMethod && prevSumbmitMethod) { + prevSumbmitMethod.replace(event.silentMethod); + return false; + } +}); +``` + +### onPushedQueue + +silentMethod 进入队列后的事件,当行为模式为`queue`或`silent`时有效,如果在 **onBeforePushQueue** 事件中阻止了进入队列,则此函数不会触发。 + +### onFallback + +类似传统的 optimistic ui 的解决方案,我们也提供了请求回退事件,当请求达到最大重试次数或者重试判定失败时都将会触发这个时间,你可以使用它处理一些回退操作。 + +:::warning 警告 + +当绑定回退事件后,即使行为模式是`silent`的请求也不再被持久化,它将会在刷新页面后丢失,这是因为持久化的 silentMethod 通常都是需要确保完成的,而不是回退让用户重新处理的,在这种情况下不应该使用回退功能。 + +::: + +## 保存额外的操作数据 + +在创建或编辑数据项时,之前的章节仅保存了回显数据到`silentMethod.reviewData`中,如果有一些额外的数据需要记录,例如编辑页的菜单可选项等,我们同样需要记录它们以确保在断网情况下还可以选择到它们,此时将这些数据挂载到 silentMethod 实例上一起持久化。 + +一般而言,你可以以任意属性名保存持久化数据,但在 typescript 中会报错,因此为你指定了 `silentMethod.extraData` 属性作为额外数据的保存字段,记得通过 `silentMethod.save()` 持久化数据。 + +## 自定义序列化器 + +默认情况下,alova 是使用 localStorage 进行 silentMethod 的数据持久化的,因此在持久化时会调用`JSON.stringify`转换为字符串,但 json 数据只支持基本数据类型、纯对象以及数组,如果你希望序列化特殊的数据结构例如 Date 实例、RegExp 实例、函数以及自定义的类实例,alova 支持自定义的序列化器来处理它们,在存储时应该如何将它转换为 json 支持的数据结构,在取出时如何转换为原对象结构。 + +```javascript +const regExpSerializer = { + // forward在序列化时被调用 + // 需要判断data是否为目标值,如果不是目标值则返回undefined,表示不处理它 + forward: data => data instanceof RegExp ? data.source : undefined, + + // backward在反序列化时被调用,data为forward中返回的值 + backward: source => new RegExp(source); +}; + +bootSilentFactory({ + // ... + // 通过serializers使用这个序列化器 + serializers: { + customRegExp: regExpSerializer + } +}) +``` + +SilentFactory 内部默认提供了 Date 和 RegExp 的序列化器,你也可以使用相同的 key 来覆盖默认的序列化器 + +```javascript +const defaultSerializers = { + Date: dateSerializer, + RegExp: regExpSerializer +}; +``` + +[阅读 Date 序列化器源码](https://github.com/alovajs/scene/blob/main/src/hooks/silent/serializer/date.ts) + +## 操纵静默队列 + +静默队列用于保证请求时序问题,我们可以任意创建队列,进入队列的请求都将会以**SilentMethod**实例的形式保存在队列中,每个**SilentMethod**除了包含请求信息外,还包含静默提交的相关配置。静默队列是可以生成任意多个的,且支持查找、修改、删除队列中的 silentMethod 实例。 + +### 使用多个静默队列 + +行为模式设置为`queue`和`silent`的请求都会进入静默队列,默认情况下,silentMethod 实例将会被分配到**default**队列中,当需要分配到其他队列时可以在*useSQRequest*中指定`queue`参数。 + +```javascript +useSQRequest(createOrEditTodo, { + // ... + // 指定silentMethod实例进入名称为customQueue的队列中 + queue: 'customQueue', + behavior: 'silent' +}); +``` + +也可以将`queue`指定为函数,要求返回队列名称,这个函数将会在每次发起请求时被调用,函数参数来自于 send 函数。 + +```javascript +const { send } = useSQRequest(createOrEditTodo, { + // ... + // 根据useCustomQueue判断是否进入customQueue队列 + queue: useCustomQueue => (useCustomQueue ? 'customQueue' : 'default'), + behavior: 'silent', + immediate: false +}); +const handleClick = () => { + send(true); +}; +``` + +### 查找 silentMethod + +在之前的[数据补偿](/tutorial/strategy/sensorless-data-interaction/data-compensation)中,我们使用了 [filterSilentMethods](/tutorial/strategy/sensorless-data-interaction/data-compensation#filtersilentmethods) 查找指定队列的 silentMethod 实例,它将返回所有匹配的 silentMethod 实例,这里再介绍两种查找队列的方式: + +#### 查找个 silentMethod 实例 + +使用`getSilentMethod`查询匹配的第一个 silentMethod 实例,用法与 [filterSilentMethods](/tutorial/strategy/sensorless-data-interaction/data-compensation#filtersilentmethods) 相同。 + +```typescript +function filterSilentMethods( + methodNameMatcher?: string | number | RegExp, + queueName?: string, + filterActive?: boolean +): SilentMethod | undefined; +``` + +#### 自定义查找 + +通过导出的 `silentQueueMap` 队列集合自定义查找,`silentQueueMap` 的数据结构为: + +```javascript +const silentQueueMap = { + default: [silentMethod1, silentMethod2 /* ... */], + queueName1: [silentMethod3, silentMethod4 /* ... */], + queueName2: [silentMethod5, silentMethod6 /* ... */] + // ... +}; +``` + +### 更改队列中的 silentMethod + +当查找到你想要的 silentMethod 实例后,可以对这些等待中的 silentMethod 实例进行操纵。 + +#### 替换 silentMethod + +调用`silentMethod.replace`可以在队列中将一个 silentMethod 替换为另一个 silentMethod。 + +```javascript +oldSilentMethod.replace(newSilentMethod); +``` + +#### 移除 silentMethod + +调用`silentMethod.remove`可以将当前 silentMethod 在队列中移除。 + +```javascript +oldSilentMethod.remove(); +``` + +#### 使用 silentQueueMap 更改 silentMethod + +你还可以访问 `silentQueueMap` 自定义更改任意队列的任意数据。 + +```javascript +import { silentQueueMap } from '@alova/scene-*'; + +// 修改default队列中的所有silentMethod +silentQueueMap.default.forEach(silentMethodItem => { + // ... +}); +``` + +## 队列请求延迟 + +有些应用需要频繁地提交数据,例如编辑器类型应用,在编辑过程中进行实时保存,同时不打断用户使用,在这种类型的应用中使用静默提交时将会产生较多的请求信息,不仅会塞满前端缓存还会让服务端接收过多的请求,此时,我们可能不再需要将同步所有保存操作,而是在一段时间内发送一次操作,将有以下两种方案: + +1. 对编辑操作节流,在 n 秒内只发起一次提交,这种方案可能会丢失延迟时间段内的操作记录,导致刷新时只能获取最后一次提交时的状态; +2. 延迟队列内的保存请求,并在延迟时间内只保留最新一次的请求信息,这样就可以减少请求的同时保留最新的编辑状态; + +默认情况下,队列中的 silentMethod 会在上一个响应后立即发送请求,我们可以在启动时通过 `requestWait` 设置 silentMethod 的延迟发送时间,在这段时间里通过操纵队列保留最新的 silentMethod。 + +### 设置请求延迟 + +你可以对指定的队列设置延迟时间,也可以一次设置多个队列。 + +```javascript +bootSilentFactory({ + alova: alovaInst, + // highlight-start + requestWait: [ + // customQueue队列内的每个silentMethod将会延迟5000ms发起请求 + { queue: 'customQueue', wait: 5000 }, + + // 通过正则表达式对名称前缀为delay的队列统一设置3000ms延迟请求时间 + { queue: /^delay/, wait: 3000 } + ] + // highlight-end +}); +``` + +当 `requestWait` 直接设置为数字时默认对 default 队列有效。 + +```javascript +bootSilentFactory({ + alova: alovaInst, + // highlight-start + // default队列内的每个silentMethod将会延迟5000ms发起请求 + requestWait: 5000 + // highlight-end +}); +``` + +### 动态设置请求延迟 + +很多时候我们只希望队列内的特定 silentMethod 设置延迟请求,此时可以通过函数来动态设置请求延迟,指定队列的每个 silentMethod 在发起请求前都将会调用这个函数,来确定延迟的时间。 + +```javascript +bootSilentFactory({ + alova: alovaInst, + // highlight-start + requestWait: [ + { + queue: /^delay, + // 只对url为/edit的post请求延迟5000ms + wait: (silentMethod, queuName) => { + const { type, url, data } = silentMethod.entity; + if (type === 'POST' && url === '/edit') { + return 5000; + } + } + }, + ] + // highlight-end +}); +``` + +同样的,当 `requestWait` 直接设置为函数时默认对 default 队列有效。 + +```javascript +bootSilentFactory({ + alova: alovaInst, + // highlight-start + requestWait: (silentMethod, queuName) => { + const { type, url, data } = silentMethod.entity; + if (type === 'POST' && url === '/edit') { + return 5000; + } + } + // highlight-end +}); +``` + +## 动态设置 method 名称 + +为了便于查找,如果你需要动态设置 silentMethod 内 method 的名称,可以通过调用 setName。 + +```javascript +// 在请求成功时对silentMethod重新设置名称 +onSuccess(({ data, silentMethod }) => { + silentMethod.entity.setName('name' + data.id); +}); +``` + +## 全局的静默提交事件 + +在之前的章节中,我们接触了 `onSilentSubmitSuccess`,我们总共提供了 5 个全局的事件。 + +### onSilentSubmitBoot + +静默工厂启动事件,在静默工厂启动后触发。 + +```typescript +function onSilentSubmitBoot(handler: () => void): OffEventCallback; +``` + +### onBeforeSilentSubmit + +`behavior=silent`的 silentMethod 请求前触发。 + +```typescript +function onBeforeSilentSubmit(handler: (event: GlobalSQEvent)): OffEventCallback; +``` + +### onSilentSubmitSuccess + +`behavior=silent`的 silentMethod 请求成功时触发。 + +```typescript +function onSilentSubmitSuccess( + handler: (event: GlobalSQSuccessEvent) => void +): OffEventCallback; +``` + +### onSilentSubmitError + +`behavior=silent`的 silentMethod 请求失败,但还没有达到最大重试次数时触发。 + +```typescript +function onSilentSubmitError(handler: (event: GlobalSQErrorEvent) => void): OffEventCallback; +``` + +### onSilentSubmitFail + +`behavior=silent`的 silentMethod 在遇到请求失败时,以下 3 种情况将会触发此事件: + +1. 请求重试到达最大次数时触发; +2. 未设置重试次数时,首次请求失败将会触发; +3. 请求未到最大重试次数,但重试判定为不再重试时触发; + +```typescript +function onSilentSubmitFail(handler: (event: GlobalSQFailEvent) => void): OffEventCallback; +``` + +以上的事件绑定函数都将返回解绑函数,你可以在组件卸载前解绑事件。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/02-usePagination.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/02-usePagination.md index 92c4cfbfe..cfd600adb 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/02-usePagination.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/02-usePagination.md @@ -1,6 +1,5 @@ --- title: 分页请求策略 -sidebar_position: 20 --- import Tabs from '@theme/Tabs'; diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/03-useForm.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/03-useForm.md index 1ed893ee2..bcb3dd778 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/03-useForm.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/03-useForm.md @@ -1,6 +1,5 @@ --- title: 表单提交策略 -sidebar_position: 30 --- import Tabs from '@theme/Tabs'; diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/04-tokenAuthentication.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/04-tokenAuthentication.md index f032d35e0..36f51d8fb 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/04-tokenAuthentication.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/04-tokenAuthentication.md @@ -1,6 +1,5 @@ --- title: Token认证拦截器 -sidebar_position: 40 --- import Tabs from '@theme/Tabs'; diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/05-useUploader.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/05-useUploader.md index de9d5670b..56a2b9a5f 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/05-useUploader.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/05-useUploader.md @@ -1,177 +1,176 @@ ---- -title: 通用的上传策略 -sidebar_position: 50 ---- - -:::warning - -此策略暂未实现,以下为设计文档 - -::: - -> 在使用扩展 hooks 前,确保你已熟悉了 alova 的基本使用。 - -简便的文件上传,传入 base64、file 对象、blob 对象、arraybuffer 对象、Canvas 都可以直接上传,内部实现自动转换,使用场景有: - -1. 上传组件的样式不符合时 -2. 上传数据格式多样时 - -## 功能需求 - -1. 支持单个、多个文件上传,多个文件同时上传时可以同时上传 -2. 多个文件上传进度、总进度 -3. 自动转换数据格式为 File -4. 失败可手动触发重新上传 -5. 限制文件格式和大小 -6. 文件数据回显 -7. 自动的缩略图(适用于图片) -8. 文件上传总进度 - -## useUploader 的全部参数一览 - -```javascript -const { - fileList, // 文件数据列表,每项包含文件名、文件路径、上传状态、上传进度,具体格式见下面 - file, // 第一个文件数据项,可以使用计算属性,一般用于上传单个文件使用 - loading, // 是否正在上传,boolean 值 - progress, // 上传总进度,具体格式见下面 - appendFiles, // 添加文件数据项(添加后自动转换为 File 对象) - upload, // 上传操作,传入下标指定哪个项上传(用于错误重试),未传入上传全部未成功的项 - onFilesAppend, // 添加文件事件 - onExceed, // 文件数量被限制事件 - onFormatMismatch, // 文件格式不匹配事件 - onSuccess, // 每个文件上传成功事件 - onError, // 每个文件上传失败事件 - onComplete // 每个文件上传完成事件 -} = useUploader(({ file, name }) => uploadFile(file, name), { - limit: 3, // 限制上传文件 - accept: ['png', 'jpg', 'pdf'], // 接受的自动转换后的文件格式 - imageTempLink: true, // 是否生成临时图片路径,用于在图片上传完成前也可以展示图片内容,默认为 false - - // 设置一个函数,当上传成功时,可以自动将 fileList 项中的 src 字段替换为服务器上的文件地址,在上传图片时常用 - // data 为响应数据,此函数返回服务端的文件地址 - // 如果未指定此函数则不会有替换行为 - replaceSrcFromResponse: data => data.imgPath -}); -``` - -## useUploader 参数的详细格式及解释 - -参数格式使用 typescript 表示,便于理解 - -### 返回的响应式状态 - -以下都是响应式的值 - -```typescript -// file值的格式 -interface file { - src?: string; // 临时路径或上传成功后的文件路径,非图片 - file: File; // File 对象 - status: 0 | 1 | 2 | 3; // 0 表示未上传,1 表示上传中,2 表示已完成, 3 表示上传错误 - error?: Error; // 错误对象,当上传错误时需要将错误对象赋值上去 - progress: { - loaded: number; // 已上传大小 - total: number; // 文件总大小 - }; -} -// fileList值的格式 -type fileList = file[]; - -// loading值的格式 -type loading = boolean; // 是否正在上传 - -// 上传总进度信息 -interface progress { - loaded: number; // 已上传的大小,多个文件叠加 - total: number; // 文件总大小,多个文件叠加 - count: number; // 正在上传的文件总数 - successCount: number; // 已成功上传的文件数 - failCount: number; // 上传失败的文件数 -} -``` - -### 返回的操作函数 - -```typescript -interface RawFile { - file?: File | string | Blob | ArrayBuffer | HTMLCanvasElement; // 可以传入 base64、file 对象、blob 对象、arraybuffer,canvas 元素,回显时可不传 - src?: string; // 文件预览地址,如果传了即使 imageTempLink 为 true 也不会覆盖它 - name?: string; // 文件名,file 为非 File 对象时必填,在 new File 时用到 - mimeType?: string; // 内部转换为 File 对象时的文件类型,在 new File 时用到,建议 file 为非 File 对象时传入 - status?: 0 | 1 | 2 | 3; // 0 表示未上传,1 表示上传中,2 表示已完成,3 表示上传失败,不传默认为 0 -} - -/** - * 追加上传的文件进去,追加后会自动将列表的 file 项转换为 File 对象,此时 name 必须有值 - */ -type appendFiles = ( - files: RawFile | RawFile[], // 单个文件时传对象、多个文件时传数组 - start?: number // 从 fileList 哪个位置开始追加 -) => number; // 追加成功的个数,可能因为数量限制、格式限制而追加失败 - -/** - * 执行上传操作 - * 如果未传入参数则会自动将 fileList 内所有 file 属性有值的、status 为 0 和 3(未上传和上传失败)的重新发起上传请求 - * 如果传入了参数下标,则将 fileList 对应下标的项重新组成一个数组,也通过上面的条件过滤出可上传的进行上传请求,但此时如果有不符合条件的需要报错,而不是忽略 - * - * 发起上传操作后: - * 多个文件的上传将会并行发起上传请求,也就是将符合上传条件的文件数组遍历,依次调用 useUploader 的第一个回调函数获取 Method 对象发送请求,内部可以使用 useRequst 实现,因为 fileList 中有很多属性是在 useRequest 都可以提供 - */ -type upload = (...indexes: number[]) => void; -``` - -### 事件绑定函数 - -每个事件绑定的回调函数都会接收一个事件对象,格式如下 - -```typescript -// 触发时机:调用appendFiles添加文件时触发 -type onFilesAppend = (handler: (event: AlovaFileEvent) => void) => void; -interface AlovaFileEvent { - files: file[]; // 符合条件的,并且转换过后的文件数组 - rawFiles: RawFile[]; // 在 appendFiles 中传入的符合条件的数据数组,是还未转换过后的文件数组 - allRawFiles: RawFile[]; // 在 appendFiles 中传入的原数据数组 -} - -// 触发时机:当调用appendFiles时,文件数量被限制时触发 -type onExceed = (handler: (event: AlovaFileExceededEvent) => void) => void; -interface AlovaFileExceededEvent extends AlovaFileEvent { - exceeded: number; // 超过的数量 - limit: number; // 总限制数量 -} - -// 触发时机:当调用appendFiles时,有文件格式不匹配时触发 -type onFormatMismatch = (handler: (event: AlovaFileMismatchEvent) => void) => void; -interface AlovaFileMismatchEvent extends AlovaFileEvent { - mismatchFiles: RawFile[]; // 格式不匹配的文件数组 -} - -// 触发时机:每个文件上传成功时触发,内部可复用useRequest的onSuccess事件 -type onSuccess = (handler: (event: AlovaFileSuccessEvent) => void) => void; -interface AlovaFileSuccessEvent extends AlovaSuccessEvent { - file: file; // 上传成功的文件项 -} - -// 触发时机:每个文件上传失败时触发,内部可复用useRequest的onError事件 -type onError = (handler: (event: AlovaFileErrorEvent) => void) => void; -interface AlovaFileErrorEvent extends AlovaErrorEvent { - file: file; // 上传失败的文件项 -} - -// 触发时机:每个文件上传完成时触发,内部可复用useRequest的onComplete事件 -type onComplete = (handler: (event: AlovaFileCompleteEvent) => void) => void; -interface AlovaFileCompleteEvent extends AlovaCompleteEvent { - file: file; // 上传成功的文件项 -} -``` - -## 开发指南 - -开发前请仔细阅读[开发指南](/contributing/developing-guidelines) - -开发此 use hook 需要开发以下内容: - -1. 在 src/hooks 下编写 useUploader 功能代码 -2. useUploader 功能的完整单元测试,建议在 vue 和 react 下测试 -3. useUploader 的 typescript 类型声明,需要分别在`packages/scene-react/typings/index.d.ts`、`packages/scene-vue/typings/index.d.ts`、`packages/scene-svelte/typings/index.d.ts`下添加。公共的类型声明可以放在`typings/general.d.ts`中,打包时会将此文件分别复制到子包的`typings`文件夹下,也可以手动运行`npm run cp:files`复制文件夹。 +--- +title: 通用的上传策略 +--- + +:::warning + +此策略暂未实现,以下为设计文档 + +::: + +> 在使用扩展 hooks 前,确保你已熟悉了 alova 的基本使用。 + +简便的文件上传,传入 base64、file 对象、blob 对象、arraybuffer 对象、Canvas 都可以直接上传,内部实现自动转换,使用场景有: + +1. 上传组件的样式不符合时 +2. 上传数据格式多样时 + +## 功能需求 + +1. 支持单个、多个文件上传,多个文件同时上传时可以同时上传 +2. 多个文件上传进度、总进度 +3. 自动转换数据格式为 File +4. 失败可手动触发重新上传 +5. 限制文件格式和大小 +6. 文件数据回显 +7. 自动的缩略图(适用于图片) +8. 文件上传总进度 + +## useUploader 的全部参数一览 + +```javascript +const { + fileList, // 文件数据列表,每项包含文件名、文件路径、上传状态、上传进度,具体格式见下面 + file, // 第一个文件数据项,可以使用计算属性,一般用于上传单个文件使用 + loading, // 是否正在上传,boolean 值 + progress, // 上传总进度,具体格式见下面 + appendFiles, // 添加文件数据项(添加后自动转换为 File 对象) + upload, // 上传操作,传入下标指定哪个项上传(用于错误重试),未传入上传全部未成功的项 + onFilesAppend, // 添加文件事件 + onExceed, // 文件数量被限制事件 + onFormatMismatch, // 文件格式不匹配事件 + onSuccess, // 每个文件上传成功事件 + onError, // 每个文件上传失败事件 + onComplete // 每个文件上传完成事件 +} = useUploader(({ file, name }) => uploadFile(file, name), { + limit: 3, // 限制上传文件 + accept: ['png', 'jpg', 'pdf'], // 接受的自动转换后的文件格式 + imageTempLink: true, // 是否生成临时图片路径,用于在图片上传完成前也可以展示图片内容,默认为 false + + // 设置一个函数,当上传成功时,可以自动将 fileList 项中的 src 字段替换为服务器上的文件地址,在上传图片时常用 + // data 为响应数据,此函数返回服务端的文件地址 + // 如果未指定此函数则不会有替换行为 + replaceSrcFromResponse: data => data.imgPath +}); +``` + +## useUploader 参数的详细格式及解释 + +参数格式使用 typescript 表示,便于理解 + +### 返回的响应式状态 + +以下都是响应式的值 + +```typescript +// file值的格式 +interface file { + src?: string; // 临时路径或上传成功后的文件路径,非图片 + file: File; // File 对象 + status: 0 | 1 | 2 | 3; // 0 表示未上传,1 表示上传中,2 表示已完成, 3 表示上传错误 + error?: Error; // 错误对象,当上传错误时需要将错误对象赋值上去 + progress: { + loaded: number; // 已上传大小 + total: number; // 文件总大小 + }; +} +// fileList值的格式 +type fileList = file[]; + +// loading值的格式 +type loading = boolean; // 是否正在上传 + +// 上传总进度信息 +interface progress { + loaded: number; // 已上传的大小,多个文件叠加 + total: number; // 文件总大小,多个文件叠加 + count: number; // 正在上传的文件总数 + successCount: number; // 已成功上传的文件数 + failCount: number; // 上传失败的文件数 +} +``` + +### 返回的操作函数 + +```typescript +interface RawFile { + file?: File | string | Blob | ArrayBuffer | HTMLCanvasElement; // 可以传入 base64、file 对象、blob 对象、arraybuffer,canvas 元素,回显时可不传 + src?: string; // 文件预览地址,如果传了即使 imageTempLink 为 true 也不会覆盖它 + name?: string; // 文件名,file 为非 File 对象时必填,在 new File 时用到 + mimeType?: string; // 内部转换为 File 对象时的文件类型,在 new File 时用到,建议 file 为非 File 对象时传入 + status?: 0 | 1 | 2 | 3; // 0 表示未上传,1 表示上传中,2 表示已完成,3 表示上传失败,不传默认为 0 +} + +/** + * 追加上传的文件进去,追加后会自动将列表的 file 项转换为 File 对象,此时 name 必须有值 + */ +type appendFiles = ( + files: RawFile | RawFile[], // 单个文件时传对象、多个文件时传数组 + start?: number // 从 fileList 哪个位置开始追加 +) => number; // 追加成功的个数,可能因为数量限制、格式限制而追加失败 + +/** + * 执行上传操作 + * 如果未传入参数则会自动将 fileList 内所有 file 属性有值的、status 为 0 和 3(未上传和上传失败)的重新发起上传请求 + * 如果传入了参数下标,则将 fileList 对应下标的项重新组成一个数组,也通过上面的条件过滤出可上传的进行上传请求,但此时如果有不符合条件的需要报错,而不是忽略 + * + * 发起上传操作后: + * 多个文件的上传将会并行发起上传请求,也就是将符合上传条件的文件数组遍历,依次调用 useUploader 的第一个回调函数获取 Method 对象发送请求,内部可以使用 useRequst 实现,因为 fileList 中有很多属性是在 useRequest 都可以提供 + */ +type upload = (...indexes: number[]) => void; +``` + +### 事件绑定函数 + +每个事件绑定的回调函数都会接收一个事件对象,格式如下 + +```typescript +// 触发时机:调用appendFiles添加文件时触发 +type onFilesAppend = (handler: (event: AlovaFileEvent) => void) => void; +interface AlovaFileEvent { + files: file[]; // 符合条件的,并且转换过后的文件数组 + rawFiles: RawFile[]; // 在 appendFiles 中传入的符合条件的数据数组,是还未转换过后的文件数组 + allRawFiles: RawFile[]; // 在 appendFiles 中传入的原数据数组 +} + +// 触发时机:当调用appendFiles时,文件数量被限制时触发 +type onExceed = (handler: (event: AlovaFileExceededEvent) => void) => void; +interface AlovaFileExceededEvent extends AlovaFileEvent { + exceeded: number; // 超过的数量 + limit: number; // 总限制数量 +} + +// 触发时机:当调用appendFiles时,有文件格式不匹配时触发 +type onFormatMismatch = (handler: (event: AlovaFileMismatchEvent) => void) => void; +interface AlovaFileMismatchEvent extends AlovaFileEvent { + mismatchFiles: RawFile[]; // 格式不匹配的文件数组 +} + +// 触发时机:每个文件上传成功时触发,内部可复用useRequest的onSuccess事件 +type onSuccess = (handler: (event: AlovaFileSuccessEvent) => void) => void; +interface AlovaFileSuccessEvent extends AlovaSuccessEvent { + file: file; // 上传成功的文件项 +} + +// 触发时机:每个文件上传失败时触发,内部可复用useRequest的onError事件 +type onError = (handler: (event: AlovaFileErrorEvent) => void) => void; +interface AlovaFileErrorEvent extends AlovaErrorEvent { + file: file; // 上传失败的文件项 +} + +// 触发时机:每个文件上传完成时触发,内部可复用useRequest的onComplete事件 +type onComplete = (handler: (event: AlovaFileCompleteEvent) => void) => void; +interface AlovaFileCompleteEvent extends AlovaCompleteEvent { + file: file; // 上传成功的文件项 +} +``` + +## 开发指南 + +开发前请仔细阅读[开发指南](/contributing/developing-guidelines) + +开发此 use hook 需要开发以下内容: + +1. 在 src/hooks 下编写 useUploader 功能代码 +2. useUploader 功能的完整单元测试,建议在 vue 和 react 下测试 +3. useUploader 的 typescript 类型声明,需要分别在`packages/scene-react/typings/index.d.ts`、`packages/scene-vue/typings/index.d.ts`、`packages/scene-svelte/typings/index.d.ts`下添加。公共的类型声明可以放在`typings/general.d.ts`中,打包时会将此文件分别复制到子包的`typings`文件夹下,也可以手动运行`npm run cp:files`复制文件夹。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/06-useAutoRequest.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/06-useAutoRequest.md index 88d17cd8b..802027424 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/06-useAutoRequest.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/06-useAutoRequest.md @@ -1,6 +1,5 @@ --- title: 自动拉取数据 -sidebar_position: 60 --- import Tabs from '@theme/Tabs'; diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/07-useBreakpointUploader.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/07-useBreakpointUploader.md index 514a7bd83..2c19b9dff 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/07-useBreakpointUploader.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/07-useBreakpointUploader.md @@ -1,6 +1,5 @@ ---- -title: 断点续传策略 -sidebar_position: 70 ---- - -敬请期待... +--- +title: 断点续传策略 +--- + +敬请期待... diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/08-useCaptcha.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/08-useCaptcha.md index 0d07a8203..57e3d609e 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/08-useCaptcha.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/08-useCaptcha.md @@ -1,6 +1,5 @@ --- title: 发送验证码 -sidebar_position: 80 --- import Tabs from '@theme/Tabs'; diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/09-actionDelegationMiddleware.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/09-actionDelegationMiddleware.md index 8508856a2..2bc678f23 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/09-actionDelegationMiddleware.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/09-actionDelegationMiddleware.md @@ -1,6 +1,5 @@ --- title: 跨组件触发请求 -sidebar_position: 90 --- import Tabs from '@theme/Tabs'; diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/10-useSerialRequest.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/10-useSerialRequest.md index 05ba766a2..464f9574a 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/10-useSerialRequest.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/10-useSerialRequest.md @@ -1,6 +1,5 @@ --- title: 串行请求的useRequest -sidebar_position: 100 --- import Tabs from '@theme/Tabs'; diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/11-useSerialWatcher.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/11-useSerialWatcher.md index cf12a0c0d..d45dcbd8f 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/11-useSerialWatcher.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/11-useSerialWatcher.md @@ -1,6 +1,5 @@ --- title: 串行请求的useWatcher -sidebar_position: 110 --- import Tabs from '@theme/Tabs'; diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/12-useRetriableRequest.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/12-useRetriableRequest.md index 086f1e48a..08c389321 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/12-useRetriableRequest.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/12-useRetriableRequest.md @@ -1,6 +1,5 @@ --- title: 请求重试策略 -sidebar_position: 120 --- import Tabs from '@theme/Tabs'; diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/13-useSSE.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/13-useSSE.md index fc49ad9b9..321cbce76 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/13-useSSE.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/13-useSSE.md @@ -1,6 +1,5 @@ --- title: Server-sent events发送请求 -sidebar_position: 130 --- import Tabs from '@theme/Tabs'; diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/14-rateLimitMiddleware.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/14-rateLimitMiddleware.md index 60c1907fe..f899928a3 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/14-rateLimitMiddleware.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/14-rateLimitMiddleware.md @@ -1,8 +1,7 @@ ---- -title: 请求速率限制 -sidebar_position: 140 ---- - -设置每个间隔应立即执行的请求数,其他请求将自动延迟。 - -敬请期待... +--- +title: 请求速率限制 +--- + +设置每个间隔应立即执行的请求数,其他请求将自动延迟。 + +敬请期待... diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/01-use-fetcher.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/01-use-fetcher.md index 696148d51..ac20160fd 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/01-use-fetcher.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/01-use-fetcher.md @@ -1,307 +1,306 @@ ---- -title: 数据拉取 -sidebar_position: 10 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -当你有以下需求时: - -1. 预加载后续流程中将会使用到的数据并存放在缓存中,让用户不再等待数据加载的过程; -2. 便捷地实现跨页面更新数据(类似全局状态),例如修改 todo 列表的某一项后重新拉取最新数据,响应后将刷新界面。 - -`useFetcher`就是用于实现以上场景的 hook,通过它获取的响应数据不能直接接收到,但通过它拉取的数据除了会更新缓存外还会更新对应的状态,从而重新渲染视图。 - -## 预加载数据 - -我们来实现一个分页列表中,自动预加载下一页数据,在预加载数据前请确保使用的 Method 实例已开启了缓存。 - - - - -```html - - - -``` - - - - -```jsx -import { useState } from 'react'; - -// method实例创建函数 -const getTodoList = currentPage => { - return alovaInstance.Get('/todo/list', { - localCache: 60000, - params: { - currentPage, - pageSize: 10 - } - }); -}; - -const App = () => { - const { - // fetching属性与loading相同,发送拉取请求时为true,请求结束后为false - fetching, - error, - onSuccess, - onError, - onComplete, - - // 调用fetch后才会发送请求拉取数据,可以重复调用fetch多次拉取不同接口的数据 - fetch - } = useFetcher({ - updateState: false - }); - const [currentPage, setCurrentPage] = useState(1); - const { data, onSuccess } = useWatcher(() => getTodoList(currentPage), [currentPage], { - immediate: true - }); - - // 在当前页加载成功后,传入下一页的method实例,即可预拉取下一页的数据 - onSuccess(() => { - fetch(getTodoList(currentPage + 1)); - }); - - return ( - <> - {fetching ?
Fetching...
: null} - {/* 列表视图 */} - - ); -}; -``` - -
- - -```html - - -{#if fetching} -
Fetching...
-{/if} - -``` - -
- - -```html - - - -``` - - -
- -:::warning - -以上示例在调用`useFetcher`时设置了`updateState`为 false,这是因为默认情况下 fetch 时会自动触发跨组件更新状态,导致视图重新渲染,在预拉取的数据与当前请求的数据为同一份`data`时可以关闭它,以免影响视图错误。 - -::: - -## 跨模块/组件更新视图 - -下面我们来实现修改一条 todo 数据,并重新拉取最新的 todo 列表数据,让视图更新。我们可能并不知道 todo 列表当前位于第几页,此时在使用`fetch`函数时可以使用[method 匹配器](/tutorial/advanced/method-matcher)来动态拉取当前页的数据。 - -> method 匹配器是用于,在已请求过的 method 实例中,查找符合条件的 method 实例。 - -首先,为 todo 列表的 method 实例设置名称,用于在无法直接指定 Method 实例时,过滤出需要的 Method 实例。 - -```javascript title="api/todoList.js" -const getTodoList = currentPage => { - return alovaInstance.Get('/todo/list', { - // highlight-start - name: 'todoList', - // highlight-end - params: { - currentPage, - pageSize: 10 - } - }); -}; -``` - -然后在`EditTodo`组件中,通过`fetch`函数在已请求过的 Method 实例中,动态查找最后一个 name 为`todoList`进行数据拉取。 - -```javascript title="EditTodo Component" -const { fetch } = useFetcher(); - -// 在事件中触发数据拉取 -const handleSubmit = () => { - // 提交数据... - await fetch({ - name: 'todoList', - filter: (method, index, ary) => { - // 返回true来指定需要拉取的Method实例 - return index === ary.length - 1; - } - }); -}; -``` - -:::warning 注意事项 - -useFetcher 请求完成后只更新缓存,且如果发现这个 Method 实例在之前使用过 useHook 请求过,那么也会更新这个 useHook 创建的`data`状态,从而保证页面数据一致,这是`useFetcher`用于跨模块/组件更新视图的保证。 - -::: - -> 更多 method 匹配器的使用方法见 [method 匹配器](/tutorial/advanced/method-matcher)。 - -## 强制发送请求 - -和`useRequest`和`useWatcher`相同,更多请阅读[强制请求](/tutorial/cache/force-request)。 - -## 绑定响应回调 - -useFetcher 也支持绑定`onSuccess/onError/onComplete`回调函数。 - -```javascript -const { onSuccess, onError, onComplete } = useFetcher(); -``` - -具体请阅读[响应处理](/tutorial/combine-framework/response)。 - -## send 函数参数传递规则 - -与`useRequest`和`useWatcher`不同的是,fetch 函数的自定义参数是从第二个参数开始的,它们也将分别被事件回调和`force`函数接收。 - -```javascript -const { onSuccess, fetch } = useFetcher(); -onSuccess(({ sendArgs }) => { - // sendArgs的值为['test arg'] -}); - -fetch(getTodoList(), 'test arg'); -``` - -具体请阅读[send 函数参数传递规则](/tutorial/combine-framework/receive-params)。 - -## useRequest 与 useFetcher 对比 - -1. useFetcher 不返回`data`字段,预拉取的数据将保存在缓存中,以及更新对应位置的状态数据; -2. 将`loading`改名为了`fetching`; -3. 没有`send`函数,但多了一个`fetch`函数,可以重复利用 fetch 函数拉取不同接口的数据,此时你可以使用 `fetching` 和 `error` 状态统一渲染视图,从而达到统一处理的目的; +--- +title: 数据拉取 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +当你有以下需求时: + +1. 预加载后续流程中将会使用到的数据并存放在缓存中,让用户不再等待数据加载的过程; +2. 便捷地实现跨页面更新数据(类似全局状态),例如修改 todo 列表的某一项后重新拉取最新数据,响应后将刷新界面。 + +`useFetcher`就是用于实现以上场景的 hook,通过它获取的响应数据不能直接接收到,但通过它拉取的数据除了会更新缓存外还会更新对应的状态,从而重新渲染视图。 + +## 预加载数据 + +我们来实现一个分页列表中,自动预加载下一页数据,在预加载数据前请确保使用的 Method 实例已开启了缓存。 + + + + +```html + + + +``` + + + + +```jsx +import { useState } from 'react'; + +// method实例创建函数 +const getTodoList = currentPage => { + return alovaInstance.Get('/todo/list', { + localCache: 60000, + params: { + currentPage, + pageSize: 10 + } + }); +}; + +const App = () => { + const { + // fetching属性与loading相同,发送拉取请求时为true,请求结束后为false + fetching, + error, + onSuccess, + onError, + onComplete, + + // 调用fetch后才会发送请求拉取数据,可以重复调用fetch多次拉取不同接口的数据 + fetch + } = useFetcher({ + updateState: false + }); + const [currentPage, setCurrentPage] = useState(1); + const { data, onSuccess } = useWatcher(() => getTodoList(currentPage), [currentPage], { + immediate: true + }); + + // 在当前页加载成功后,传入下一页的method实例,即可预拉取下一页的数据 + onSuccess(() => { + fetch(getTodoList(currentPage + 1)); + }); + + return ( + <> + {fetching ?
Fetching...
: null} + {/* 列表视图 */} + + ); +}; +``` + +
+ + +```html + + +{#if fetching} +
Fetching...
+{/if} + +``` + +
+ + +```html + + + +``` + + +
+ +:::warning + +以上示例在调用`useFetcher`时设置了`updateState`为 false,这是因为默认情况下 fetch 时会自动触发跨组件更新状态,导致视图重新渲染,在预拉取的数据与当前请求的数据为同一份`data`时可以关闭它,以免影响视图错误。 + +::: + +## 跨模块/组件更新视图 + +下面我们来实现修改一条 todo 数据,并重新拉取最新的 todo 列表数据,让视图更新。我们可能并不知道 todo 列表当前位于第几页,此时在使用`fetch`函数时可以使用[method 匹配器](/tutorial/advanced/method-matcher)来动态拉取当前页的数据。 + +> method 匹配器是用于,在已请求过的 method 实例中,查找符合条件的 method 实例。 + +首先,为 todo 列表的 method 实例设置名称,用于在无法直接指定 Method 实例时,过滤出需要的 Method 实例。 + +```javascript title="api/todoList.js" +const getTodoList = currentPage => { + return alovaInstance.Get('/todo/list', { + // highlight-start + name: 'todoList', + // highlight-end + params: { + currentPage, + pageSize: 10 + } + }); +}; +``` + +然后在`EditTodo`组件中,通过`fetch`函数在已请求过的 Method 实例中,动态查找最后一个 name 为`todoList`进行数据拉取。 + +```javascript title="EditTodo Component" +const { fetch } = useFetcher(); + +// 在事件中触发数据拉取 +const handleSubmit = () => { + // 提交数据... + await fetch({ + name: 'todoList', + filter: (method, index, ary) => { + // 返回true来指定需要拉取的Method实例 + return index === ary.length - 1; + } + }); +}; +``` + +:::warning 注意事项 + +useFetcher 请求完成后只更新缓存,且如果发现这个 Method 实例在之前使用过 useHook 请求过,那么也会更新这个 useHook 创建的`data`状态,从而保证页面数据一致,这是`useFetcher`用于跨模块/组件更新视图的保证。 + +::: + +> 更多 method 匹配器的使用方法见 [method 匹配器](/tutorial/advanced/method-matcher)。 + +## 强制发送请求 + +和`useRequest`和`useWatcher`相同,更多请阅读[强制请求](/tutorial/cache/force-request)。 + +## 绑定响应回调 + +useFetcher 也支持绑定`onSuccess/onError/onComplete`回调函数。 + +```javascript +const { onSuccess, onError, onComplete } = useFetcher(); +``` + +具体请阅读[响应处理](/tutorial/combine-framework/response)。 + +## send 函数参数传递规则 + +与`useRequest`和`useWatcher`不同的是,fetch 函数的自定义参数是从第二个参数开始的,它们也将分别被事件回调和`force`函数接收。 + +```javascript +const { onSuccess, fetch } = useFetcher(); +onSuccess(({ sendArgs }) => { + // sendArgs的值为['test arg'] +}); + +fetch(getTodoList(), 'test arg'); +``` + +具体请阅读[send 函数参数传递规则](/tutorial/combine-framework/receive-params)。 + +## useRequest 与 useFetcher 对比 + +1. useFetcher 不返回`data`字段,预拉取的数据将保存在缓存中,以及更新对应位置的状态数据; +2. 将`loading`改名为了`fetching`; +3. 没有`send`函数,但多了一个`fetch`函数,可以重复利用 fetch 函数拉取不同接口的数据,此时你可以使用 `fetching` 和 `error` 状态统一渲染视图,从而达到统一处理的目的; diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/02-update-across-components.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/02-update-across-components.md index 005dba6f1..48f298436 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/02-update-across-components.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/02-update-across-components.md @@ -1,102 +1,101 @@ ---- -title: 跨组件更新状态 -sidebar_position: 20 ---- - -有这个一个场景,当用户点开 todo 列表中的某一项,进入 todo 详情页并对它执行了编辑,此时我们希望上一页中的 todo 列表数据,在不重新情况的情况下也更新为编辑后的内容,`useFetcher`就不再适用了。 - -此时可以使用`updateState`来更新任意模块/页面下的已存在的响应状态,它可以查找并修改其他模块内的响应式状态。 - -[这里有个`updateState`的 示例](/tutorial/example/update-state) - -## 使用 method 实例查找响应状态 - -当确定更新的响应状态对应的 method 实例时,你可以在`updateState`中传入此 method 实例,它将查找这个实例下是否存在对应的响应状态,并在回调函数中提供给你进行修改,最后将修改后的数据返回即可。 - -```javascript -import { updateState } from 'alova'; - -// 正在编辑的todo项 -const editingTodo = { - id: 1, - title: 'todo1', - time: '09:00' -}; - -const { send, onSuccess } = useRequest(createTodoPoster, { immediate: false }); -onSuccess(() => { - // highlight-start - // 固定修改第一页的todo数据数据 - // updateState将返回是否更新成功 - const updated = updateState(getTodoList(1), todoList => { - return todoList.map(item => { - if (item.id === editingTodo.id) { - return { - ...item, - ...editingTodo - }; - } - return item; - }); - }); - // highlight-end -}); -``` - -:::warning 注意 - -1. 通过`updateState`更新状态时,如果检测到缓存(内存缓存和持久化缓存)也将会更新新的数据更新缓存。 -2. 只有当使用 useRequest、useWatcher 发起过请求时,alova 才会管理 hook 返回的状态,原因是响应状态是通过一个 Method 实例来生成 key 并保存的,但在未发起请求时 Method 实例内的 url、params、query、headers 等参数都还不确定。 - -::: - -## 动态更新响应状态 - -可能有时候你并不确定需要更新 method 下的响应状态,但却知道以什么方式来找到需要失效的缓存数据,我们可以使用 [method 匹配器](/tutorial/advanced/method-matcher) 来动态查找对应的 method 实例。以下例子展示了为名称为 todoList 的 method 实例对应的列表添加一条数据。 - -```javascript -updateState('todoList', todoListRaw => { - todoListRaw.push({ - title: 'new todo', - time: '10:00' - }); - return todoListRaw; -}); -``` - -关于 [method 匹配器](/tutorial/advanced/method-matcher) 将在后面的章节中详细介绍。 - -## 监听匹配事件 - -在动态更新响应状态时,有时候你可能想要匹配到 method 实例时做一些处理,或者想要获取匹配的 method 实例,`updateState`还可以传入第三个参数来设置匹配事件来达到这些目的。 - -```javascript -updateState( - 'todoList', - todoListRaw => { - // ... - }, - { - // 匹配到method实例时调用,参数为匹配到的method实例 - onMatch: method => { - // ... - } - } -); -``` - -:::warning ⚠️ 请确保组件未销毁 - -`updateState`默认会查找由 alova 的 useHooks 发送请求时所创建的响应状态,但由于防止内存溢出,一个组件的销毁同时也会回收它内部创建的所有状态,因此在使用`updateState`时请确保你希望更新的响应状态对应的容器组件未被销毁,否则将无法查找到对应的响应状态而导致更新失败。 - -这个问题常常出现在跨页面更新状态,我们容易忽略的是,在默认情况下,当页面跳转时上一个页面已被销毁,因此,如果你希望跨页面更新状态,这里有两个建议: - -1. 将页面组件持久化,以保证被更新的状态还可以被查找到; -2. 使用 [setCache](/tutorial/cache/set-and-query) 替代`updateState`,其原理是,当上一个页面的请求存在缓存时,更新它的缓存以保证再次创建页面时,所触发的请求可以命中更新后的缓存,达到同样的效果。 - -::: - -## 注意事项 - -1. 在实际使用中,不管是使用`useRequest`还是`useWatcher`发送请求时,你都可以调用`send`函数来指定不同参数重复发送请求,这些 use hook 返回的响应状态会被多个 method 实例引用,因此你可以选择任意一个 method 实例都可以匹配到同一个响应状态值; -2. 当动态查找更新响应状态时,method 匹配器找到了多个 method 实例,将会以第一个实例为准; +--- +title: 跨组件更新状态 +--- + +有这个一个场景,当用户点开 todo 列表中的某一项,进入 todo 详情页并对它执行了编辑,此时我们希望上一页中的 todo 列表数据,在不重新情况的情况下也更新为编辑后的内容,`useFetcher`就不再适用了。 + +此时可以使用`updateState`来更新任意模块/页面下的已存在的响应状态,它可以查找并修改其他模块内的响应式状态。 + +[这里有个`updateState`的 示例](/tutorial/example/update-state) + +## 使用 method 实例查找响应状态 + +当确定更新的响应状态对应的 method 实例时,你可以在`updateState`中传入此 method 实例,它将查找这个实例下是否存在对应的响应状态,并在回调函数中提供给你进行修改,最后将修改后的数据返回即可。 + +```javascript +import { updateState } from 'alova'; + +// 正在编辑的todo项 +const editingTodo = { + id: 1, + title: 'todo1', + time: '09:00' +}; + +const { send, onSuccess } = useRequest(createTodoPoster, { immediate: false }); +onSuccess(() => { + // highlight-start + // 固定修改第一页的todo数据数据 + // updateState将返回是否更新成功 + const updated = updateState(getTodoList(1), todoList => { + return todoList.map(item => { + if (item.id === editingTodo.id) { + return { + ...item, + ...editingTodo + }; + } + return item; + }); + }); + // highlight-end +}); +``` + +:::warning 注意 + +1. 通过`updateState`更新状态时,如果检测到缓存(内存缓存和持久化缓存)也将会更新新的数据更新缓存。 +2. 只有当使用 useRequest、useWatcher 发起过请求时,alova 才会管理 hook 返回的状态,原因是响应状态是通过一个 Method 实例来生成 key 并保存的,但在未发起请求时 Method 实例内的 url、params、query、headers 等参数都还不确定。 + +::: + +## 动态更新响应状态 + +可能有时候你并不确定需要更新 method 下的响应状态,但却知道以什么方式来找到需要失效的缓存数据,我们可以使用 [method 匹配器](/tutorial/advanced/method-matcher) 来动态查找对应的 method 实例。以下例子展示了为名称为 todoList 的 method 实例对应的列表添加一条数据。 + +```javascript +updateState('todoList', todoListRaw => { + todoListRaw.push({ + title: 'new todo', + time: '10:00' + }); + return todoListRaw; +}); +``` + +关于 [method 匹配器](/tutorial/advanced/method-matcher) 将在后面的章节中详细介绍。 + +## 监听匹配事件 + +在动态更新响应状态时,有时候你可能想要匹配到 method 实例时做一些处理,或者想要获取匹配的 method 实例,`updateState`还可以传入第三个参数来设置匹配事件来达到这些目的。 + +```javascript +updateState( + 'todoList', + todoListRaw => { + // ... + }, + { + // 匹配到method实例时调用,参数为匹配到的method实例 + onMatch: method => { + // ... + } + } +); +``` + +:::warning ⚠️ 请确保组件未销毁 + +`updateState`默认会查找由 alova 的 useHooks 发送请求时所创建的响应状态,但由于防止内存溢出,一个组件的销毁同时也会回收它内部创建的所有状态,因此在使用`updateState`时请确保你希望更新的响应状态对应的容器组件未被销毁,否则将无法查找到对应的响应状态而导致更新失败。 + +这个问题常常出现在跨页面更新状态,我们容易忽略的是,在默认情况下,当页面跳转时上一个页面已被销毁,因此,如果你希望跨页面更新状态,这里有两个建议: + +1. 将页面组件持久化,以保证被更新的状态还可以被查找到; +2. 使用 [setCache](/tutorial/cache/set-and-query) 替代`updateState`,其原理是,当上一个页面的请求存在缓存时,更新它的缓存以保证再次创建页面时,所触发的请求可以命中更新后的缓存,达到同样的效果。 + +::: + +## 注意事项 + +1. 在实际使用中,不管是使用`useRequest`还是`useWatcher`发送请求时,你都可以调用`send`函数来指定不同参数重复发送请求,这些 use hook 返回的响应状态会被多个 method 实例引用,因此你可以选择任意一个 method 实例都可以匹配到同一个响应状态值; +2. 当动态查找更新响应状态时,method 匹配器找到了多个 method 实例,将会以第一个实例为准; diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/03-method-matcher.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/03-method-matcher.md index 50ceec7ee..17a530133 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/03-method-matcher.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/03-method-matcher.md @@ -1,141 +1,140 @@ ---- -title: method匹配器 -sidebar_position: 30 ---- - -method 匹配器是一个在已请求的 method 快照列表中动态查找 method 实例的方法。它一般用于,开发者不确定具体使用哪个 method 时,可以使用 method 匹配器按一定的规则查找。 - -## 匹配规则 - -当使用 method 实例请求时,它将被作为快照保存起来,method 匹配器依据 method 实例设置的`name`属性在这些 method 快照中进行查找,多个匹配器允许设置相同的`name`。 - -method 实例匹配类型如下: - -```typescript -type MethodFilter = - | string - | RegExp - | { - name: string | RegExp; - filter: (method: Method, index: number, methods: Method[]) => boolean; - - // 可选参数,如果传入alova对象则只匹配此alova所创建的Method实例,否则匹配所有alova实例的Method实例 - alova?: Alova; - }; -``` - -在以下函数中都可以使用 method 实例匹配器。 - -- [setCache](/tutorial/cache/set-and-query) -- [queryCache](/tutorial/cache/set-and-query) -- [invalidateCache](/tutorial/cache/manually-invalidate) -- [updateState](/tutorial/advanced/update-across-components) -- [useFetcher.fetch](/tutorial/advanced/use-fetcher) - -## 通过 name 属性匹配 - -通过传入完整的实例名称进行匹配,它的匹配结果是一个数组。 - -```javascript -// 每次调用getTodoList时都会生成一个新的method实例,它们的name是相同的 -const getTodoList = currentPage => - alova.Get('/todo/list', { - // highlight-start - name: 'todoList' - // highlight-end - // ... - }); - -// 以下表示让name为'todoList'的所有Method实例的缓存失效 -invalidateCache('todoList'); -``` - -## 通过正则表达式匹配 - -通过传入正则表达式进行匹配,method 实例的 name 符合正则表达式的都将匹配,它的结果也是一个数组。 - -```javascript -// 以下表示让name为以'todo'开头的所有Method实例的缓存失效 -invalidateCache(/^todo/); -``` - -## 过滤匹配结果 - -通过指定`filter`来进一步过滤不满足条件的 method 实例,filter 函数使用与 Array.prototype.filter 相同,返回 true 表示匹配成功,返回 false 表示失败,详见上面的类型声明。 - -让我们来看几个例子。 - -**让特定名称的最后一个 method 实例的缓存失效** - -```javascript -invalidateCache({ - name: 'todoList', - filter: (method, index, methods) => index === methods.length - 1 -}); -``` - -**设置由`alovaInst`创建的,特定名称的最后一个 method 实例的缓存** - -```javascript -setCache( - { - name: /^todo/, - filter: (method, index, methods) => index === methods.length - 1, - - // 如果传了alova参数,那么只匹配由此alova实例创建的Method实例,否则会在所有Method实例中匹配 - alova: alovaInst - }, - newCache -); -``` - -**重新拉取 todo 列表最后一次请求的数据** - -```javascript -const { fetch } = useFetcher(); -fetch({ - name: 'todoList', - filter: (method, index, methods) => index === methods.length - 1 -}); -``` - -> alova 参数可以进一步缩小匹配范围。 - -## 在不同函数中使用的区别 - -### invalidateCache - -应用所有匹配的 Method 实例集合,即失效所有匹配的 Method 实例对应的缓存。 - -### setCache - -应用所有匹配的 Method 实例集合,当传入静态数据时所有的 Method 实例缓存设置为相同值,传入回调函数时将循环调用此函数,并将返回值作为缓存数据。 - -### updateState - -应用第一个匹配的 Method 实例。 - -### fetch - -应用第一个匹配的 Method 实例,即只会拉取一次数据。 - -## 限制实例快照 - -`[v2.20.0+]`默认情况下,会保存 1000 个 method 实例快照,否则在频繁的请求场景下可能导致内存溢出,你也可以根据需要调整限制数量。 - -```js -import { globalConfig } from 'alova'; - -globalConfig({ - // 限制保存500个实例快照 - limitSnapshots: 500 -}); -``` - -当设置为 0 时将不再保存实例快照,此时也将无法使用 method 匹配器。 - -```js -globalConfig({ - limitSnapshots: 0 -}); -``` +--- +title: method匹配器 +--- + +method 匹配器是一个在已请求的 method 快照列表中动态查找 method 实例的方法。它一般用于,开发者不确定具体使用哪个 method 时,可以使用 method 匹配器按一定的规则查找。 + +## 匹配规则 + +当使用 method 实例请求时,它将被作为快照保存起来,method 匹配器依据 method 实例设置的`name`属性在这些 method 快照中进行查找,多个匹配器允许设置相同的`name`。 + +method 实例匹配类型如下: + +```typescript +type MethodFilter = + | string + | RegExp + | { + name: string | RegExp; + filter: (method: Method, index: number, methods: Method[]) => boolean; + + // 可选参数,如果传入alova对象则只匹配此alova所创建的Method实例,否则匹配所有alova实例的Method实例 + alova?: Alova; + }; +``` + +在以下函数中都可以使用 method 实例匹配器。 + +- [setCache](/tutorial/cache/set-and-query) +- [queryCache](/tutorial/cache/set-and-query) +- [invalidateCache](/tutorial/cache/manually-invalidate) +- [updateState](/tutorial/advanced/update-across-components) +- [useFetcher.fetch](/tutorial/advanced/use-fetcher) + +## 通过 name 属性匹配 + +通过传入完整的实例名称进行匹配,它的匹配结果是一个数组。 + +```javascript +// 每次调用getTodoList时都会生成一个新的method实例,它们的name是相同的 +const getTodoList = currentPage => + alova.Get('/todo/list', { + // highlight-start + name: 'todoList' + // highlight-end + // ... + }); + +// 以下表示让name为'todoList'的所有Method实例的缓存失效 +invalidateCache('todoList'); +``` + +## 通过正则表达式匹配 + +通过传入正则表达式进行匹配,method 实例的 name 符合正则表达式的都将匹配,它的结果也是一个数组。 + +```javascript +// 以下表示让name为以'todo'开头的所有Method实例的缓存失效 +invalidateCache(/^todo/); +``` + +## 过滤匹配结果 + +通过指定`filter`来进一步过滤不满足条件的 method 实例,filter 函数使用与 Array.prototype.filter 相同,返回 true 表示匹配成功,返回 false 表示失败,详见上面的类型声明。 + +让我们来看几个例子。 + +**让特定名称的最后一个 method 实例的缓存失效** + +```javascript +invalidateCache({ + name: 'todoList', + filter: (method, index, methods) => index === methods.length - 1 +}); +``` + +**设置由`alovaInst`创建的,特定名称的最后一个 method 实例的缓存** + +```javascript +setCache( + { + name: /^todo/, + filter: (method, index, methods) => index === methods.length - 1, + + // 如果传了alova参数,那么只匹配由此alova实例创建的Method实例,否则会在所有Method实例中匹配 + alova: alovaInst + }, + newCache +); +``` + +**重新拉取 todo 列表最后一次请求的数据** + +```javascript +const { fetch } = useFetcher(); +fetch({ + name: 'todoList', + filter: (method, index, methods) => index === methods.length - 1 +}); +``` + +> alova 参数可以进一步缩小匹配范围。 + +## 在不同函数中使用的区别 + +### invalidateCache + +应用所有匹配的 Method 实例集合,即失效所有匹配的 Method 实例对应的缓存。 + +### setCache + +应用所有匹配的 Method 实例集合,当传入静态数据时所有的 Method 实例缓存设置为相同值,传入回调函数时将循环调用此函数,并将返回值作为缓存数据。 + +### updateState + +应用第一个匹配的 Method 实例。 + +### fetch + +应用第一个匹配的 Method 实例,即只会拉取一次数据。 + +## 限制实例快照 + +`[v2.20.0+]`默认情况下,会保存 1000 个 method 实例快照,否则在频繁的请求场景下可能导致内存溢出,你也可以根据需要调整限制数量。 + +```js +import { globalConfig } from 'alova'; + +globalConfig({ + // 限制保存500个实例快照 + limitSnapshots: 500 +}); +``` + +当设置为 0 时将不再保存实例快照,此时也将无法使用 method 匹配器。 + +```js +globalConfig({ + limitSnapshots: 0 +}); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/04-middleware.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/04-middleware.md index e6870112a..e919759ea 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/04-middleware.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/04-middleware.md @@ -1,405 +1,404 @@ ---- -title: 请求中间件 -sidebar_position: 40 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -请求中间件是一个异步函数,它提供了强大的,几乎能控制一个请求的所有行为的能力。如果你只是使用 alova,那你应该很可能不需要使用请求中间件,因为它主要用于完成自定义的请求策略,无论简单还是复杂的请求策略,可能你都会用上它,接下来我们看下它到底有什么神通。 - -## 中间件函数 - -请求中间件是一个异步函数,你可以在`useRequest`、`useWatcher`、`useFetcher`中定义请求中间件。以下是一个简单的请求中间件,它在请求前和请求后分别打印了一些信息,没有改变任何请求行为。 - -```javascript -useRequest(todoList, { - async middleware(_, next) { - console.log('before request'); - await next(); - console.log('after requeste'); - } -}); -``` - -这里有几点你需要知道的,有关`next`函数调用的问题,这个函数也是一个异步函数,调用它可以继续发送请求,此时将会把 _loading_ 状态设置为 true,然后发送请求。next 的返回值是带有响应数据的 Promise 实例,你可以在中间件函数中操纵返回值。 - -## 控制响应数据 - -中间件函数的返回值将作为本次请求的响应数据参与后续的处理,如果中间件没有返回任何数据但调用了 `next`,则会将本次请求的响应数据参与后续处理。 - -```javascript -// 将会以修改后的result作为响应数据 -useRequest(todoList, { - async middleware(_, next) { - const result = await next(); - result.code = 500; - return result; - } -}); - -// 将会以本次请求的响应数据参与后续处理 -useRequest(todoList, { - async middleware(_, next) { - await next(); - } -}); - -// 将会以字符串abc作为响应数据 -useRequest(todoList, { - async middleware(_, next) { - await next(); - return 'abc'; - } -}); -``` - -这里还有一个特例,当既没有调用 `next`,又没有返回值时,将不再执行后续的处理,这表示*onSuccess*、_onError_、*onComplete*响应事件不会被触发。 - -```javascript -useRequest(todoList, { - async middleware() {} -}); -``` - -## 更改请求 - -有时候你想要更改请求,此时可以在 `next` 中指定另一个 method 实例,在发送请求时就会将这个 method 中的信息进行请求,同时你还可以通过 `next` 设置是否强制请求来穿透缓存,这也很简单。 - -```javascript -useRequest(todoList, { - async middleware(_, next) { - await next({ - // 更改请求的method实例 - method: newMethodInstance, - - // 本次是否强制请求 - force: true - }); - } -}); -``` - -## 控制错误 - -### 捕获错误 - -在中间件中,可以捕获 `next` 中产生的请求错误,捕获后,全局的`onError`钩子不再触发。 - -```javascript -useRequest(todoList, { - async middleware(_, next) { - try { - await next(); - } catch (e) { - console.error('捕获到错误', e); - } - } -}); -``` - -### 抛出错误 - -当然,也可以在中间件中抛出一个自定义错误,即使请求正常也将会进入请求错误的流程。 - -```javascript -// 未发出请求,同时还会触发全局的以及请求级的onError,如果是通过`method.send`发送的请求将返回reject的promise实例 -useRequest(todoList, { - async middleware(_, next) { - throw new Error('error on before request'); - await next(); - } -}); - -// 请求成功后,将触发全局的以及请求级的onError,如果是通过`method.send`发送的请求将返回reject的promise实例 -useRequest(todoList, { - async middleware(_, next) { - await next(); - throw new Error('error on after request'); - } -}); -``` - -## 控制响应延迟 - -在中间件中我们可以延迟响应,也可以提前响应,在提前的情况下,虽然获取不到响应数据,但可以返回一些其他的数据作为响应数据参与后续的处理。 - -```javascript -// 延迟1秒响应 -useRequest(todoList, { - async middleware(_, next) { - await new Promise(resolve => { - setTimeout(resolve, 1000); - }); - return next(); - } -}); - -// 立即响应,并使用字符串abc作为响应数据 -useRequest(todoList, { - async middleware(_, next) { - return 'abc'; - } -}); -``` - -## 不止于此 - -**至此,我们所提及的都是中间件的第二个参数 `next` 的使用,那第一个参数是做什么的呢?** - -中间件第一个参数中包含了本次请求的一些信息,以及对`loading`、`data`和`onSuccess`等 useHook 中返回的状态和事件的控制函数。我们接着往下看! - -## 包含的请求信息 - - - - -以下为 useRequest 和 useWatcher 的中间件所包含的请求信息 - -```javascript -async function alovaFrontMiddleware(context, next) { - // 本次请求的method实例 - context.method; - - // send函数发送的参数数组,默认为[] - context.sendArgs; - - // 本次请求命中的缓存数据 - context.cachedResponse; - - // useHook的配置集合 - context.config; - - // useHook返回的各项状态,包含以下属性 - // loading、data、error、downloading、uploading,以及通过managedStates管理的额外状态 - context.frontStates; - // ... -} -``` - - - - -以下为 useFetcher 的中间件所包含的请求信息 - -```javascript -async function alovaFetcherMiddleware(context, next) { - // 本次请求的method实例 - context.method; - - // 由useFetcher的fetch传入的参数组,默认为[] - context.fetchArgs; - - // 本次请求命中的缓存数据 - context.cachedResponse; - - // useHook的配置集合 - context.config; - - // useHook返回的各项状态,包含以下属性 - // fetching、error、downloading、uploading - context.fetchStates; - // ... -} -``` - - - - -接下来,我们再来看看有哪些控制能力。 - -## 修改响应式数据 - -使用`context.update`修改响应式数据。 - - - - -```javascript -async function alovaFrontMiddleware(context, next) { - context.update({ - // 提前修改loading状态为true - loading: true, - - // 修改data值,如设置自定义的初始化数据 - data: { - /* ... */ - } - }); - // ... -} -``` - - - - -```javascript -async function alovaFetcherMiddleware(context, next) { - context.update({ - // 提前修改fetching状态为true - fetching: true, - - // 修改error的值 - error: new Error('custom midleware error') - }); - // ... -} -``` - - - - -## 装饰事件 - -你还可以在中间件中装饰*onSuccess*、_onError_、*onComplete*回调函数,让它们变得更丰富,例如改变回调函数的参数,又或者接收回调函数的返回值,实现更多的功能。 - -你可以使用`decorateSuccess`、`decorateError`、`decorateComplete`函数来装饰回调函数。下面将成功回调作为示例,它装饰了 3 处地方: - -1. 为 event 对象新增了`custom`属性; -2. 为成功回调函数新增了第二个参数,值为`extra data`; -3. 接收第二个成功回调函数的值,并打印它; - -```javascript -const { onSuccess } = useRequest(todoList, { - // ... - async middleware(context, next) { - // 装饰成功回调函数,以下函数参数解释: - // handler: 绑定的回调函数 - // event: 回调函数对应的事件对象 - // index: 回调函数下标,表示当前正在执行第几个回调函数 - // length: 回调函数绑定个数 - context.decorateSuccess((handler, event, index, length) => { - event.custom = 1; - const received = handler(event, 'extra data'); - if (index === 1) { - console.log(`接收到第${index + 1}个回调函数的返回值:`, received); - // [打印信息] 接收到第2个回调函数的返回值:I'm second handler - } - }); - // ... - } -}); -onSuccess((event, extra) => { - console.log(event.custom); // 1 - console.log(extra); // extra data -}); -onSuccess((event, extra) => { - return "I'm second handler"; -}); -``` - -`decorateError`、`decorateComplete`的用法与`decorateSuccess`相同。 - -## 中断或重复发送请求 - -在中间件中还可以接收到 use hooks 返回的`abort`和`send`函数(useFetcher 中为`fetch`),你还可以在触发一次请求意图时,发送多个请求。 - -典型的使用例子是请求重试,发送一次请求后如果请求失败将自动按一定策略再次请求,重试成功后再触发`onSuccess`。以下为简单的请求重试示例代码。 - - - - -```javascript -async function alovaFrontMiddleware(context, next) { - return next().catch(error => { - if (needRetry) { - setTimeout(() => { - context.send(...context.sendArgs); - }, retryDelay); - } - return Promise.reject(error); - }); -} -``` - - - - -```javascript -async function alovaFetcherMiddleware(context, next) { - return next().catch(error => { - if (needRetry) { - setTimeout(() => { - context.fetch(context.method, ...context.fetchArgs); - }, retryDelay); - } - return Promise.reject(error); - }); -} -``` - - - - -如果需要在中间件内中断请求,可以调用`context.abort()`。 - -## 受控的加载状态 - -在上面内容中,我们知道了可以通过`context.update`自定义修改响应式数据,不过当你在修改加载状态值(`loading`或`fetching`)时将会有所阻碍,因为在正常情况下,加载状态值会在调用`next`时自动设置为 true,在响应流程中自动设置 false,这将覆盖通过`context.update`修改的加载状态值,此时我们可以开启受控的加载状态,开启后,在`next`函数和响应流程将不再修改加载状态值,而由我们完全控制。 - -我们还是以请求重试为例,我们希望在触发一次请求意图开始,经过请求重试直到请求结束为止,加载状态一直保持为 true。 - - - - -在 useRequest 和 useWatcher 的中间件中,使用`context.controlLoading`开启自定义控制加载状态。 - -```javascript -async function alovaFrontMiddleware(context, next) { - context.controlLoading(); - - // 请求开始时设置为true - context.update({ loading: true }); - return next() - .then(value => { - // 请求成功后设置为false - context.update({ loading: false }); - return value; - }) - .catch(error => { - if (needRetry) { - setTimeout(() => { - context.send(...context.sendArgs); - }, retryDelay); - } else { - // 不再重试时也设置为false - context.update({ loading: false }); - } - return Promise.reject(error); - }); -} -``` - - - - -在 useFetching 的中间件中,使用`context.controlFetching`开启自定义控制加载状态。 - -```javascript -async function alovaFetcherMiddleware(context, next) { - context.controlFetching(); - - // 请求开始时设置为true - context.update({ fetching: true }); - return next() - .then(value => { - // 请求成功后设置为false - context.update({ fetching: false }); - return value; - }) - .catch(error => { - if (needRetry) { - setTimeout(() => { - context.fetch(context.method, ...context.fetchArgs); - }, retryDelay); - } else { - // 不再重试时也设置为false - context.update({ fetching: false }); - } - return Promise.reject(error); - }); -} -``` - - - +--- +title: 请求中间件 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +请求中间件是一个异步函数,它提供了强大的,几乎能控制一个请求的所有行为的能力。如果你只是使用 alova,那你应该很可能不需要使用请求中间件,因为它主要用于完成自定义的请求策略,无论简单还是复杂的请求策略,可能你都会用上它,接下来我们看下它到底有什么神通。 + +## 中间件函数 + +请求中间件是一个异步函数,你可以在`useRequest`、`useWatcher`、`useFetcher`中定义请求中间件。以下是一个简单的请求中间件,它在请求前和请求后分别打印了一些信息,没有改变任何请求行为。 + +```javascript +useRequest(todoList, { + async middleware(_, next) { + console.log('before request'); + await next(); + console.log('after requeste'); + } +}); +``` + +这里有几点你需要知道的,有关`next`函数调用的问题,这个函数也是一个异步函数,调用它可以继续发送请求,此时将会把 _loading_ 状态设置为 true,然后发送请求。next 的返回值是带有响应数据的 Promise 实例,你可以在中间件函数中操纵返回值。 + +## 控制响应数据 + +中间件函数的返回值将作为本次请求的响应数据参与后续的处理,如果中间件没有返回任何数据但调用了 `next`,则会将本次请求的响应数据参与后续处理。 + +```javascript +// 将会以修改后的result作为响应数据 +useRequest(todoList, { + async middleware(_, next) { + const result = await next(); + result.code = 500; + return result; + } +}); + +// 将会以本次请求的响应数据参与后续处理 +useRequest(todoList, { + async middleware(_, next) { + await next(); + } +}); + +// 将会以字符串abc作为响应数据 +useRequest(todoList, { + async middleware(_, next) { + await next(); + return 'abc'; + } +}); +``` + +这里还有一个特例,当既没有调用 `next`,又没有返回值时,将不再执行后续的处理,这表示*onSuccess*、_onError_、*onComplete*响应事件不会被触发。 + +```javascript +useRequest(todoList, { + async middleware() {} +}); +``` + +## 更改请求 + +有时候你想要更改请求,此时可以在 `next` 中指定另一个 method 实例,在发送请求时就会将这个 method 中的信息进行请求,同时你还可以通过 `next` 设置是否强制请求来穿透缓存,这也很简单。 + +```javascript +useRequest(todoList, { + async middleware(_, next) { + await next({ + // 更改请求的method实例 + method: newMethodInstance, + + // 本次是否强制请求 + force: true + }); + } +}); +``` + +## 控制错误 + +### 捕获错误 + +在中间件中,可以捕获 `next` 中产生的请求错误,捕获后,全局的`onError`钩子不再触发。 + +```javascript +useRequest(todoList, { + async middleware(_, next) { + try { + await next(); + } catch (e) { + console.error('捕获到错误', e); + } + } +}); +``` + +### 抛出错误 + +当然,也可以在中间件中抛出一个自定义错误,即使请求正常也将会进入请求错误的流程。 + +```javascript +// 未发出请求,同时还会触发全局的以及请求级的onError,如果是通过`method.send`发送的请求将返回reject的promise实例 +useRequest(todoList, { + async middleware(_, next) { + throw new Error('error on before request'); + await next(); + } +}); + +// 请求成功后,将触发全局的以及请求级的onError,如果是通过`method.send`发送的请求将返回reject的promise实例 +useRequest(todoList, { + async middleware(_, next) { + await next(); + throw new Error('error on after request'); + } +}); +``` + +## 控制响应延迟 + +在中间件中我们可以延迟响应,也可以提前响应,在提前的情况下,虽然获取不到响应数据,但可以返回一些其他的数据作为响应数据参与后续的处理。 + +```javascript +// 延迟1秒响应 +useRequest(todoList, { + async middleware(_, next) { + await new Promise(resolve => { + setTimeout(resolve, 1000); + }); + return next(); + } +}); + +// 立即响应,并使用字符串abc作为响应数据 +useRequest(todoList, { + async middleware(_, next) { + return 'abc'; + } +}); +``` + +## 不止于此 + +**至此,我们所提及的都是中间件的第二个参数 `next` 的使用,那第一个参数是做什么的呢?** + +中间件第一个参数中包含了本次请求的一些信息,以及对`loading`、`data`和`onSuccess`等 useHook 中返回的状态和事件的控制函数。我们接着往下看! + +## 包含的请求信息 + + + + +以下为 useRequest 和 useWatcher 的中间件所包含的请求信息 + +```javascript +async function alovaFrontMiddleware(context, next) { + // 本次请求的method实例 + context.method; + + // send函数发送的参数数组,默认为[] + context.sendArgs; + + // 本次请求命中的缓存数据 + context.cachedResponse; + + // useHook的配置集合 + context.config; + + // useHook返回的各项状态,包含以下属性 + // loading、data、error、downloading、uploading,以及通过managedStates管理的额外状态 + context.frontStates; + // ... +} +``` + + + + +以下为 useFetcher 的中间件所包含的请求信息 + +```javascript +async function alovaFetcherMiddleware(context, next) { + // 本次请求的method实例 + context.method; + + // 由useFetcher的fetch传入的参数组,默认为[] + context.fetchArgs; + + // 本次请求命中的缓存数据 + context.cachedResponse; + + // useHook的配置集合 + context.config; + + // useHook返回的各项状态,包含以下属性 + // fetching、error、downloading、uploading + context.fetchStates; + // ... +} +``` + + + + +接下来,我们再来看看有哪些控制能力。 + +## 修改响应式数据 + +使用`context.update`修改响应式数据。 + + + + +```javascript +async function alovaFrontMiddleware(context, next) { + context.update({ + // 提前修改loading状态为true + loading: true, + + // 修改data值,如设置自定义的初始化数据 + data: { + /* ... */ + } + }); + // ... +} +``` + + + + +```javascript +async function alovaFetcherMiddleware(context, next) { + context.update({ + // 提前修改fetching状态为true + fetching: true, + + // 修改error的值 + error: new Error('custom midleware error') + }); + // ... +} +``` + + + + +## 装饰事件 + +你还可以在中间件中装饰*onSuccess*、_onError_、*onComplete*回调函数,让它们变得更丰富,例如改变回调函数的参数,又或者接收回调函数的返回值,实现更多的功能。 + +你可以使用`decorateSuccess`、`decorateError`、`decorateComplete`函数来装饰回调函数。下面将成功回调作为示例,它装饰了 3 处地方: + +1. 为 event 对象新增了`custom`属性; +2. 为成功回调函数新增了第二个参数,值为`extra data`; +3. 接收第二个成功回调函数的值,并打印它; + +```javascript +const { onSuccess } = useRequest(todoList, { + // ... + async middleware(context, next) { + // 装饰成功回调函数,以下函数参数解释: + // handler: 绑定的回调函数 + // event: 回调函数对应的事件对象 + // index: 回调函数下标,表示当前正在执行第几个回调函数 + // length: 回调函数绑定个数 + context.decorateSuccess((handler, event, index, length) => { + event.custom = 1; + const received = handler(event, 'extra data'); + if (index === 1) { + console.log(`接收到第${index + 1}个回调函数的返回值:`, received); + // [打印信息] 接收到第2个回调函数的返回值:I'm second handler + } + }); + // ... + } +}); +onSuccess((event, extra) => { + console.log(event.custom); // 1 + console.log(extra); // extra data +}); +onSuccess((event, extra) => { + return "I'm second handler"; +}); +``` + +`decorateError`、`decorateComplete`的用法与`decorateSuccess`相同。 + +## 中断或重复发送请求 + +在中间件中还可以接收到 use hooks 返回的`abort`和`send`函数(useFetcher 中为`fetch`),你还可以在触发一次请求意图时,发送多个请求。 + +典型的使用例子是请求重试,发送一次请求后如果请求失败将自动按一定策略再次请求,重试成功后再触发`onSuccess`。以下为简单的请求重试示例代码。 + + + + +```javascript +async function alovaFrontMiddleware(context, next) { + return next().catch(error => { + if (needRetry) { + setTimeout(() => { + context.send(...context.sendArgs); + }, retryDelay); + } + return Promise.reject(error); + }); +} +``` + + + + +```javascript +async function alovaFetcherMiddleware(context, next) { + return next().catch(error => { + if (needRetry) { + setTimeout(() => { + context.fetch(context.method, ...context.fetchArgs); + }, retryDelay); + } + return Promise.reject(error); + }); +} +``` + + + + +如果需要在中间件内中断请求,可以调用`context.abort()`。 + +## 受控的加载状态 + +在上面内容中,我们知道了可以通过`context.update`自定义修改响应式数据,不过当你在修改加载状态值(`loading`或`fetching`)时将会有所阻碍,因为在正常情况下,加载状态值会在调用`next`时自动设置为 true,在响应流程中自动设置 false,这将覆盖通过`context.update`修改的加载状态值,此时我们可以开启受控的加载状态,开启后,在`next`函数和响应流程将不再修改加载状态值,而由我们完全控制。 + +我们还是以请求重试为例,我们希望在触发一次请求意图开始,经过请求重试直到请求结束为止,加载状态一直保持为 true。 + + + + +在 useRequest 和 useWatcher 的中间件中,使用`context.controlLoading`开启自定义控制加载状态。 + +```javascript +async function alovaFrontMiddleware(context, next) { + context.controlLoading(); + + // 请求开始时设置为true + context.update({ loading: true }); + return next() + .then(value => { + // 请求成功后设置为false + context.update({ loading: false }); + return value; + }) + .catch(error => { + if (needRetry) { + setTimeout(() => { + context.send(...context.sendArgs); + }, retryDelay); + } else { + // 不再重试时也设置为false + context.update({ loading: false }); + } + return Promise.reject(error); + }); +} +``` + + + + +在 useFetching 的中间件中,使用`context.controlFetching`开启自定义控制加载状态。 + +```javascript +async function alovaFetcherMiddleware(context, next) { + context.controlFetching(); + + // 请求开始时设置为true + context.update({ fetching: true }); + return next() + .then(value => { + // 请求成功后设置为false + context.update({ fetching: false }); + return value; + }) + .catch(error => { + if (needRetry) { + setTimeout(() => { + context.fetch(context.method, ...context.fetchArgs); + }, retryDelay); + } else { + // 不再重试时也设置为false + context.update({ fetching: false }); + } + return Promise.reject(error); + }); +} +``` + + + diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/05-custom-method-key.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/05-custom-method-key.md index 4457c40a1..69832c126 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/05-custom-method-key.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/05-custom-method-key.md @@ -1,26 +1,25 @@ ---- -title: 自定义method key -sidebar_position: 50 ---- - -:::info version required - -v2.20.0+ - -::: - -method key 用来标识一切与 method 实例关联的数据,有很大的作用,例如: - -- 关联响应数据的缓存 -- 标识共享请求 -- 关联 useRequest 等 useHook 返回的状态值 - -在默认情况下,method key 由 method 实例的相关请求参数生成,它可以准确标识一个请求。 - -但有时候你希望改变它,让以上三个情况在不同的请求中也可以被识别为同一个 method。 - -```javascript -// method key在创建时生成,可以通过__key__自定义它 -const methodInst = alovaInstance.Get('/api/user', {}); -methodInst.__key__ = 'my-custom-method-key'; -``` +--- +title: 自定义method key +--- + +:::info version required + +v2.20.0+ + +::: + +method key 用来标识一切与 method 实例关联的数据,有很大的作用,例如: + +- 关联响应数据的缓存 +- 标识共享请求 +- 关联 useRequest 等 useHook 返回的状态值 + +在默认情况下,method key 由 method 实例的相关请求参数生成,它可以准确标识一个请求。 + +但有时候你希望改变它,让以上三个情况在不同的请求中也可以被识别为同一个 method。 + +```javascript +// method key在创建时生成,可以通过__key__自定义它 +const methodInst = alovaInstance.Get('/api/user', {}); +methodInst.__key__ = 'my-custom-method-key'; +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/06-error-logger.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/06-error-logger.md index 98dd64467..543c2b093 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/06-error-logger.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/06-error-logger.md @@ -1,51 +1,50 @@ ---- -title: 错误日志 -sidebar_position: 60 ---- - -:::info 版本要求 - -v2.6.0+ - -::: - -为了方便调试,当在使用 use hooks 请求或响应处理错误时会默认在控制台打印错误日志,如果你在一些情况下(例如生产环境)不希望打印错误信息或自定义控制打印错误信息,alova 也提供了对它们的支持。 - -## 关闭打印错误日志 - -可在创建 alova 实例时将`errorLogger`设置为`false或null`关闭日志打印。 - -```javascript -const alovaInstance = createAlova({ - // ... - errorLogger: false -}); -``` - -你也可以根据不同的环境动态开启与关闭。 - -```javascript -const alovaInstance = createAlova({ - // ... - // 在开发环境开启错误日志 - errorLogger: process.env.NODE_ENV === 'development' -}); -``` - -## 自定义打印错误日志 - -错误日志默认通过`console.error`进行打印,如果你的项目环境中不支持`console.error`,或者希望收集错误信息,可以将`errorLogger`指定为一个函数自定义处理错误日志。 - -```javascript -const alovaInstance = createAlova({ - // ... - /** - * 自定义的错误日志函数 - * @param error 错误对象 - * @param method 当前的method实例 - */ - errorLogger(error, method) { - reportError(`${method.url}: ${error.message}`); - } -}); -``` +--- +title: 错误日志 +--- + +:::info 版本要求 + +v2.6.0+ + +::: + +为了方便调试,当在使用 use hooks 请求或响应处理错误时会默认在控制台打印错误日志,如果你在一些情况下(例如生产环境)不希望打印错误信息或自定义控制打印错误信息,alova 也提供了对它们的支持。 + +## 关闭打印错误日志 + +可在创建 alova 实例时将`errorLogger`设置为`false或null`关闭日志打印。 + +```javascript +const alovaInstance = createAlova({ + // ... + errorLogger: false +}); +``` + +你也可以根据不同的环境动态开启与关闭。 + +```javascript +const alovaInstance = createAlova({ + // ... + // 在开发环境开启错误日志 + errorLogger: process.env.NODE_ENV === 'development' +}); +``` + +## 自定义打印错误日志 + +错误日志默认通过`console.error`进行打印,如果你的项目环境中不支持`console.error`,或者希望收集错误信息,可以将`errorLogger`指定为一个函数自定义处理错误日志。 + +```javascript +const alovaInstance = createAlova({ + // ... + /** + * 自定义的错误日志函数 + * @param error 错误对象 + * @param method 当前的method实例 + */ + errorLogger(error, method) { + reportError(`${method.url}: ${error.message}`); + } +}); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/07-cache-logger.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/07-cache-logger.md index c871a76df..de7516b17 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/07-cache-logger.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/07-cache-logger.md @@ -1,60 +1,59 @@ ---- -title: 缓存命中日志 -sidebar_position: 70 ---- - -:::info 版本要求 - -v2.8.0+ - -::: - -在使用接口缓存时为了便于调试,当请求命中缓存而未发出网络请求时,将默认在控制台打印出命中的缓存信息,这可以解决在使用缓存时的一些困惑。 - -如果你在一些情况下(例如生产环境)不希望打印缓存信息或自定义控制打印缓存信息,alova 也提供了对它们的支持。 - -## 关闭打印缓存命中日志 - -可在创建 alova 实例时将`cacheLogger`设置为`false或null`关闭控制台打印。 - -```javascript -const alovaInstance = createAlova({ - // ... - cacheLogger: false -}); -``` - -你也可以根据不同的环境动态开启与关闭。 - -```javascript -const alovaInstance = createAlova({ - // ... - // 在开发环境开启缓存命中日志 - cacheLogger: process.env.NODE_ENV === 'development' -}); -``` - -## 自定义打印缓存命中日志 - -缓存日志默认通过`console.log`进行打印,如果你的项目环境中不支持`console.log`或其他目的,可以将`cacheLogger`指定为一个函数自定义处理缓存命中的日志。 - -```javascript -const alovaInstance = createAlova({ - // ... - /** - * 自定义的缓存命中日志函数 - * @param response 命中的缓存数据 - * @param method 当前的method实例 - * @param cacheMode 缓存模式 memory或restore - * @param tag restore模式下的tag,只有在对应的缓存设置了tag时有值 - */ - cacheLogger(response, method, cacheMode, tag) { - saveHitCache({ - response, - method, - cacheMode, - tag - }); - } -}); -``` +--- +title: 缓存命中日志 +--- + +:::info 版本要求 + +v2.8.0+ + +::: + +在使用接口缓存时为了便于调试,当请求命中缓存而未发出网络请求时,将默认在控制台打印出命中的缓存信息,这可以解决在使用缓存时的一些困惑。 + +如果你在一些情况下(例如生产环境)不希望打印缓存信息或自定义控制打印缓存信息,alova 也提供了对它们的支持。 + +## 关闭打印缓存命中日志 + +可在创建 alova 实例时将`cacheLogger`设置为`false或null`关闭控制台打印。 + +```javascript +const alovaInstance = createAlova({ + // ... + cacheLogger: false +}); +``` + +你也可以根据不同的环境动态开启与关闭。 + +```javascript +const alovaInstance = createAlova({ + // ... + // 在开发环境开启缓存命中日志 + cacheLogger: process.env.NODE_ENV === 'development' +}); +``` + +## 自定义打印缓存命中日志 + +缓存日志默认通过`console.log`进行打印,如果你的项目环境中不支持`console.log`或其他目的,可以将`cacheLogger`指定为一个函数自定义处理缓存命中的日志。 + +```javascript +const alovaInstance = createAlova({ + // ... + /** + * 自定义的缓存命中日志函数 + * @param response 命中的缓存数据 + * @param method 当前的method实例 + * @param cacheMode 缓存模式 memory或restore + * @param tag restore模式下的tag,只有在对应的缓存设置了tag时有值 + */ + cacheLogger(response, method, cacheMode, tag) { + saveHitCache({ + response, + method, + cacheMode, + tag + }); + } +}); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/08-manage-extra-states.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/08-manage-extra-states.md index 86d4741a8..4e79e54fb 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/08-manage-extra-states.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/08-manage-extra-states.md @@ -1,199 +1,198 @@ ---- -title: 管理额外的状态 -sidebar_position: 80 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -在之前的[跨页面/模块更新响应状态](/tutorial/advanced/update-across-components)章节中,介绍了如何跨页面或模块更新响应状态,但在此章节中我们只介绍了通过`updateState`更新在`useRequest`和`useWatcher`中返回的`data`状态,data 的值总是和响应数据一致,但在很多情况下我们会使用额外的状态来展示(如状态 A)数据,并在请求成功后将 data 数据附加到额外的状态 A 中,如下拉加载的分页方案。在这种情况下,我们就需要将额外的状态 A 进行管理,便于实现跨页面/模块更新它。 - -## 更新单个状态 - -可以在 use hook 调用时通过`managedStates`管理额外的状态,并在其他模块/页面中调用`updateState`时,自动指定状态名称来更新它。 - - - - -```javascript title="A.vue" -const todoList = page => - alova.Get('/todo', { - name: 'todoList' - }); - -const allTodo = ref([]); -useRequest(todoList, { - // ... - - // highlight-start - // 将allTodo作为额外的状态进行管理 - managedStates: { - allTodo - } - // highlight-end -}); -``` - -```javascript title="B.vue" -const handleSuccess = () => { - // highlight-start - // 传入一个对象并指定状态名来查找 - updateState('todoList', { - allTodo: allTodoData => { - // 新增一条todo项 - allTodoData.push({ - title: 'new todo', - time: '10:00' - }); - return allTodoData; - } - }); - // highlight-end -}; -``` - - - - - -```javascript title="A.jsx" -const PageA = () => { - const todoList = page => - alova.Get('/todo', { - name: 'todoList' - }); - - const [allTodo, setAllTodo] = allTodoState = useState([]); - useRequest(todoList, { - // ... - - // highlight-start - // 将allTodo作为额外的状态进行管理 - managedStates: { - allTodo: allTodoState - } - // highlight-end - }); - - return ( - // ... - ); -} -``` - -```javascript title="B.jsx" -const PageB = () => { - // ... - const handleSuccess = () => { - // highlight-start - // 传入一个对象并指定状态名来查找 - updateState('todoList', { - allTodo: allTodoData => { - // 新增一条todo项 - allTodoData.push({ - title: 'new todo', - time: '10:00' - }); - return allTodoData; - } - }); - // highlight-end - }; - - return ( - // ... - ); -} -``` - - - - - -```javascript title="A.svelte" -// a.svelte -const todoList = page => - alova.Get('/todo', { - name: 'todoList' - }); - -const allTodo = ref([]); -useRequest(todoList, { - // ... - - // highlight-start - // 将allTodo作为额外的状态进行管理 - managedStates: { - allTodo - } - // highlight-end -}); -``` - -```javascript title="B.svelte" -const handleSuccess = () => { - // highlight-start - // 传入一个对象并指定状态名来查找 - updateState('todoList', { - allTodo: allTodoData => { - // 新增一条todo项 - allTodoData.push({ - title: 'new todo', - time: '10:00' - }); - return allTodoData; - } - }); - // highlight-end -}; -``` - - - - -:::info 说明 - -不支持管理额外状态。 - -::: - - - - -## 更新多个状态 - -在上面的例子中我们实现了跨页面对单个`allTodo`状态进行更新,实际上,通过`updateState`的对象描述方式可以同时更新任意多个状态。 - -```javascript -updateState('todoList', { - state1: state1Data => { - // ... - }, - state2: state2Data => { - // ... - }, - state3: state3Data => { - // ... - } - // ... -}); -``` - -需要注意的是,以上 3 个额外的状态在更新前,需要通过`managedStates`属性来管理起来。 - -## data 状态更新的简写 - -当只更新 data 状态时,可以直接传入回调函数即可,而不需要指定为对象。 - -```javascript -updateState('todoList', { - data: dataRaw => { - // ... - } -}); - -// 以下为简写 -updateState('todoList', dataRaw => { - // ... -}); -``` +--- +title: 管理额外的状态 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +在之前的[跨页面/模块更新响应状态](/tutorial/advanced/update-across-components)章节中,介绍了如何跨页面或模块更新响应状态,但在此章节中我们只介绍了通过`updateState`更新在`useRequest`和`useWatcher`中返回的`data`状态,data 的值总是和响应数据一致,但在很多情况下我们会使用额外的状态来展示(如状态 A)数据,并在请求成功后将 data 数据附加到额外的状态 A 中,如下拉加载的分页方案。在这种情况下,我们就需要将额外的状态 A 进行管理,便于实现跨页面/模块更新它。 + +## 更新单个状态 + +可以在 use hook 调用时通过`managedStates`管理额外的状态,并在其他模块/页面中调用`updateState`时,自动指定状态名称来更新它。 + + + + +```javascript title="A.vue" +const todoList = page => + alova.Get('/todo', { + name: 'todoList' + }); + +const allTodo = ref([]); +useRequest(todoList, { + // ... + + // highlight-start + // 将allTodo作为额外的状态进行管理 + managedStates: { + allTodo + } + // highlight-end +}); +``` + +```javascript title="B.vue" +const handleSuccess = () => { + // highlight-start + // 传入一个对象并指定状态名来查找 + updateState('todoList', { + allTodo: allTodoData => { + // 新增一条todo项 + allTodoData.push({ + title: 'new todo', + time: '10:00' + }); + return allTodoData; + } + }); + // highlight-end +}; +``` + + + + + +```javascript title="A.jsx" +const PageA = () => { + const todoList = page => + alova.Get('/todo', { + name: 'todoList' + }); + + const [allTodo, setAllTodo] = allTodoState = useState([]); + useRequest(todoList, { + // ... + + // highlight-start + // 将allTodo作为额外的状态进行管理 + managedStates: { + allTodo: allTodoState + } + // highlight-end + }); + + return ( + // ... + ); +} +``` + +```javascript title="B.jsx" +const PageB = () => { + // ... + const handleSuccess = () => { + // highlight-start + // 传入一个对象并指定状态名来查找 + updateState('todoList', { + allTodo: allTodoData => { + // 新增一条todo项 + allTodoData.push({ + title: 'new todo', + time: '10:00' + }); + return allTodoData; + } + }); + // highlight-end + }; + + return ( + // ... + ); +} +``` + + + + + +```javascript title="A.svelte" +// a.svelte +const todoList = page => + alova.Get('/todo', { + name: 'todoList' + }); + +const allTodo = ref([]); +useRequest(todoList, { + // ... + + // highlight-start + // 将allTodo作为额外的状态进行管理 + managedStates: { + allTodo + } + // highlight-end +}); +``` + +```javascript title="B.svelte" +const handleSuccess = () => { + // highlight-start + // 传入一个对象并指定状态名来查找 + updateState('todoList', { + allTodo: allTodoData => { + // 新增一条todo项 + allTodoData.push({ + title: 'new todo', + time: '10:00' + }); + return allTodoData; + } + }); + // highlight-end +}; +``` + + + + +:::info 说明 + +不支持管理额外状态。 + +::: + + + + +## 更新多个状态 + +在上面的例子中我们实现了跨页面对单个`allTodo`状态进行更新,实际上,通过`updateState`的对象描述方式可以同时更新任意多个状态。 + +```javascript +updateState('todoList', { + state1: state1Data => { + // ... + }, + state2: state2Data => { + // ... + }, + state3: state3Data => { + // ... + } + // ... +}); +``` + +需要注意的是,以上 3 个额外的状态在更新前,需要通过`managedStates`属性来管理起来。 + +## data 状态更新的简写 + +当只更新 data 状态时,可以直接传入回调函数即可,而不需要指定为对象。 + +```javascript +updateState('todoList', { + data: dataRaw => { + // ... + } +}); + +// 以下为简写 +updateState('todoList', dataRaw => { + // ... +}); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/09-ssr.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/09-ssr.md index 529595d09..f4788a7b6 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/09-ssr.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/09-ssr.md @@ -1,228 +1,227 @@ ---- -title: 服务端渲染(SSR) -sidebar_position: 90 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -:::info 版本要求 - -2.8.0+ - -::: - -## 概述 - -尽管 alova 的定位并不是在 nodejs 中进行请求,但为了可以结合 UI 框架的服务端渲染([Nuxt3.x](https://nuxt.com/) / [Nextjs](https://nextjs.org/) / [sveltekit](https://kit.svelte.dev/)),我们也对它做了适配。尽管例如`Nuxt3.x`、`Sveltekit`中提供了内置的请求功能,但如果你选择使用 alova 的话,你可以同时在服务端和客户端中使用 alova 管理请求,而不是服务端和客户端分别使用不同的请求方案来管理它们。 - -这里有一些在 SSR 中使用 alova 需要注意的地方,以及不同 UI 框架的 SSR 中的使用示例。 - -## 在服务端调用接口 - -SSR 中经常需要在服务端获取数据并渲染成 HTML,这种情况下我们不能使用 alova 的 use hooks(也无需使用)来获取数据,以下我们将分别对支持的 SSR 框架进行展示。 - -### Nuxt3.x - -在 Nuxt3.x 中提供了`useAsyncData`在服务端初始化页面数据,同时还提供了`useFetch`和`$fetch`请求函数,这些可以同时在服务端和客户端使用的请求函数真的很方便。尽管如此,如果你希望在 nuxt 中使用 alova 的话,你可以使用 **useAsyncData + alova.Method** 组合的方式完成服务端数据获取,这与你平时使用`useAsyncData`没什么区别。 - -```html - -``` - -### Nextjs - -Nextjs 提供了固定的服务端初始化页面数据的函数,如`getStaticProps`、`getServerSideProps`等,可以在函数中[直接使用 method 实例](/tutorial/getting-started/quick-start)调用接口。 - -```jsx -const todoListGetter = alovaInstance.Get('/todo/list', { - headers: { - 'Content-Type': 'application/json;charset=UTF-8' - } -}); - -export const getServerSideProps = async ctx => { - const list = await todoListGetter.send(); - return { - props: { - list - } - }; -}; -export default function App(props) { - return props.list.map(item => ( -
- {item.title} - {item.time} -
- )); -} -``` - -### Sveltekit - -Sveltekit 中也提供了`load`函数进行服务端的页面数据初始化,你同样可以在函数中[直接使用 method 实例](/tutorial/getting-started/quick-start)调用接口。例如在`+page.server.js`中调用接口。 - -```javascript title=+page.server.js -const todoListGetter = alovaInstance.Get('/todo/list', { - headers: { - 'Content-Type': 'application/json;charset=UTF-8' - } -}); - -/** @type {import('./$types').PageServerLoad} */ -export async function load({ params }) { - return { - list: todoListGetter.send() - }; -} -``` - -## 在 SSR 中使用 usehooks - -由于每个 SSR 框架都有各自的在服务端中初始化数据的方式,因此在 SSR 中生成 html 时,组件中的`useRequest`和`useWatcher`即使将`immediate`设置为`true`也不会发起请求,因为这更像是客户端初始化数据。 - -不过,如果你需要像客户端中一样初始化页面的数据,也可以设置`immediate`为`true`,当页面在浏览器中运行时,你可以和往常一样使用 alova 的所有功能。 - -## 注意事项 - -### 客户端和服务端的缓存可能不一致 - -如果你使用了 alova 的缓存功能,这里可能需要注意的是,客户端和服务端的缓存并不是共享的,这意味着如果你在初始化页面时直接使用了**usehooks**获取数据,你可能会遇到客户端和服务端渲染不一致的问题,尽管很少人这样做。 - -请看以下代码片段。 - - - - -```html - - - -``` - - - - -```jsx -function App(props) { - const { loading, data } = useRequest(alovaGetter); - return ( - <> - {loading ?
loading
: null} -
{data}
- - ); -} -``` - -
- - -```html - - -{#if $loading} -
loading
-{/if} -
{{ data }}
-``` - -
-
- -以下代码假设`alovaGetter`请求在服务端存在缓存,但在客户端不存在。 - -此时在服务端生成时 html 时,由于命中缓存,`loading`为`false`而不显示`
loading
`,但在客户端初始化时由于未命中缓存,`loading`为`true`而导致显示`
loading
`,此时 SSR 框架将会提示两个端渲染不一致。 - -**解决方法** - -1. 尽量将页面数据初始化的工作放在获取函数中,而不是组件中; -2. 如果必须这样做,则可以避免在客户端和服务端使用相同的接口,或者关闭出现问题的接口缓存; -3. 如果也需要缓存,你可以在服务端数据初始化函数中清除服务端的缓存,示例代码如下: - - - - -```html - - - -``` - - - - -```jsx -import { invalidateCache } from 'alova'; - -function App(props) { - const { loading, data } = useRequest(alovaGetter); - return ( - <> - {loading ?
loading
: null} -
{data}
- - ); -} - -export const getServerSideProps = async () => { - // 在服务端中清除缓存 - invalidateCache(alovaGetter); - return { - props: {} - }; -}; -``` - -
- - -```javascript title=+page.server.js -import { invalidateCache } from 'alova'; - -/** @type {import('./$types').PageServerLoad} */ -export async function load({ params }) { - // 在服务端中清除缓存 - invalidateCache(alovaGetter); - return {}; -} -``` - - -
+--- +title: 服务端渲染(SSR) +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info 版本要求 + +2.8.0+ + +::: + +## 概述 + +尽管 alova 的定位并不是在 nodejs 中进行请求,但为了可以结合 UI 框架的服务端渲染([Nuxt3.x](https://nuxt.com/) / [Nextjs](https://nextjs.org/) / [sveltekit](https://kit.svelte.dev/)),我们也对它做了适配。尽管例如`Nuxt3.x`、`Sveltekit`中提供了内置的请求功能,但如果你选择使用 alova 的话,你可以同时在服务端和客户端中使用 alova 管理请求,而不是服务端和客户端分别使用不同的请求方案来管理它们。 + +这里有一些在 SSR 中使用 alova 需要注意的地方,以及不同 UI 框架的 SSR 中的使用示例。 + +## 在服务端调用接口 + +SSR 中经常需要在服务端获取数据并渲染成 HTML,这种情况下我们不能使用 alova 的 use hooks(也无需使用)来获取数据,以下我们将分别对支持的 SSR 框架进行展示。 + +### Nuxt3.x + +在 Nuxt3.x 中提供了`useAsyncData`在服务端初始化页面数据,同时还提供了`useFetch`和`$fetch`请求函数,这些可以同时在服务端和客户端使用的请求函数真的很方便。尽管如此,如果你希望在 nuxt 中使用 alova 的话,你可以使用 **useAsyncData + alova.Method** 组合的方式完成服务端数据获取,这与你平时使用`useAsyncData`没什么区别。 + +```html + +``` + +### Nextjs + +Nextjs 提供了固定的服务端初始化页面数据的函数,如`getStaticProps`、`getServerSideProps`等,可以在函数中[直接使用 method 实例](/tutorial/getting-started/quick-start)调用接口。 + +```jsx +const todoListGetter = alovaInstance.Get('/todo/list', { + headers: { + 'Content-Type': 'application/json;charset=UTF-8' + } +}); + +export const getServerSideProps = async ctx => { + const list = await todoListGetter.send(); + return { + props: { + list + } + }; +}; +export default function App(props) { + return props.list.map(item => ( +
+ {item.title} + {item.time} +
+ )); +} +``` + +### Sveltekit + +Sveltekit 中也提供了`load`函数进行服务端的页面数据初始化,你同样可以在函数中[直接使用 method 实例](/tutorial/getting-started/quick-start)调用接口。例如在`+page.server.js`中调用接口。 + +```javascript title=+page.server.js +const todoListGetter = alovaInstance.Get('/todo/list', { + headers: { + 'Content-Type': 'application/json;charset=UTF-8' + } +}); + +/** @type {import('./$types').PageServerLoad} */ +export async function load({ params }) { + return { + list: todoListGetter.send() + }; +} +``` + +## 在 SSR 中使用 usehooks + +由于每个 SSR 框架都有各自的在服务端中初始化数据的方式,因此在 SSR 中生成 html 时,组件中的`useRequest`和`useWatcher`即使将`immediate`设置为`true`也不会发起请求,因为这更像是客户端初始化数据。 + +不过,如果你需要像客户端中一样初始化页面的数据,也可以设置`immediate`为`true`,当页面在浏览器中运行时,你可以和往常一样使用 alova 的所有功能。 + +## 注意事项 + +### 客户端和服务端的缓存可能不一致 + +如果你使用了 alova 的缓存功能,这里可能需要注意的是,客户端和服务端的缓存并不是共享的,这意味着如果你在初始化页面时直接使用了**usehooks**获取数据,你可能会遇到客户端和服务端渲染不一致的问题,尽管很少人这样做。 + +请看以下代码片段。 + + + + +```html + + + +``` + + + + +```jsx +function App(props) { + const { loading, data } = useRequest(alovaGetter); + return ( + <> + {loading ?
loading
: null} +
{data}
+ + ); +} +``` + +
+ + +```html + + +{#if $loading} +
loading
+{/if} +
{{ data }}
+``` + +
+
+ +以下代码假设`alovaGetter`请求在服务端存在缓存,但在客户端不存在。 + +此时在服务端生成时 html 时,由于命中缓存,`loading`为`false`而不显示`
loading
`,但在客户端初始化时由于未命中缓存,`loading`为`true`而导致显示`
loading
`,此时 SSR 框架将会提示两个端渲染不一致。 + +**解决方法** + +1. 尽量将页面数据初始化的工作放在获取函数中,而不是组件中; +2. 如果必须这样做,则可以避免在客户端和服务端使用相同的接口,或者关闭出现问题的接口缓存; +3. 如果也需要缓存,你可以在服务端数据初始化函数中清除服务端的缓存,示例代码如下: + + + + +```html + + + +``` + + + + +```jsx +import { invalidateCache } from 'alova'; + +function App(props) { + const { loading, data } = useRequest(alovaGetter); + return ( + <> + {loading ?
loading
: null} +
{data}
+ + ); +} + +export const getServerSideProps = async () => { + // 在服务端中清除缓存 + invalidateCache(alovaGetter); + return { + props: {} + }; +}; +``` + +
+ + +```javascript title=+page.server.js +import { invalidateCache } from 'alova'; + +/** @type {import('./$types').PageServerLoad} */ +export async function load({ params }) { + // 在服务端中清除缓存 + invalidateCache(alovaGetter); + return {}; +} +``` + + +
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/01-manage-apis.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/01-manage-apis.md index 28562ab19..9b2cf1a09 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/01-manage-apis.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/01-manage-apis.md @@ -1,154 +1,153 @@ ---- -title: 管理APIs -sidebar_position: 10 ---- - -在一个项目中,我们可能需要使用到成百上千个请求 api,因此管理这些请求 api 变得尤为重要。 - -你可能会像 [快速开始](/tutorial/getting-started/quick-start) 中的代码片段那样编写请求发送的代码,所有请求代码写在一个文件中。 - -```javascript -const { loading, data, error } = useRequest( - alovaInstance.Get('https://api.alovajs.org/profile', { - params: { - id: 1 - } - }) -); -``` - -这只是便于初学者理解,但在实际项目中,我们并不推荐这样做,因为 method 实例的用途不仅用于发送请求,它还可以用于操作缓存和状态,上面的用法会让这些请求 api 变得难以管理,如果你认为不对的话,你可能忘记一点: - -> 响应数据缓存的 key 是由 method 实例的请求方法(method)、请求地址(url)、请求头参数(headers)、url 参数(params)、请求体参数(requestBody)组合作为唯一标识,任意一个信息或位置不同都将被当做不同的 key。 - -因此,在实际项目中应该把 method 实例进行管理,也可以统一管理 alova 实例。 - -## api 文件结构 - -首先,你的项目需要一个统一存放 method 实例和 alova 实例的文件夹,例如叫做`api`,以下为一个常见的 api 管理结构,你也可以使用适合项目的任何结构。 - -``` -|-api -| |-index.js -> 包含所有的alova实例 -| |-methods -| | |-user.js -| | |-article.js -| | |-order.js -| | |-... -|-... -``` - -总之,你的项目应该使用适合的文件夹结构将它们管理起来。 - -> 接下来以 vue 为例展示示例代码 - -## 管理 alova 实例 - -你的项目可能需要和不同的服务器通信,也可能需要在特定的请求中使用特殊的请求方案,或者使用不同的响应拦截器等,这些都需要在项目中创建并维护多个 alova 实例,建议可以使用一个单独的文件来管理它们,例如在上面的 api 管理结构中,将使用`api/index.js`来管理。 - -```javascript title=api/index.js -import { createAlova } from 'alova'; -import VueHook from 'alova/vue'; -import GlobalFetch from 'alova/GlobalFetch'; -import { axiosRequestAdapter } from '@alova/adapter-axios'; - -// user alova instance -export const userAlova = createAlova({ - baseURL: 'https://api-user.alovajs.org', - statesHook: VueHook, - requestAdapter: GlobalFetch(), - async responded(method) { - method.config.headers.token = 'user token'; - } -}); - -// order alova instance -export const orderAlova = createAlova({ - baseURL: 'https://api-order.alovajs.org', - statesHook: VueHook, - requestAdapter: GlobalFetch(), - async responded(method) { - method.config.headers.token = 'order token'; - } -}); - -// upload alova instance -export const uploadAlova = createAlova({ - baseURL: 'https://api-order.alovajs.org', - statesHook: VueHook, - requestAdapter: axiosRequestAdapter() -}); -``` - -## 管理 method 实例 - -我们可以使用不同的 js 文件将 method 实例分类管理,例如上面的 api 管理结构中,将使用`api/methods/user.js`来管理用户信息相关的 method 实例,用`api/methods/order.js`管理订单相关的 method 实例。 - -此外,在上文中提到过一点,method 实例除了用于发送请求外,还可以用于操作缓存和状态,为了确保请求参数的个数和顺序,我们可以使用一个函数来对应一个请求 api,通过传入请求参数的形式来返回对应的 method 实例,只要传入参数是相同的,method 实例的请求信息和参数顺序也是相同的,这样就可以确保用于操作缓存和状态的 method 实例不出错。 - -```javascript title=api/methods/user.js -import { userAlova } from '..'; - -// 获取用户信息 -export const getUserInfo = id => userAlova.Get('/user/' + id); - -// 编辑用户信息 -export const editUserInfo = (name, age, mobile) => - userAlova.Post('/user', { - name, - age, - mobile - }); - -// 移除用户 -export const removeUser = id => userAlova.Delete('/user/' + id); - -// ... -``` - -在**user 组件**中可以直接导入 method 函数进行使用,并且可以在调用`invalidateCache`再次使用 method 函数来失效对应的缓存。 - -```html title=views/user.vue - - -``` +--- +title: 管理APIs +--- + +在一个项目中,我们可能需要使用到成百上千个请求 api,因此管理这些请求 api 变得尤为重要。 + +你可能会像 [快速开始](/tutorial/getting-started/quick-start) 中的代码片段那样编写请求发送的代码,所有请求代码写在一个文件中。 + +```javascript +const { loading, data, error } = useRequest( + alovaInstance.Get('https://api.alovajs.org/profile', { + params: { + id: 1 + } + }) +); +``` + +这只是便于初学者理解,但在实际项目中,我们并不推荐这样做,因为 method 实例的用途不仅用于发送请求,它还可以用于操作缓存和状态,上面的用法会让这些请求 api 变得难以管理,如果你认为不对的话,你可能忘记一点: + +> 响应数据缓存的 key 是由 method 实例的请求方法(method)、请求地址(url)、请求头参数(headers)、url 参数(params)、请求体参数(requestBody)组合作为唯一标识,任意一个信息或位置不同都将被当做不同的 key。 + +因此,在实际项目中应该把 method 实例进行管理,也可以统一管理 alova 实例。 + +## api 文件结构 + +首先,你的项目需要一个统一存放 method 实例和 alova 实例的文件夹,例如叫做`api`,以下为一个常见的 api 管理结构,你也可以使用适合项目的任何结构。 + +``` +|-api +| |-index.js -> 包含所有的alova实例 +| |-methods +| | |-user.js +| | |-article.js +| | |-order.js +| | |-... +|-... +``` + +总之,你的项目应该使用适合的文件夹结构将它们管理起来。 + +> 接下来以 vue 为例展示示例代码 + +## 管理 alova 实例 + +你的项目可能需要和不同的服务器通信,也可能需要在特定的请求中使用特殊的请求方案,或者使用不同的响应拦截器等,这些都需要在项目中创建并维护多个 alova 实例,建议可以使用一个单独的文件来管理它们,例如在上面的 api 管理结构中,将使用`api/index.js`来管理。 + +```javascript title=api/index.js +import { createAlova } from 'alova'; +import VueHook from 'alova/vue'; +import GlobalFetch from 'alova/GlobalFetch'; +import { axiosRequestAdapter } from '@alova/adapter-axios'; + +// user alova instance +export const userAlova = createAlova({ + baseURL: 'https://api-user.alovajs.org', + statesHook: VueHook, + requestAdapter: GlobalFetch(), + async responded(method) { + method.config.headers.token = 'user token'; + } +}); + +// order alova instance +export const orderAlova = createAlova({ + baseURL: 'https://api-order.alovajs.org', + statesHook: VueHook, + requestAdapter: GlobalFetch(), + async responded(method) { + method.config.headers.token = 'order token'; + } +}); + +// upload alova instance +export const uploadAlova = createAlova({ + baseURL: 'https://api-order.alovajs.org', + statesHook: VueHook, + requestAdapter: axiosRequestAdapter() +}); +``` + +## 管理 method 实例 + +我们可以使用不同的 js 文件将 method 实例分类管理,例如上面的 api 管理结构中,将使用`api/methods/user.js`来管理用户信息相关的 method 实例,用`api/methods/order.js`管理订单相关的 method 实例。 + +此外,在上文中提到过一点,method 实例除了用于发送请求外,还可以用于操作缓存和状态,为了确保请求参数的个数和顺序,我们可以使用一个函数来对应一个请求 api,通过传入请求参数的形式来返回对应的 method 实例,只要传入参数是相同的,method 实例的请求信息和参数顺序也是相同的,这样就可以确保用于操作缓存和状态的 method 实例不出错。 + +```javascript title=api/methods/user.js +import { userAlova } from '..'; + +// 获取用户信息 +export const getUserInfo = id => userAlova.Get('/user/' + id); + +// 编辑用户信息 +export const editUserInfo = (name, age, mobile) => + userAlova.Post('/user', { + name, + age, + mobile + }); + +// 移除用户 +export const removeUser = id => userAlova.Delete('/user/' + id); + +// ... +``` + +在**user 组件**中可以直接导入 method 函数进行使用,并且可以在调用`invalidateCache`再次使用 method 函数来失效对应的缓存。 + +```html title=views/user.vue + + +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/02-skills.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/02-skills.md index c1baa555f..b4d3cd409 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/02-skills.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/02-skills.md @@ -1,6 +1,5 @@ --- title: 使用技巧 -sidebar_position: 20 --- import Tabs from '@theme/Tabs'; @@ -259,8 +258,16 @@ const { data: todoCounter } = useRequest(todoCountGetter); 手动创建 promise 对象,并使用`Promise.all`完成效果。 ```javascript -const { data: todoList, onSuccess: onListSuccess, onError: onListError } = useRequest(todoListGetter); -const { data: todoCounter, onSuccess: onCountSuccess, onError: onCountError } = useRequest(todoCountGetter); +const { + data: todoList, + onSuccess: onListSuccess, + onError: onListError +} = useRequest(todoListGetter); +const { + data: todoCounter, + onSuccess: onCountSuccess, + onError: onCountError +} = useRequest(todoCountGetter); // 手动创建promise对象 const listPromise = new Promise((resolve, reject) => { @@ -302,7 +309,10 @@ const parallelRequest = async () => { ```javascript // const { data: todoList, onSuccess } = useRequest(todoListGetter); -const { data: todoDetail, send: sendTodoDetail } = useRequest(todoId => todoDetailGetter(todoId), { immediate: false }); +const { data: todoDetail, send: sendTodoDetail } = useRequest( + todoId => todoDetailGetter(todoId), + { immediate: false } +); // 先获取列表,再获取第一个todo的详情 onSuccess(event => { @@ -317,7 +327,9 @@ onSuccess(event => { ```javascript // 先让它们不自动发送请求 const { send: sendList } = useRequest(todoListGetter, { immediate: false }); -const { send: sendTodoDetail } = useRequest(todoId => todoDetailGetter(todoId), { immediate: false }); +const { send: sendTodoDetail } = useRequest(todoId => todoDetailGetter(todoId), { + immediate: false +}); // 利用send函数返回的promise对象 const serialRequest = async () => { diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/03-manage-cache-by-indexeddb.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/03-manage-cache-by-indexeddb.md index 521109e18..df74cdfcc 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/03-manage-cache-by-indexeddb.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/03-manage-cache-by-indexeddb.md @@ -1,123 +1,122 @@ ---- -title: 使用IndexedDB管理缓存 -sidebar_position: 30 ---- - -如果你正在开发需要大量使用本地缓存的应用,如图形编辑类应用、文件管理类应用等,低容量的 localStorage 已经无法满足开发需求,此时你可以使用 IndexedDB 配合 alova 进行大容量的本地缓存管理。 - -这一功能主要得益于 alova 的 [受控缓存](/tutorial/cache/controlled-cache) 功能,它可以实现自定义的缓存管理,我们来看看实践步骤。 - -这里有一个[使用 IndexedDB 管理缓存的示例](/tutorial/example/controlled-cache-by-indexeddb) - -我们以自定义管理大图片数据为例。 - -## 创建 IndexedDB 实例 - -首先创建一个 IndexedDB 实例用于操作本地缓存,并导出缓存操作的函数。 - -```javascript title=db.js -const dbVersion = 1; -let dbInstance; -const request = window.indexedDB.open('MyTestDatabase', dbVersion); -request.onupgradeneeded = ({ target }) => { - dbInstance = target.result; - const imgStore = dbInstance.createObjectStore('images', { - autoIncrement: true - }); - imgStore.createIndex('fileName', 'fileName', { - unique: true - }); -}; -request.onerror = () => { - throw new Error('Database open fail'); -}; -request.onsuccess = ({ target }) => { - dbInstance = target.result; -}; - -// 新增数据到IndexedDB -export const addImage2Cache = async (fileName, data) => { - const tx = dbInstance.transaction(['images'], 'readwrite'); - const request = tx.objectStore('images').add({ - fileName, - data - }); - return new Promise((resolve, reject) => { - request.onerror = () => { - reject('data add fail'); - }; - request.onsuccess = ({ result }) => { - resolve(result); - }; - }); -}; - -// 根据fileName获取文件数据 -export const getImageFromCache = async fileName => { - const tx = dbInstance.transaction(['images']); - const request = tx.objectStore('images').index('fileName').get(fileName); - return new Promise((resolve, reject) => { - request.onerror = () => { - reject('data add fail'); - }; - request.onsuccess = ({ target }) => { - resolve(target.result); - }; - }); -}; -``` - -## 保存数据 - -在保存数据时,我们可以在 method 的`transformData`中保存缓存,因为`transformData`只会在网络请求响应时被触发,而命中缓存时不会触发的特性。在示例代码中,将图片 blob 实例转换为 base64 数据,缓存并返回这个 base64 数据。 - -```javascript api.js -import { addImage2Cache } from './db'; - -export const image = fileName => - alovaInst.Get(`/image/${fileName}`, { - // highlight-start - async transformData(imgBlob) { - // 将blob异步转换为base64 - const reader = new FileReader(); - reader.readAsDataURL(imgBlob); - const base64Img = await new Promise(resolve => { - reader.onload = ({ target }) => { - resolve(target.result); - }; - }); - - // 缓存image数据到IndexedDB中 - await addImage2Cache(fileName, base64Img); - return base64Img; - } - // highlight-end - }); -``` - -## 获取数据 - -将这个 method 实例的`localCache`指定为一个异步函数,让缓存转变为受控状态,在这个函数中匹配 IndexedDB 中的缓存,如果匹配则返回它,否则返回`undefined`继续发起请求获取数据。 - -```javascript title=api.js -import { getImageFromCache } from './db'; - -export const image = fileName => - alovaInst.Get(`/image/${fileName}`, { - async transformData(imgBlob) { - // ... - }, - - // highlight-start - async localCache() { - // 获取缓存 - const cache = await getImageFromCache(fileName); - return cache && cache.data; - } - // highlight-end - }); -``` - -这样就基本完成了一个基本的自定义缓存管理,你也可以保存缓存的过期时间,并在`localCache`中匹配到缓存时再判断是否已过期,从而实现缓存过期功能。 - -IndexedDB 只是其中一个异步管理缓存的案例,你也可以连接你的缓存服务器来管理它们。 +--- +title: 使用IndexedDB管理缓存 +--- + +如果你正在开发需要大量使用本地缓存的应用,如图形编辑类应用、文件管理类应用等,低容量的 localStorage 已经无法满足开发需求,此时你可以使用 IndexedDB 配合 alova 进行大容量的本地缓存管理。 + +这一功能主要得益于 alova 的 [受控缓存](/tutorial/cache/controlled-cache) 功能,它可以实现自定义的缓存管理,我们来看看实践步骤。 + +这里有一个[使用 IndexedDB 管理缓存的示例](/tutorial/example/controlled-cache-by-indexeddb) + +我们以自定义管理大图片数据为例。 + +## 创建 IndexedDB 实例 + +首先创建一个 IndexedDB 实例用于操作本地缓存,并导出缓存操作的函数。 + +```javascript title=db.js +const dbVersion = 1; +let dbInstance; +const request = window.indexedDB.open('MyTestDatabase', dbVersion); +request.onupgradeneeded = ({ target }) => { + dbInstance = target.result; + const imgStore = dbInstance.createObjectStore('images', { + autoIncrement: true + }); + imgStore.createIndex('fileName', 'fileName', { + unique: true + }); +}; +request.onerror = () => { + throw new Error('Database open fail'); +}; +request.onsuccess = ({ target }) => { + dbInstance = target.result; +}; + +// 新增数据到IndexedDB +export const addImage2Cache = async (fileName, data) => { + const tx = dbInstance.transaction(['images'], 'readwrite'); + const request = tx.objectStore('images').add({ + fileName, + data + }); + return new Promise((resolve, reject) => { + request.onerror = () => { + reject('data add fail'); + }; + request.onsuccess = ({ result }) => { + resolve(result); + }; + }); +}; + +// 根据fileName获取文件数据 +export const getImageFromCache = async fileName => { + const tx = dbInstance.transaction(['images']); + const request = tx.objectStore('images').index('fileName').get(fileName); + return new Promise((resolve, reject) => { + request.onerror = () => { + reject('data add fail'); + }; + request.onsuccess = ({ target }) => { + resolve(target.result); + }; + }); +}; +``` + +## 保存数据 + +在保存数据时,我们可以在 method 的`transformData`中保存缓存,因为`transformData`只会在网络请求响应时被触发,而命中缓存时不会触发的特性。在示例代码中,将图片 blob 实例转换为 base64 数据,缓存并返回这个 base64 数据。 + +```javascript api.js +import { addImage2Cache } from './db'; + +export const image = fileName => + alovaInst.Get(`/image/${fileName}`, { + // highlight-start + async transformData(imgBlob) { + // 将blob异步转换为base64 + const reader = new FileReader(); + reader.readAsDataURL(imgBlob); + const base64Img = await new Promise(resolve => { + reader.onload = ({ target }) => { + resolve(target.result); + }; + }); + + // 缓存image数据到IndexedDB中 + await addImage2Cache(fileName, base64Img); + return base64Img; + } + // highlight-end + }); +``` + +## 获取数据 + +将这个 method 实例的`localCache`指定为一个异步函数,让缓存转变为受控状态,在这个函数中匹配 IndexedDB 中的缓存,如果匹配则返回它,否则返回`undefined`继续发起请求获取数据。 + +```javascript title=api.js +import { getImageFromCache } from './db'; + +export const image = fileName => + alovaInst.Get(`/image/${fileName}`, { + async transformData(imgBlob) { + // ... + }, + + // highlight-start + async localCache() { + // 获取缓存 + const cache = await getImageFromCache(fileName); + return cache && cache.data; + } + // highlight-end + }); +``` + +这样就基本完成了一个基本的自定义缓存管理,你也可以保存缓存的过期时间,并在`localCache`中匹配到缓存时再判断是否已过期,从而实现缓存过期功能。 + +IndexedDB 只是其中一个异步管理缓存的案例,你也可以连接你的缓存服务器来管理它们。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/04-multiple-servers.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/04-multiple-servers.md index 89cfa40c7..235018429 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/04-multiple-servers.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/04-multiple-servers.md @@ -1,20 +1,19 @@ ---- -title: 多服务器 -sidebar_position: 40 ---- - -如果你的项目需要请求多个服务器,可以创建多个 alova 实例来分别对应不同的服务器,为了便于区分不同的环境,也可以使用环境变量来管理多个服务器的 host。 - -```ts -import { createAlova } from '@alova/core'; - -// 创建user相关的alova实例 -const userAlova = createAlova({ - baseURL: VITE_API_USER -}); - -// 创建order相关的alova实例 -const alova2 = createAlova({ - baseURL: VITE_API_ORDER -}); -``` +--- +title: 多服务器 +--- + +如果你的项目需要请求多个服务器,可以创建多个 alova 实例来分别对应不同的服务器,为了便于区分不同的环境,也可以使用环境变量来管理多个服务器的 host。 + +```ts +import { createAlova } from '@alova/core'; + +// 创建user相关的alova实例 +const userAlova = createAlova({ + baseURL: VITE_API_USER +}); + +// 创建order相关的alova实例 +const alova2 = createAlova({ + baseURL: VITE_API_ORDER +}); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/05-middleware.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/05-middleware.md index d8912f57e..d2242dc8c 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/05-middleware.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/05-middleware.md @@ -1,29 +1,28 @@ ---- -title: 常用中间件实践 -sidebar_position: 50 ---- - -## 延迟更新 loading - -当响应非常快的时候,加载状态会出现一次闪烁,这给会用户带来糟糕的体验,延迟更新 loading 可以让加载状态在一段时间后才显示,如果在这段时间内完成响应则不会出现加载状态。我们来实现一个带延迟更新 loading 的 middleware。 - -```javascript -const delayLoadingMiddleware = - (delayTimer = 1000) => - async (ctx, next) => { - // 自行控制loading - ctx.controlLoading(); - - // 延迟特定时间更新 - const timer = setTimeout(() => { - ctx.update({ loading: true }); - }, delayTimer); - await next(); - ctx.update({ loading: false }); - clearTimeout(timer); - }; - -useRequest(methodInstance, { - middleware: delayLoadingMiddleware() -}); -``` +--- +title: 常用中间件实践 +--- + +## 延迟更新 loading + +当响应非常快的时候,加载状态会出现一次闪烁,这给会用户带来糟糕的体验,延迟更新 loading 可以让加载状态在一段时间后才显示,如果在这段时间内完成响应则不会出现加载状态。我们来实现一个带延迟更新 loading 的 middleware。 + +```javascript +const delayLoadingMiddleware = + (delayTimer = 1000) => + async (ctx, next) => { + // 自行控制loading + ctx.controlLoading(); + + // 延迟特定时间更新 + const timer = setTimeout(() => { + ctx.update({ loading: true }); + }, delayTimer); + await next(); + ctx.update({ loading: false }); + clearTimeout(timer); + }; + +useRequest(methodInstance, { + middleware: delayLoadingMiddleware() +}); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/12-parallel-request.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/12-parallel-request.md index 006742dba..42eb62cf6 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/12-parallel-request.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/12-parallel-request.md @@ -1,60 +1,67 @@ ---- -title: 并行请求 -sidebar_position: 120 ---- - -## 使用 method - -由于 method 是 PromiseLike 实例,通过 method 发送并行请求只需要使用`Promise.all`等待即可。 - -```javascript -const [todoList, todoCounter] = await Promise.all[(todoListGetter, todoCountGetter)]; -``` - -## 使用 useRequest - -简单的并行请求,只需要同时调用多个 useRequest 即可。 - -```javascript -const { data: todoList } = useRequest(todoListGetter); -const { data: todoCounter } = useRequest(todoCountGetter); -``` - -但这样的请求只适用于单纯的并行请求,如果你需要在并行请求都完成后再进行某些操作,有以下两种方式可以实现: - -### 方法 1 - -手动创建 promise 对象,并使用`Promise.all`完成效果。 - -```javascript -const { data: todoList, onSuccess: onListSuccess, onError: onListError } = useRequest(todoListGetter); -const { data: todoCounter, onSuccess: onCountSuccess, onError: onCountError } = useRequest(todoCountGetter); - -// 手动创建promise对象 -const listPromise = new Promise((resolve, reject) => { - onListSuccess(resolve); - onListError(reject); -}); -const countPromise = new Promise((resolve, reject) => { - onCountSuccess(resolve); - onCountError(reject); -}); -const [listEvent, countEvent] = await Promise.all([listPromise, countPromise]); -// 并行请求完成,继续处理业务... -``` - -### 方法 2 - -使用`useRequest`函数返回的`send`函数,调用`send`将会返回一个可用的 promise 对象。 - -```javascript -// 先让它们不自动发送请求 -const { send: sendList } = useRequest(todoListGetter, { immediate: false }); -const { send: sendCount } = useRequest(todoCountGetter, { immediate: false }); - -// 利用send函数返回的promise对象 -const parallelRequest = async () => { - const [listResponse, countResponse] = await Promise.all([sendList(), sendCount()]); - // 并行请求完成,继续处理业务... -}; -``` +--- +title: 并行请求 +--- + +## 使用 method + +由于 method 是 PromiseLike 实例,通过 method 发送并行请求只需要使用`Promise.all`等待即可。 + +```javascript +const [todoList, todoCounter] = await Promise.all[(todoListGetter, todoCountGetter)]; +``` + +## 使用 useRequest + +简单的并行请求,只需要同时调用多个 useRequest 即可。 + +```javascript +const { data: todoList } = useRequest(todoListGetter); +const { data: todoCounter } = useRequest(todoCountGetter); +``` + +但这样的请求只适用于单纯的并行请求,如果你需要在并行请求都完成后再进行某些操作,有以下两种方式可以实现: + +### 方法 1 + +手动创建 promise 对象,并使用`Promise.all`完成效果。 + +```javascript +const { + data: todoList, + onSuccess: onListSuccess, + onError: onListError +} = useRequest(todoListGetter); +const { + data: todoCounter, + onSuccess: onCountSuccess, + onError: onCountError +} = useRequest(todoCountGetter); + +// 手动创建promise对象 +const listPromise = new Promise((resolve, reject) => { + onListSuccess(resolve); + onListError(reject); +}); +const countPromise = new Promise((resolve, reject) => { + onCountSuccess(resolve); + onCountError(reject); +}); +const [listEvent, countEvent] = await Promise.all([listPromise, countPromise]); +// 并行请求完成,继续处理业务... +``` + +### 方法 2 + +使用`useRequest`函数返回的`send`函数,调用`send`将会返回一个可用的 promise 对象。 + +```javascript +// 先让它们不自动发送请求 +const { send: sendList } = useRequest(todoListGetter, { immediate: false }); +const { send: sendCount } = useRequest(todoCountGetter, { immediate: false }); + +// 利用send函数返回的promise对象 +const parallelRequest = async () => { + const [listResponse, countResponse] = await Promise.all([sendList(), sendCount()]); + // 并行请求完成,继续处理业务... +}; +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/13-serial-request.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/13-serial-request.md index 54fda3717..5e2e7b097 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/13-serial-request.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/07-best-practice/13-serial-request.md @@ -1,49 +1,53 @@ ---- -title: 串行请求 -sidebar_position: 130 ---- - -## 使用 method - -由于 method 是 PromiseLike 实例,可以使用`await`等待请求成功。 - -```javascript -const todoList = await todoListGetter; -const todoDetail = await todoDetailGetter(todoList[0].id); -``` - -## 使用 useRequest - -串行请求也具有两种方式。 - -### 方法 1 - -让第一个请求自动发出,第二个请求在第一个请求的`onSuccess`回调中触发,即可完成串行请求,可通过以下写法完成串行请求: - -```javascript -// -const { data: todoList, onSuccess } = useRequest(todoListGetter); -const { data: todoDetail, send: sendTodoDetail } = useRequest(todoId => todoDetailGetter(todoId), { immediate: false }); - -// 先获取列表,再获取第一个todo的详情 -onSuccess(event => { - sendTodoDetail(event.todoList[0].id); -}); -``` - -### 方法 2 - -使用`useRequest`函数返回的`send`函数,调用`send`将会返回一个可用的 promise 对象。 - -```javascript -// 先让它们不自动发送请求 -const { send: sendList } = useRequest(todoListGetter, { immediate: false }); -const { send: sendTodoDetail } = useRequest(todoId => todoDetailGetter(todoId), { immediate: false }); - -// 利用send函数返回的promise对象 -const serialRequest = async () => { - const todoList = await sendList(); - const todoDetail = await sendTodoDetail(todoList[0].id); - // 串行请求完成,继续处理业务... -}; -``` +--- +title: 串行请求 +--- + +## 使用 method + +由于 method 是 PromiseLike 实例,可以使用`await`等待请求成功。 + +```javascript +const todoList = await todoListGetter; +const todoDetail = await todoDetailGetter(todoList[0].id); +``` + +## 使用 useRequest + +串行请求也具有两种方式。 + +### 方法 1 + +让第一个请求自动发出,第二个请求在第一个请求的`onSuccess`回调中触发,即可完成串行请求,可通过以下写法完成串行请求: + +```javascript +// +const { data: todoList, onSuccess } = useRequest(todoListGetter); +const { data: todoDetail, send: sendTodoDetail } = useRequest( + todoId => todoDetailGetter(todoId), + { immediate: false } +); + +// 先获取列表,再获取第一个todo的详情 +onSuccess(event => { + sendTodoDetail(event.todoList[0].id); +}); +``` + +### 方法 2 + +使用`useRequest`函数返回的`send`函数,调用`send`将会返回一个可用的 promise 对象。 + +```javascript +// 先让它们不自动发送请求 +const { send: sendList } = useRequest(todoListGetter, { immediate: false }); +const { send: sendTodoDetail } = useRequest(todoId => todoDetailGetter(todoId), { + immediate: false +}); + +// 利用send函数返回的promise对象 +const serialRequest = async () => { + const todoList = await sendList(); + const todoDetail = await sendTodoDetail(todoList[0].id); + // 串行请求完成,继续处理业务... +}; +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/01-alova-mock.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/01-alova-mock.md index 36676f14b..2e0ddf0a5 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/01-alova-mock.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/01-alova-mock.md @@ -1,6 +1,5 @@ --- title: 模拟数据 -sidebar_position: 10 --- import Tabs from '@theme/Tabs'; diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/02-alova-adapter-xhr.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/02-alova-adapter-xhr.md index 780837842..69d636fb8 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/02-alova-adapter-xhr.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/02-alova-adapter-xhr.md @@ -1,337 +1,336 @@ ---- -title: XMLHttpRequest适配器 -sidebar_position: 20 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -## 安装 - - - - -```bash -npm install @alova/adapter-xhr --save -``` - - - - -```bash -yarn add @alova/adapter-xhr -``` - - - - -## 使用方法 - -### 创建 alova - -使用 **xhrRequestAdapter** 作为 alova 的请求适配器。 - -```javascript -import { createAlova } from 'alova'; -import { xhrRequestAdapter } from '@alova/adapter-xhr'; - -const alovaInst = createAlova({ - // ... - requestAdapter: xhrResponseAdapter() - // ... -}); -``` - -### 请求 - -XMLHttpRequest 适配器提供了基本的配置参数,包含`responseType`、`withCredentials`、`mimeType`、`auth`,具体如下: - - - - -```html - -
加载中...
-
请求数据为:{{ data }}
-
- - -``` - -
- - -```jsx -const list = () => - alovaInst.Get('/list', { - /** - * 设置响应数据类型 - * 可以设置更改响应类型。 值为:“arraybuffer”、“blob”、“document”、“json”和“text” - * 默认为“json” - */ - responseType: 'text', - - /** - * 当凭证要包含在跨源请求中时为true。 当它们被排除在跨源请求中以及当 cookie 在其响应中被忽略时为 false。 默认为false - */ - withCredentials: true, - - /** - * 设置响应数据的mimeType - */ - mimeType: 'text/plain; charset=x-user-defined', - - /** - * auth 表示使用 HTTP Basic 身份验证,并提供凭据。 - * 这将设置一个 `Authorization` 标头,覆盖任何现有的 - * 使用 `headers` 设置的 `Authorization` 自定义标头。 - * 请注意,只有 HTTP Basic 身份验证可以通过此参数进行配置。 - * 对于 Bearer 令牌等,请改用 `Authorization` 自定义标头。 - */ - auth: { - username: 'name1', - password: '123456' - } - }); - -const App = () => { - const { loading, data } = useRequest(list); - - return ( - { loading ?
加载中...
: null } -
请求数据为:{ JSON.stringify(data) }
- ) -}; -``` - -
- - -```html - - -{#if $loading} -
加载中...
-{/if} -
请求数据为:{ data }
-``` - -
-
- -### 上传 - -使用`FormData`上传文件,这个`FormData`实例会通过`xhr.send`发送到服务端。上传时将自动设置`Content-Type`,不需要自定义设置为`multipart/form-data`。 - -```javascript -const uploadFile = imageFile => { - const formData = new FormData(); - formData.append('file', imageFile); - return alovaInst.Post('/uploadImg', formData, { - // 开启上传进度 - enableUpload: true - }); -}; - -const { - loading, - data, - uploading, - send: sendUpload -} = useRequest(uploadFile, { - immediate: false -}); - -// 图片选择事件回调 -const handleImageChoose = ({ target }) => { - sendUpload(target.files[0]); -}; -``` - -### 下载 - -将请求 url 指向文件地址即可下载,你也可以通过将`enableDownload`设置为 true 来开启下载进度。 - -```javascript -const downloadFile = () => - alovaInst.Get('/bigImage.jpg', { - // 开启下载进度 - enableDownload: true, - responseType: 'blob' - }); - -const { loading, data, downloading, send, onSuccess } = useRequest(downloadFile, { - immediate: false -}); -onSuccess(({ data: imageBlob }) => { - // 下载图片 - const anchor = document.createElement('a'); - anchor.href = URL.createObjectURL(blob); - anchor.download = 'image.jpg'; - anchor.click(); - URL.revokeObjectURL(anchor.href); -}); -const handleImageDownload = () => { - send(); -}; -``` - -## 模拟请求适配器兼容 - -在开发应用时,我们仍然可能需要用到模拟请求。只是默认情况下,[模拟请求适配器(@alova/mock)](/tutorial/request-adapter/alova-mock)的响应数据是一个`Response`实例,即默认兼容`GlobalFetch`请求适配器,当使用 XMLHttpRequest 适配器时,我们需要让模拟请求适配器的响应数据适配 XMLHttpRequest 适配器,此时你需要使用**@alova/adapter-xhr**包中导出的`xhrMockResponse`作为响应适配器。 - -```javascript -import { defineMock, createAlovaMockAdapter } from '@alova/mock'; -import { xhrRequestAdapter, xhrMockResponse } from '@alova/adapter-xhr'; - -const mocks = defineMock({ - // ... -}); - -// 模拟数据请求适配器 -export default createAlovaMockAdapter([mocks], { - // 指定请求适配器后,未匹配模拟接口的请求将使用这个适配器发送请求 - httpAdapter: xhrRequestAdapter(), - - // 使用xhrMockResponse,让模拟数据适配XMLHttpRequest适配器 - onMockResponse: xhrMockResponse -}); - -export const alovaInst = createAlova({ - // ... - // 通过环境变量控制是否使用模拟请求适配器 - requestAdapter: process.env.NODE_ENV === 'development' ? mockAdapter : xhrRequestAdapter() -}); -``` - -## Typescript - -XMLHttpRequest 请求适配器 提供了完整的类型适配。 - -### method 配置 - -在创建 method 实例时,除了 method 中通用的配置项外,你还可以使用`AlovaXHRRequestConfig`中的配置项。 - -```typescript -/** - * xhr请求配置参数 - */ -interface AlovaXHRRequestConfig { - /** - * 设置响应数据类型。 - * - * 可以设置更改响应类型。 值为:“arraybuffer”、“blob”、“document”、“json”和“text”。 - * 设置1:如果当前全局对象不是 Window 对象,则忽略设置为“文档”。 - * 设置2:如果状态正在加载或完成,则抛出“InvalidStateError”DOMException。 - * 设置3:如果设置了同步标志且当前全局对象是 Window 对象,则抛出“InvalidAccessError”DOMException。 - * @default "json" - */ - responseType?: XMLHttpRequestResponseType; - - /** - * 当凭证要包含在跨源请求中时为true。 当它们被排除在跨源请求中以及当 cookie 在其响应中被忽略时为 false。 默认为false。 - * 如果状态未发送或未打开,或者设置了send() 标志,则抛出“InvalidStateError”DOMException。 - * @default false - */ - withCredentials?: boolean; - - /** - * 设置响应数据的mimeType - */ - mimeType?: string; - - /** - * `auth` 表示应该使用 HTTP Basic 身份验证,并提供凭据。 - * 这将设置一个 `Authorization` 标头,覆盖任何现有的 - * 使用 `headers` 设置的 `Authorization` 自定义标头。 - * 请注意,只有 HTTP Basic 身份验证可以通过此参数进行配置。 - * 对于 Bearer 令牌等,请改用 `Authorization` 自定义标头。 - */ - auth?: { - username: string; - password: string; - }; -} -``` - -### 响应数据 - -XMLHttpRequest 适配器响应数据如下: - -```typescript -interface AlovaXHRResponseHeaders { - [x: string]: any; -} -interface AlovaXHRResponse { - status: number; - statusText: string; - data: T; - headers: AlovaXHRResponseHeaders; -} -``` +--- +title: XMLHttpRequest适配器 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## 安装 + + + + +```bash +npm install @alova/adapter-xhr --save +``` + + + + +```bash +yarn add @alova/adapter-xhr +``` + + + + +## 使用方法 + +### 创建 alova + +使用 **xhrRequestAdapter** 作为 alova 的请求适配器。 + +```javascript +import { createAlova } from 'alova'; +import { xhrRequestAdapter } from '@alova/adapter-xhr'; + +const alovaInst = createAlova({ + // ... + requestAdapter: xhrResponseAdapter() + // ... +}); +``` + +### 请求 + +XMLHttpRequest 适配器提供了基本的配置参数,包含`responseType`、`withCredentials`、`mimeType`、`auth`,具体如下: + + + + +```html + +
加载中...
+
请求数据为:{{ data }}
+
+ + +``` + +
+ + +```jsx +const list = () => + alovaInst.Get('/list', { + /** + * 设置响应数据类型 + * 可以设置更改响应类型。 值为:“arraybuffer”、“blob”、“document”、“json”和“text” + * 默认为“json” + */ + responseType: 'text', + + /** + * 当凭证要包含在跨源请求中时为true。 当它们被排除在跨源请求中以及当 cookie 在其响应中被忽略时为 false。 默认为false + */ + withCredentials: true, + + /** + * 设置响应数据的mimeType + */ + mimeType: 'text/plain; charset=x-user-defined', + + /** + * auth 表示使用 HTTP Basic 身份验证,并提供凭据。 + * 这将设置一个 `Authorization` 标头,覆盖任何现有的 + * 使用 `headers` 设置的 `Authorization` 自定义标头。 + * 请注意,只有 HTTP Basic 身份验证可以通过此参数进行配置。 + * 对于 Bearer 令牌等,请改用 `Authorization` 自定义标头。 + */ + auth: { + username: 'name1', + password: '123456' + } + }); + +const App = () => { + const { loading, data } = useRequest(list); + + return ( + { loading ?
加载中...
: null } +
请求数据为:{ JSON.stringify(data) }
+ ) +}; +``` + +
+ + +```html + + +{#if $loading} +
加载中...
+{/if} +
请求数据为:{ data }
+``` + +
+
+ +### 上传 + +使用`FormData`上传文件,这个`FormData`实例会通过`xhr.send`发送到服务端。上传时将自动设置`Content-Type`,不需要自定义设置为`multipart/form-data`。 + +```javascript +const uploadFile = imageFile => { + const formData = new FormData(); + formData.append('file', imageFile); + return alovaInst.Post('/uploadImg', formData, { + // 开启上传进度 + enableUpload: true + }); +}; + +const { + loading, + data, + uploading, + send: sendUpload +} = useRequest(uploadFile, { + immediate: false +}); + +// 图片选择事件回调 +const handleImageChoose = ({ target }) => { + sendUpload(target.files[0]); +}; +``` + +### 下载 + +将请求 url 指向文件地址即可下载,你也可以通过将`enableDownload`设置为 true 来开启下载进度。 + +```javascript +const downloadFile = () => + alovaInst.Get('/bigImage.jpg', { + // 开启下载进度 + enableDownload: true, + responseType: 'blob' + }); + +const { loading, data, downloading, send, onSuccess } = useRequest(downloadFile, { + immediate: false +}); +onSuccess(({ data: imageBlob }) => { + // 下载图片 + const anchor = document.createElement('a'); + anchor.href = URL.createObjectURL(blob); + anchor.download = 'image.jpg'; + anchor.click(); + URL.revokeObjectURL(anchor.href); +}); +const handleImageDownload = () => { + send(); +}; +``` + +## 模拟请求适配器兼容 + +在开发应用时,我们仍然可能需要用到模拟请求。只是默认情况下,[模拟请求适配器(@alova/mock)](/tutorial/request-adapter/alova-mock)的响应数据是一个`Response`实例,即默认兼容`GlobalFetch`请求适配器,当使用 XMLHttpRequest 适配器时,我们需要让模拟请求适配器的响应数据适配 XMLHttpRequest 适配器,此时你需要使用**@alova/adapter-xhr**包中导出的`xhrMockResponse`作为响应适配器。 + +```javascript +import { defineMock, createAlovaMockAdapter } from '@alova/mock'; +import { xhrRequestAdapter, xhrMockResponse } from '@alova/adapter-xhr'; + +const mocks = defineMock({ + // ... +}); + +// 模拟数据请求适配器 +export default createAlovaMockAdapter([mocks], { + // 指定请求适配器后,未匹配模拟接口的请求将使用这个适配器发送请求 + httpAdapter: xhrRequestAdapter(), + + // 使用xhrMockResponse,让模拟数据适配XMLHttpRequest适配器 + onMockResponse: xhrMockResponse +}); + +export const alovaInst = createAlova({ + // ... + // 通过环境变量控制是否使用模拟请求适配器 + requestAdapter: process.env.NODE_ENV === 'development' ? mockAdapter : xhrRequestAdapter() +}); +``` + +## Typescript + +XMLHttpRequest 请求适配器 提供了完整的类型适配。 + +### method 配置 + +在创建 method 实例时,除了 method 中通用的配置项外,你还可以使用`AlovaXHRRequestConfig`中的配置项。 + +```typescript +/** + * xhr请求配置参数 + */ +interface AlovaXHRRequestConfig { + /** + * 设置响应数据类型。 + * + * 可以设置更改响应类型。 值为:“arraybuffer”、“blob”、“document”、“json”和“text”。 + * 设置1:如果当前全局对象不是 Window 对象,则忽略设置为“文档”。 + * 设置2:如果状态正在加载或完成,则抛出“InvalidStateError”DOMException。 + * 设置3:如果设置了同步标志且当前全局对象是 Window 对象,则抛出“InvalidAccessError”DOMException。 + * @default "json" + */ + responseType?: XMLHttpRequestResponseType; + + /** + * 当凭证要包含在跨源请求中时为true。 当它们被排除在跨源请求中以及当 cookie 在其响应中被忽略时为 false。 默认为false。 + * 如果状态未发送或未打开,或者设置了send() 标志,则抛出“InvalidStateError”DOMException。 + * @default false + */ + withCredentials?: boolean; + + /** + * 设置响应数据的mimeType + */ + mimeType?: string; + + /** + * `auth` 表示应该使用 HTTP Basic 身份验证,并提供凭据。 + * 这将设置一个 `Authorization` 标头,覆盖任何现有的 + * 使用 `headers` 设置的 `Authorization` 自定义标头。 + * 请注意,只有 HTTP Basic 身份验证可以通过此参数进行配置。 + * 对于 Bearer 令牌等,请改用 `Authorization` 自定义标头。 + */ + auth?: { + username: string; + password: string; + }; +} +``` + +### 响应数据 + +XMLHttpRequest 适配器响应数据如下: + +```typescript +interface AlovaXHRResponseHeaders { + [x: string]: any; +} +interface AlovaXHRResponse { + status: number; + statusText: string; + data: T; + headers: AlovaXHRResponseHeaders; +} +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/03-alova-adapter-axios.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/03-alova-adapter-axios.md index 337bbab35..ec9906789 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/03-alova-adapter-axios.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/03-alova-adapter-axios.md @@ -1,291 +1,290 @@ ---- -title: axios适配器 -sidebar_position: 30 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -## 安装 - - - - -```bash -npm install @alova/adapter-axios --save -``` - - - - -```bash -yarn add @alova/adapter-axios -``` - - - - -## 使用方法 - -### 创建 alova - -使用 **axiosRequestAdapter** 作为 alova 的请求适配器。 - -```javascript -import { createAlova } from 'alova'; -import { axiosRequestAdapter } from '@alova/adapter-axios'; - -const alovaInst = createAlova({ - // ... - requestAdapter: axiosRequestAdapter() - // ... -}); -``` - -适配器内部将会使用默认的 axios 实例进行请求,如果你为 axios 设置了一些全局参数,你可能需要注意以下两点: - -1. 优先使用 axios 实例内的`baseURL`和`timeout`参数,因此如果你在 axios 实例上设置了这些参数,那么就可以不需要在`createAlova`时设置了; -2. alova 实例的`beforeRequest`钩子将会早于 axios 的`interceptor.request`触发,alova 实例的`responded`钩子将会晚于 axios 实例的`interceptor.response`触发; - -> 你也可以[使用自定义的 axios 实例](#使用自定义的-axios-实例) - -### 请求 - -请求的使用方法与 web 环境中使用完全一致。已经完全兼容**axios**,你可以在创建 method 实例的*config*中指定`axios`支持的[全部配置项](https://axios-http.com/docs/req_config) - - - - -```html - -
加载中...
-
请求数据为:{{ data }}
-
- - -``` - -
- - -```jsx -const list = () => - alovaInst.Get('/list', { - // 设置的参数将传递给axios - paramsSerializer: params => { - return Qs.stringify(params, {arrayFormat: 'brackets'}) - }, - }); - -const App = () => { - const { loading, data } = useRequest(list); - - return ( - { loading ?
加载中...
: null } -
请求数据为:{ JSON.stringify(data) }
- ) -}; -``` - -
- - -```html - - -{#if $loading} -
加载中...
-{/if} -
请求数据为:{ data }
-``` - -
-
- -### 上传 - -使用`FormData`上传文件,这个`FormData`实例会被传递到 axios 中,与 axios 上传文件用法保持了一致。 - -```javascript -const uploadFile = imageFile => { - const formData = new FormData(); - formData.append('file', imageFile); - return alovaInst.Post('/uploadImg', formData, { - // 开启上传进度 - enableUpload: true - }); -}; - -const { - loading, - data, - uploading, - send: sendUpload -} = useRequest(uploadFile, { - immediate: false -}); - -// 图片选择事件回调 -const handleImageChoose = ({ target }) => { - sendUpload(target.files[0]); -}; -``` - -### 下载 - -将请求 url 指向文件地址即可下载,你也可以通过将`enableDownload`设置为 true 来开启下载进度。 - -```javascript -const downloadFile = () => - alovaInst.Get('/bigImage.jpg', { - // 开启下载进度 - enableDownload: true, - responseType: 'blob' - }); - -const { loading, data, downloading, send, onSuccess } = useRequest(downloadFile, { - immediate: false -}); -onSuccess(({ data: imageBlob }) => { - // 下载图片 - const anchor = document.createElement('a'); - anchor.href = URL.createObjectURL(blob); - anchor.download = 'image.jpg'; - anchor.click(); - URL.revokeObjectURL(anchor.href); -}); -const handleImageDownload = () => { - send(); -}; -``` - -## 使用自定义的 axios 实例 - -在默认情况下,此适配器会使用默认的 axios 实例进行请求,但在一些情况下你需要使用自定义创建的 axios 实例,你可以这么做: - -```javascript -const customAxios = axios.create({ - // ... -}); - -const alovaInst = createAlova({ - // ... - // highlight-start - requestAdapter: axiosRequestAdapter({ - axios: customAxios - }) - // highlight-end -}); -``` - -## 模拟请求适配器兼容 - -在开发应用时,我们仍然可能需要用到模拟请求。只是默认情况下,[模拟请求适配器(@alova/mock)](/tutorial/request-adapter/alova-mock)的响应数据是一个`Response`实例,即默认兼容`GlobalFetch`请求适配器,当使用 axios 适配器时,我们需要让模拟请求适配器的响应数据是**AxiosResponse**兼容的,错误实例是**AxiosError**,因此你需要使用**@alova/adapter-axios**包中导出的`axiosMockResponse`作为响应适配器。 - -```javascript -import { defineMock, createAlovaMockAdapter } from '@alova/mock'; -import { axiosRequestAdapter, axiosMockResponse } from '@alova/adapter-axios'; - -const mocks = defineMock({ - // ... -}); - -// 模拟数据请求适配器 -export default createAlovaMockAdapter([mocks], { - // 指定axios请求适配器后,未匹配模拟接口的请求将使用这个适配器发送请求 - httpAdapter: axiosRequestAdapter(), - - // axiosMockResponse中包含了onMockResponse和onMockError - // 用于将模拟数据转换为AxiosResponse和AxiosError兼容的格式 - ...axiosMockResponse -}); - -export const alovaInst = createAlova({ - // ... - // 通过环境变量控制是否使用模拟请求适配器 - requestAdapter: process.env.NODE_ENV === 'development' ? mockAdapter : axiosRequestAdapter() -}); -``` - -## Typescript - -axios 请求适配器 提供了完整的类型适配,method 配置、响应数据的类型将与 axios 的类型完全匹配。 - -### method 配置 - -在创建 method 实例时,除了 method 中通用的配置项外,你还可以使用`AxiosRequestConfig`中的配置项,我们已经在类型中去除了和 method 实例通用配置冲突的项。 - -```typescript -/** - * axios请求配置参数 - * 去掉了与method冲突的属性 - */ -export type AlovaAxiosRequestConfig = Omit< - AxiosRequestConfig, - | 'url' - | 'method' - | 'baseURL' - | 'headers' - | 'params' - | 'data' - | 'timeout' - | 'cancelToken' - | 'signal' - | 'onUploadProgress' - | 'onDownloadProgress' ->; -``` - -### 响应数据 - -axios 的响应数据类型是`AxiosResponse`,当你使用 axios 适配器时,也将获得相同格式的响应数据。在实际使用中,我们通常需要在全局处理响应数据,一个简单的实例如下: - -```typescript -const alovaInst = createAlova( - baseURL: 'https://api.alovajs.org', - requestAdapter: axiosRequestAdapter(), - responded(response) { - // response自动被推断为AxiosResponse类型 - return response.data; - } -}); -``` - -### 错误 - -当 axios 遇到非 20x 和 30x 的响应状态码时将会抛出错误,为了包含更多信息,axios 将错误实例自定义设计成了一个`AxiosError`实例,而不是普通的 Error 实例,因此当遇到服务端错误或网络错误时都将抛出一个错误,你可以在全局的错误回调中捕获它。 - -```typescript -const alovaInst = createAlova( - baseURL: 'https://api.alovajs.org', - requestAdapter: axiosRequestAdapter(), - responded: { - onSuccess(response) { - // response自动被推断为AxiosResponse类型 - return response.data; - }, - onError(err: AxiosError) { - // err默认为any,你可以强制转换为AxiosError处理 - // ... - } - } -}); -``` +--- +title: axios适配器 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## 安装 + + + + +```bash +npm install @alova/adapter-axios --save +``` + + + + +```bash +yarn add @alova/adapter-axios +``` + + + + +## 使用方法 + +### 创建 alova + +使用 **axiosRequestAdapter** 作为 alova 的请求适配器。 + +```javascript +import { createAlova } from 'alova'; +import { axiosRequestAdapter } from '@alova/adapter-axios'; + +const alovaInst = createAlova({ + // ... + requestAdapter: axiosRequestAdapter() + // ... +}); +``` + +适配器内部将会使用默认的 axios 实例进行请求,如果你为 axios 设置了一些全局参数,你可能需要注意以下两点: + +1. 优先使用 axios 实例内的`baseURL`和`timeout`参数,因此如果你在 axios 实例上设置了这些参数,那么就可以不需要在`createAlova`时设置了; +2. alova 实例的`beforeRequest`钩子将会早于 axios 的`interceptor.request`触发,alova 实例的`responded`钩子将会晚于 axios 实例的`interceptor.response`触发; + +> 你也可以[使用自定义的 axios 实例](#使用自定义的-axios-实例) + +### 请求 + +请求的使用方法与 web 环境中使用完全一致。已经完全兼容**axios**,你可以在创建 method 实例的*config*中指定`axios`支持的[全部配置项](https://axios-http.com/docs/req_config) + + + + +```html + +
加载中...
+
请求数据为:{{ data }}
+
+ + +``` + +
+ + +```jsx +const list = () => + alovaInst.Get('/list', { + // 设置的参数将传递给axios + paramsSerializer: params => { + return Qs.stringify(params, {arrayFormat: 'brackets'}) + }, + }); + +const App = () => { + const { loading, data } = useRequest(list); + + return ( + { loading ?
加载中...
: null } +
请求数据为:{ JSON.stringify(data) }
+ ) +}; +``` + +
+ + +```html + + +{#if $loading} +
加载中...
+{/if} +
请求数据为:{ data }
+``` + +
+
+ +### 上传 + +使用`FormData`上传文件,这个`FormData`实例会被传递到 axios 中,与 axios 上传文件用法保持了一致。 + +```javascript +const uploadFile = imageFile => { + const formData = new FormData(); + formData.append('file', imageFile); + return alovaInst.Post('/uploadImg', formData, { + // 开启上传进度 + enableUpload: true + }); +}; + +const { + loading, + data, + uploading, + send: sendUpload +} = useRequest(uploadFile, { + immediate: false +}); + +// 图片选择事件回调 +const handleImageChoose = ({ target }) => { + sendUpload(target.files[0]); +}; +``` + +### 下载 + +将请求 url 指向文件地址即可下载,你也可以通过将`enableDownload`设置为 true 来开启下载进度。 + +```javascript +const downloadFile = () => + alovaInst.Get('/bigImage.jpg', { + // 开启下载进度 + enableDownload: true, + responseType: 'blob' + }); + +const { loading, data, downloading, send, onSuccess } = useRequest(downloadFile, { + immediate: false +}); +onSuccess(({ data: imageBlob }) => { + // 下载图片 + const anchor = document.createElement('a'); + anchor.href = URL.createObjectURL(blob); + anchor.download = 'image.jpg'; + anchor.click(); + URL.revokeObjectURL(anchor.href); +}); +const handleImageDownload = () => { + send(); +}; +``` + +## 使用自定义的 axios 实例 + +在默认情况下,此适配器会使用默认的 axios 实例进行请求,但在一些情况下你需要使用自定义创建的 axios 实例,你可以这么做: + +```javascript +const customAxios = axios.create({ + // ... +}); + +const alovaInst = createAlova({ + // ... + // highlight-start + requestAdapter: axiosRequestAdapter({ + axios: customAxios + }) + // highlight-end +}); +``` + +## 模拟请求适配器兼容 + +在开发应用时,我们仍然可能需要用到模拟请求。只是默认情况下,[模拟请求适配器(@alova/mock)](/tutorial/request-adapter/alova-mock)的响应数据是一个`Response`实例,即默认兼容`GlobalFetch`请求适配器,当使用 axios 适配器时,我们需要让模拟请求适配器的响应数据是**AxiosResponse**兼容的,错误实例是**AxiosError**,因此你需要使用**@alova/adapter-axios**包中导出的`axiosMockResponse`作为响应适配器。 + +```javascript +import { defineMock, createAlovaMockAdapter } from '@alova/mock'; +import { axiosRequestAdapter, axiosMockResponse } from '@alova/adapter-axios'; + +const mocks = defineMock({ + // ... +}); + +// 模拟数据请求适配器 +export default createAlovaMockAdapter([mocks], { + // 指定axios请求适配器后,未匹配模拟接口的请求将使用这个适配器发送请求 + httpAdapter: axiosRequestAdapter(), + + // axiosMockResponse中包含了onMockResponse和onMockError + // 用于将模拟数据转换为AxiosResponse和AxiosError兼容的格式 + ...axiosMockResponse +}); + +export const alovaInst = createAlova({ + // ... + // 通过环境变量控制是否使用模拟请求适配器 + requestAdapter: process.env.NODE_ENV === 'development' ? mockAdapter : axiosRequestAdapter() +}); +``` + +## Typescript + +axios 请求适配器 提供了完整的类型适配,method 配置、响应数据的类型将与 axios 的类型完全匹配。 + +### method 配置 + +在创建 method 实例时,除了 method 中通用的配置项外,你还可以使用`AxiosRequestConfig`中的配置项,我们已经在类型中去除了和 method 实例通用配置冲突的项。 + +```typescript +/** + * axios请求配置参数 + * 去掉了与method冲突的属性 + */ +export type AlovaAxiosRequestConfig = Omit< + AxiosRequestConfig, + | 'url' + | 'method' + | 'baseURL' + | 'headers' + | 'params' + | 'data' + | 'timeout' + | 'cancelToken' + | 'signal' + | 'onUploadProgress' + | 'onDownloadProgress' +>; +``` + +### 响应数据 + +axios 的响应数据类型是`AxiosResponse`,当你使用 axios 适配器时,也将获得相同格式的响应数据。在实际使用中,我们通常需要在全局处理响应数据,一个简单的实例如下: + +```typescript +const alovaInst = createAlova( + baseURL: 'https://api.alovajs.org', + requestAdapter: axiosRequestAdapter(), + responded(response) { + // response自动被推断为AxiosResponse类型 + return response.data; + } +}); +``` + +### 错误 + +当 axios 遇到非 20x 和 30x 的响应状态码时将会抛出错误,为了包含更多信息,axios 将错误实例自定义设计成了一个`AxiosError`实例,而不是普通的 Error 实例,因此当遇到服务端错误或网络错误时都将抛出一个错误,你可以在全局的错误回调中捕获它。 + +```typescript +const alovaInst = createAlova( + baseURL: 'https://api.alovajs.org', + requestAdapter: axiosRequestAdapter(), + responded: { + onSuccess(response) { + // response自动被推断为AxiosResponse类型 + return response.data; + }, + onError(err: AxiosError) { + // err默认为any,你可以强制转换为AxiosError处理 + // ... + } + } +}); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/04-alova-adapter-taro.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/04-alova-adapter-taro.md index 23d98ed45..c38de5fb6 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/04-alova-adapter-taro.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/04-alova-adapter-taro.md @@ -1,436 +1,443 @@ ---- -title: Taro适配器 -sidebar_position: 40 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -:::info 提示 - -此插件只支持 react 16.8+、vue3 版本的 taro 应用。 - -::: - -## 安装 - - - - -```bash -npm install @alova/adapter-taro --save -``` - - - - -```bash -yarn add @alova/adapter-taro -``` - - - - -:::warning React-Native 应用 - -如果你正在使用 Taro 开发 React-Native 应用,请确保`metro >= 0.76.0`,并在`metro.config.js`中开启`resolver.unstable_enablePackageExports` - -[关于 metro 的 unstable_enablePackageExports 参数](https://facebook.github.io/metro/docs/configuration/#unstable_enablepackageexports-experimental) - -::: - -:::warning 依赖预编译问题 - -在 Taro v3.5 beta 中新增了[依赖预编译功能](https://docs.taro.zone/blog/2022/05/19/Taro-3.5-beta#2-%E4%BE%9D%E8%B5%96%E9%A2%84%E7%BC%96%E8%AF%91)并在开发模式下默认开启,当你正在 Taro 中同时使用`alova`库和`@alova/scene-react(vue)`时可能导致报 `` [alova]can not call useHooks until set the `statesHook` at alova instance.``的错误,这是由于 prebundle 功能重复打包了两份不同的`alova`包导致,此时关闭 prebundle 功能即可解决此问题。 - -```js -// config/dev.ts -export default { - // ... - compiler: { - type: 'webpack5', - prebundle: { - // 关闭prebundle - enable: false - } - } -} satisfies UserConfigExport - -``` - -感谢[LBinin 的 issue](https://github.com/alovajs/scene/issues/63)。 - -此问题已向 Taro 团队提交[issue](https://github.com/NervJS/taro/issues/15728),期待解决此问题。 - -::: - -## 使用方法 - -### 创建 alova - -调用 **AdapterTaro** 将返回*请求适配器*、_存储适配器_,以及*ReactHook*,因此你不再需要设置这三个项,使用方法完全一致。 - - - - -```javascript -import { createAlova } from 'alova'; -import AdapterTaro from '@alova/adapter-taro'; - -const alovaInst = createAlova({ - baseURL: 'https://api.alovajs.org', - ...AdapterTaro() -}); -``` - - - - - -```javascript -import { createAlova } from 'alova'; -import AdapterTaroVue from '@alova/adapter-taro/vue'; - -const alovaInst = createAlova({ - baseURL: 'https://api.alovajs.org', - ...AdapterTaroVue() -}); -``` - - - - -### 请求 - -请求的使用方法与 web 环境中使用完全一致。已经完全兼容`Taro.request`,你可以在创建 method 实例的*config*中指定`Taro.request`支持的[全部配置项](https://taro-docs.jd.com/docs/apis/network/request/) - - - - -```jsx -const list = () => - alovaInst.Get('/list', { - // 设置的参数将传递给Taro.request - enableHttp2: true - }); - -const App = () => { - const { loading, data } = useRequest(list); - - return ( - { loading ? 加载中... : null } - 请求数据为:{ JSON.stringify(data) } - ) -}; -``` - - - - -```html - - 加载中... - 请求数据为:{{ data }} - - - -``` - - - - -### 上传 - -在 method 实例的*config*中设置`requestType: 'upload'`时表示上传文件,请求适配器将会调用`Taro.uploadFile`,上传的文件数据放在 method 实例的 data 中,你需要在 data 中指定`name`和`filePath`,这 2 个参数将传到`Taro.uploadFile`中,同时,你还可以在 data 中指定这 2 个参数外的其他参数,请求适配器会将它们传入到`formData`参数中。 - -同样的,已经完全兼容`Taro.uploadFile`,你可以在创建 method 实例的*config*中指定`Taro.uploadFile`支持的[全部配置项](https://taro-docs.jd.com/docs/apis/network/upload/uploadFile),如果还有额外的参数需要设置,请在 method 实例的*config*中指定。 - - - - -```jsx -const uploadFile = (name, filePath, formData) => - alovaInst.Post( - '/uploadImg', - { - name, - filePath, - - // 额外数据将传入uni.uploadFile的formData中 - ...formData - }, - { - // 设置请求方式为上传,适配器内将调用uni.uploadFile - requestType: 'upload', - - // 开启上传进度 - enableUpload: true - } - ); - -const App = () => { - const { loading, data, uploading, send } = useRequest(uploadFile, { - immediate: false - }); - - const handleImageChoose = () => { - Taro.chooseImage({ - success: chooseImageRes => { - const tempFilePaths = chooseImageRes.tempFilePaths; - send('fileName', tempFilePaths[0], { - extra1: 'a', - extra2: 'b' - }); - } - }); - }; - - return ( - { loading ? 上传中... : null } - 上传进度:{ uploading.loaded }/{ uploading.total } - - {/* ... */} - ) -} -``` - - - - -```html - - 上传中... - 上传进度:{{ uploading.loaded }}/{{ uploading.total }} - - - - - -``` - - - - -### 下载 - -在 method 实例的*config*中设置`requestType: 'download'`时表示下载文件,请求适配器将会调用`Taro.downloadFile`。 - -同样的,已经完全兼容`Taro.downloadFile`,你可以在创建 method 实例的*config*中指定`Taro.downloadFile`支持的[全部配置项](https://taro-docs.jd.com/docs/apis/network/download/downloadFile),如果还有额外的参数需要设置,请在 method 实例的*config*中指定。 - - - - -```jsx -const downloadFile = filePath => - alovaInst.Get('/bigImage.jpg', { - // 设置请求方式为下载,适配器内将调用uni.downloadFile - requestType: 'download', - filePath, - - // 开启下载进度 - enableDownload: true - }); - -const App = () => { - const { loading, data, downloading, send } = useRequest(downloadFile, { - immediate: false - }); - const handleImageDownload = () => { - send('file_save_path'); - }; - - return ( - { loading ? 下载中... : null } - 下载进度:{ downloading.loaded }/{ downloading.total } - - {/* ... */} - ); -} -``` - - - - -```html - - 下载中... - 下载进度:{{ downloading.loaded }}/{{ downloading.total }} - - - - - -``` - - - - -## 模拟请求适配器兼容 - -在使用 Taro 开发应用时,我们仍然可能需要用到模拟请求,只是默认情况下,[模拟请求适配器(@alova/mock)](/tutorial/request-adapter/alova-mock)的响应数据是一个`Response`实例,即默认兼容`GlobalFetch`请求适配器,当在 Taro 环境下使用时,我们需要让模拟请求适配器的响应数据是兼容 Taro 适配器的,因此你需要使用**@alova/adapter-taro**包中导出的`taroMockResponse`作为响应适配器。 - -```javascript -import { defineMock, createAlovaMockAdapter } from '@alova/mock'; -import AdapterTaro, { taroRequestAdapter, taroMockResponse } from '@alova/adapter-taro'; - -const mocks = defineMock({ - // ... -}); - -// 模拟数据请求适配器 -export default createAlovaMockAdapter([mocks], { - // 指定taro请求适配器后,未匹配模拟接口的请求将使用这个适配器发送请求 - httpAdapter: taroRequestAdapter, - - // 模拟响应适配器,指定后响应数据将转换为taro兼容的数据格式 - onMockResponse: taroMockResponse -}); - -export const alovaInst = createAlova({ - baseURL: 'https://api.alovajs.org', - timeout: 5000, - ...AdapterTaro({ - // 通过环境变量控制是否使用模拟请求适配器 - mockRequest: process.env.NODE_ENV === 'development' ? mockAdapter : undefined - }) - // ... -}); -``` - -## Typescript - -taro 请求适配器 提供了完整的类型适配,method 配置、响应数据的类型将与 taro 的类型完全匹配。 - -### method 配置 - -在创建 method 实例时,除了 method 中通用的配置项外,你还可以使用`Taro.request`、`Taro.uploadFile`和`Taro.downloadFile`中的配置项,我们已经在类型中去除了和 method 实例通用配置冲突的项。 - -```typescript -/** - * Taro.request请求额外参数 - */ -export type TaroRequestConfig = Omit< - Taro.request.Option, - 'url' | 'data' | 'header' | 'method' | 'timeout' | 'success' | 'fail' | 'complete' ->; - -/** - * Taro.uploadFile额外参数 - */ -export type TaroUploadConfig = Omit< - Taro.uploadFile.Option, - 'url' | 'filePath' | 'name' | 'header' | 'formData' | 'timeout' | 'success' | 'fail' | 'complete' ->; - -/** - * Taro.downloadFile额外参数 - */ -export type TaroDownloadConfig = Omit< - Taro.downloadFile.Option, - 'url' | 'header' | 'timeout' | 'success' | 'fail' | 'complete' ->; - -/** - * 合并的请求配置参数 - */ -export type TaroConfig = { - /** - * 请求类型,upload表示上传,download表示下载,不填写表示正常请求 - */ - requestType?: 'upload' | 'download'; -} & TaroRequestConfig & - TaroUploadConfig & - TaroDownloadConfig; -``` - -### 响应数据 - -因为 taro 请求适配器同时兼容了`Taro.request`、`Taro.uploadFile`和`Taro.downloadFile`,但它们的响应值类型稍有不同,所以响应数据类型是这样的: - -```typescript -type TaroResponse = - // Taro.request的响应类型 - | Taro.request.SuccessCallbackResult - - // Taro.uploadFile的响应类型 - | Taro.uploadFile.SuccessCallbackResult - - // Taro.downloadFile的响应类型 - | Taro.downloadFile.FileSuccessCallbackResult; -``` - -在实际使用中,我们通常需要在全局处理响应数据,建议分开场景判断返回数据,一个简单的实例如下: - -```typescript -const alovaInst = createAlova( - baseURL: 'https://api.alovajs.org', - ...AdapterTaro(), - responded(response) { - const { statusCode, data } = response as Taro.request.SuccessCallbackResult; - if (statusCode >= 400) { - throw new Error('请求错误'); - } - return data || null; - } -}); -``` +--- +title: Taro适配器 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info 提示 + +此插件只支持 react 16.8+、vue3 版本的 taro 应用。 + +::: + +## 安装 + + + + +```bash +npm install @alova/adapter-taro --save +``` + + + + +```bash +yarn add @alova/adapter-taro +``` + + + + +:::warning React-Native 应用 + +如果你正在使用 Taro 开发 React-Native 应用,请确保`metro >= 0.76.0`,并在`metro.config.js`中开启`resolver.unstable_enablePackageExports` + +[关于 metro 的 unstable_enablePackageExports 参数](https://facebook.github.io/metro/docs/configuration/#unstable_enablepackageexports-experimental) + +::: + +:::warning 依赖预编译问题 + +在 Taro v3.5 beta 中新增了[依赖预编译功能](https://docs.taro.zone/blog/2022/05/19/Taro-3.5-beta#2-%E4%BE%9D%E8%B5%96%E9%A2%84%E7%BC%96%E8%AF%91)并在开发模式下默认开启,当你正在 Taro 中同时使用`alova`库和`@alova/scene-react(vue)`时可能导致报 `` [alova]can not call useHooks until set the `statesHook` at alova instance.``的错误,这是由于 prebundle 功能重复打包了两份不同的`alova`包导致,此时关闭 prebundle 功能即可解决此问题。 + +```js +// config/dev.ts +export default { + // ... + compiler: { + type: 'webpack5', + prebundle: { + // 关闭prebundle + enable: false + } + } +} satisfies UserConfigExport + +``` + +感谢[LBinin 的 issue](https://github.com/alovajs/scene/issues/63)。 + +此问题已向 Taro 团队提交[issue](https://github.com/NervJS/taro/issues/15728),期待解决此问题。 + +::: + +## 使用方法 + +### 创建 alova + +调用 **AdapterTaro** 将返回*请求适配器*、_存储适配器_,以及*ReactHook*,因此你不再需要设置这三个项,使用方法完全一致。 + + + + +```javascript +import { createAlova } from 'alova'; +import AdapterTaro from '@alova/adapter-taro'; + +const alovaInst = createAlova({ + baseURL: 'https://api.alovajs.org', + ...AdapterTaro() +}); +``` + + + + + +```javascript +import { createAlova } from 'alova'; +import AdapterTaroVue from '@alova/adapter-taro/vue'; + +const alovaInst = createAlova({ + baseURL: 'https://api.alovajs.org', + ...AdapterTaroVue() +}); +``` + + + + +### 请求 + +请求的使用方法与 web 环境中使用完全一致。已经完全兼容`Taro.request`,你可以在创建 method 实例的*config*中指定`Taro.request`支持的[全部配置项](https://taro-docs.jd.com/docs/apis/network/request/) + + + + +```jsx +const list = () => + alovaInst.Get('/list', { + // 设置的参数将传递给Taro.request + enableHttp2: true + }); + +const App = () => { + const { loading, data } = useRequest(list); + + return ( + { loading ? 加载中... : null } + 请求数据为:{ JSON.stringify(data) } + ) +}; +``` + + + + +```html + + 加载中... + 请求数据为:{{ data }} + + + +``` + + + + +### 上传 + +在 method 实例的*config*中设置`requestType: 'upload'`时表示上传文件,请求适配器将会调用`Taro.uploadFile`,上传的文件数据放在 method 实例的 data 中,你需要在 data 中指定`name`和`filePath`,这 2 个参数将传到`Taro.uploadFile`中,同时,你还可以在 data 中指定这 2 个参数外的其他参数,请求适配器会将它们传入到`formData`参数中。 + +同样的,已经完全兼容`Taro.uploadFile`,你可以在创建 method 实例的*config*中指定`Taro.uploadFile`支持的[全部配置项](https://taro-docs.jd.com/docs/apis/network/upload/uploadFile),如果还有额外的参数需要设置,请在 method 实例的*config*中指定。 + + + + +```jsx +const uploadFile = (name, filePath, formData) => + alovaInst.Post( + '/uploadImg', + { + name, + filePath, + + // 额外数据将传入uni.uploadFile的formData中 + ...formData + }, + { + // 设置请求方式为上传,适配器内将调用uni.uploadFile + requestType: 'upload', + + // 开启上传进度 + enableUpload: true + } + ); + +const App = () => { + const { loading, data, uploading, send } = useRequest(uploadFile, { + immediate: false + }); + + const handleImageChoose = () => { + Taro.chooseImage({ + success: chooseImageRes => { + const tempFilePaths = chooseImageRes.tempFilePaths; + send('fileName', tempFilePaths[0], { + extra1: 'a', + extra2: 'b' + }); + } + }); + }; + + return ( + { loading ? 上传中... : null } + 上传进度:{ uploading.loaded }/{ uploading.total } + + {/* ... */} + ) +} +``` + + + + +```html + + 上传中... + 上传进度:{{ uploading.loaded }}/{{ uploading.total }} + + + + + +``` + + + + +### 下载 + +在 method 实例的*config*中设置`requestType: 'download'`时表示下载文件,请求适配器将会调用`Taro.downloadFile`。 + +同样的,已经完全兼容`Taro.downloadFile`,你可以在创建 method 实例的*config*中指定`Taro.downloadFile`支持的[全部配置项](https://taro-docs.jd.com/docs/apis/network/download/downloadFile),如果还有额外的参数需要设置,请在 method 实例的*config*中指定。 + + + + +```jsx +const downloadFile = filePath => + alovaInst.Get('/bigImage.jpg', { + // 设置请求方式为下载,适配器内将调用uni.downloadFile + requestType: 'download', + filePath, + + // 开启下载进度 + enableDownload: true + }); + +const App = () => { + const { loading, data, downloading, send } = useRequest(downloadFile, { + immediate: false + }); + const handleImageDownload = () => { + send('file_save_path'); + }; + + return ( + { loading ? 下载中... : null } + 下载进度:{ downloading.loaded }/{ downloading.total } + + {/* ... */} + ); +} +``` + + + + +```html + + 下载中... + 下载进度:{{ downloading.loaded }}/{{ downloading.total }} + + + + + +``` + + + + +## 模拟请求适配器兼容 + +在使用 Taro 开发应用时,我们仍然可能需要用到模拟请求,只是默认情况下,[模拟请求适配器(@alova/mock)](/tutorial/request-adapter/alova-mock)的响应数据是一个`Response`实例,即默认兼容`GlobalFetch`请求适配器,当在 Taro 环境下使用时,我们需要让模拟请求适配器的响应数据是兼容 Taro 适配器的,因此你需要使用**@alova/adapter-taro**包中导出的`taroMockResponse`作为响应适配器。 + +```javascript +import { defineMock, createAlovaMockAdapter } from '@alova/mock'; +import AdapterTaro, { taroRequestAdapter, taroMockResponse } from '@alova/adapter-taro'; + +const mocks = defineMock({ + // ... +}); + +// 模拟数据请求适配器 +export default createAlovaMockAdapter([mocks], { + // 指定taro请求适配器后,未匹配模拟接口的请求将使用这个适配器发送请求 + httpAdapter: taroRequestAdapter, + + // 模拟响应适配器,指定后响应数据将转换为taro兼容的数据格式 + onMockResponse: taroMockResponse +}); + +export const alovaInst = createAlova({ + baseURL: 'https://api.alovajs.org', + timeout: 5000, + ...AdapterTaro({ + // 通过环境变量控制是否使用模拟请求适配器 + mockRequest: process.env.NODE_ENV === 'development' ? mockAdapter : undefined + }) + // ... +}); +``` + +## Typescript + +taro 请求适配器 提供了完整的类型适配,method 配置、响应数据的类型将与 taro 的类型完全匹配。 + +### method 配置 + +在创建 method 实例时,除了 method 中通用的配置项外,你还可以使用`Taro.request`、`Taro.uploadFile`和`Taro.downloadFile`中的配置项,我们已经在类型中去除了和 method 实例通用配置冲突的项。 + +```typescript +/** + * Taro.request请求额外参数 + */ +export type TaroRequestConfig = Omit< + Taro.request.Option, + 'url' | 'data' | 'header' | 'method' | 'timeout' | 'success' | 'fail' | 'complete' +>; + +/** + * Taro.uploadFile额外参数 + */ +export type TaroUploadConfig = Omit< + Taro.uploadFile.Option, + | 'url' + | 'filePath' + | 'name' + | 'header' + | 'formData' + | 'timeout' + | 'success' + | 'fail' + | 'complete' +>; + +/** + * Taro.downloadFile额外参数 + */ +export type TaroDownloadConfig = Omit< + Taro.downloadFile.Option, + 'url' | 'header' | 'timeout' | 'success' | 'fail' | 'complete' +>; + +/** + * 合并的请求配置参数 + */ +export type TaroConfig = { + /** + * 请求类型,upload表示上传,download表示下载,不填写表示正常请求 + */ + requestType?: 'upload' | 'download'; +} & TaroRequestConfig & + TaroUploadConfig & + TaroDownloadConfig; +``` + +### 响应数据 + +因为 taro 请求适配器同时兼容了`Taro.request`、`Taro.uploadFile`和`Taro.downloadFile`,但它们的响应值类型稍有不同,所以响应数据类型是这样的: + +```typescript +type TaroResponse = + // Taro.request的响应类型 + | Taro.request.SuccessCallbackResult + + // Taro.uploadFile的响应类型 + | Taro.uploadFile.SuccessCallbackResult + + // Taro.downloadFile的响应类型 + | Taro.downloadFile.FileSuccessCallbackResult; +``` + +在实际使用中,我们通常需要在全局处理响应数据,建议分开场景判断返回数据,一个简单的实例如下: + +```typescript +const alovaInst = createAlova( + baseURL: 'https://api.alovajs.org', + ...AdapterTaro(), + responded(response) { + const { statusCode, data } = response as Taro.request.SuccessCallbackResult; + if (statusCode >= 400) { + throw new Error('请求错误'); + } + return data || null; + } +}); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/05-alova-adapter-uniapp.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/05-alova-adapter-uniapp.md index 18ea8079f..63ff1057c 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/05-alova-adapter-uniapp.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/05-alova-adapter-uniapp.md @@ -1,279 +1,278 @@ ---- -title: Uniapp适配器 -sidebar_position: 50 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -:::info 提示 - -此插件只支持 vue3 版本的 uniapp 应用。 - -::: - -## 安装 - - - - -```bash -npm install @alova/adapter-uniapp --save -``` - - - - -```bash -yarn add @alova/adapter-uniapp -``` - - - - -## 使用方法 - -### 创建 alova - -调用 **AdapterUniapp** 将返回*请求适配器*、_存储适配器_,以及*VueHook*,因此你不再需要设置这三个项,使用方法完全一致。 - -```javascript -import { createAlova } from 'alova'; -import AdapterUniapp from '@alova/adapter-uniapp'; - -const alovaInst = createAlova({ - baseURL: 'https://api.alovajs.org', - ...AdapterUniapp() -}); -``` - -### 请求 - -请求的使用方法与 web 环境中使用完全一致。已经完全兼容`uni.request`,你可以在创建 method 实例的*config*中指定`uni.request`支持的[全部配置项](https://uniapp.dcloud.net.cn/api/request/request.html) - -```html - - 加载中... - 请求数据为:{{ data }} - - - -``` - -当使用`useRequest/useWatcher`立即发送请求时,它将会在`onLoad`钩子中异步执行,因此你可以在`methodHandler`中访问 options 数据,访问方式如下: - -```javascript -import { onLoad } from '@dcloudio/uni-app'; - -let options = {}; -onLoad(opt => { - options = opt; -}); -const { loading, data } = useRequest(() => getDetail(options.id)); -``` - -### 上传 - -在 method 实例的*config*中设置`requestType: 'upload'`时表示上传文件,请求适配器将会调用`uni.uploadFile`,上传的文件数据放在 method 实例的 data 中,你需要在 data 中指定`name`、`filePath或files`,以及`file`(如果需要),这 4 个参数将传到`uni.uploadFile`中,同时,你还可以在 data 中指定这 4 个参数外的其他参数,请求适配器会将它们传入到`formData`参数中。 - -同样的,已经完全兼容`uni.uploadFile`,你可以在创建 method 实例的*config*中指定`uni.uploadFile`支持的[全部配置项](https://uniapp.dcloud.net.cn/api/request/network-file.html#uploadfile),如果还有额外的参数需要设置,请在 method 实例的*config*中指定。 - -```html - - 上传中... - 上传进度:{{ uploading.loaded }}/{{ uploading.total }} - - - - - -``` - -### 下载 - -在 method 实例的*config*中设置`requestType: 'download'`时表示下载文件,请求适配器将会调用`uni.downloadFile`。 - -同样的,已经完全兼容`uni.downloadFile`,你可以在创建 method 实例的*config*中指定`uni.downloadFile`支持的[全部配置项](https://uniapp.dcloud.net.cn/api/request/network-file.html#downloadfile),如果还有额外的参数需要设置,请在 method 实例的*config*中指定。 - -```html - - 下载中... - 下载进度:{{ downloading.loaded }}/{{ downloading.total }} - - - - - -``` - -## 模拟请求适配器兼容 - -在使用 uniapp 开发应用时,我们仍然可能需要用到模拟请求,只是默认情况下,[模拟请求适配器(@alova/mock)](/tutorial/request-adapter/alova-mock)的响应数据是一个`Response`实例,即默认兼容`GlobalFetch`请求适配器,当在 uniapp 环境下使用时,我们需要让模拟请求适配器的响应数据是兼容 uniapp 适配器的,因此你需要使用**@alova/adapter-uniapp**包中导出的`uniappMockResponse`作为响应适配器。 - -```javascript -import { defineMock, createAlovaMockAdapter } from '@alova/mock'; -import AdapterUniapp, { uniappRequestAdapter, uniappMockResponse } from '@alova/adapter-uniapp'; - -const mocks = defineMock({ - // ... -}); - -// 模拟数据请求适配器 -export default createAlovaMockAdapter([mocks], { - // 指定uniapp请求适配器后,未匹配模拟接口的请求将使用这个适配器发送请求 - httpAdapter: uniappRequestAdapter, - - // 模拟响应适配器,指定后响应数据将转换为uniapp兼容的数据格式 - onMockResponse: uniappMockResponse -}); - -export const alovaInst = createAlova({ - baseURL: 'https://api.alovajs.org', - timeout: 5000, - ...AdapterUniapp({ - // 通过环境变量控制是否使用模拟请求适配器 - mockRequest: process.env.NODE_ENV === 'development' ? mockAdapter : undefined - }) - // ... -}); -``` - -## Typescript - -uniapp 请求适配器提供了完整的类型适配,method 配置、响应数据的类型将与 uniapp 的类型完全匹配。 - -### method 配置 - -在创建 method 实例时,除了 method 中通用的配置项外,你还可以使用`uniapp.request`、`uniapp.uploadFile`和`uniapp.downloadFile`中的配置项,我们已经在类型中去除了和 method 实例通用配置冲突的项。 - -```typescript -/** - * uni.request请求额外参数 - */ -export type UniappRequestConfig = Omit< - UniNamespace.RequestOptions, - 'url' | 'data' | 'header' | 'method' | 'timeout' | 'success' | 'fail' | 'complete' ->; - -/** - * uni.uploadFile额外参数 - */ -export type UniappUploadConfig = Omit< - UniNamespace.UploadFileOption, - 'url' | 'name' | 'header' | 'formData' | 'timeout' | 'success' | 'fail' | 'complete' ->; - -/** - * uni.downloadFile额外参数 - */ -export type UniappDownloadConfig = Omit< - UniNamespace.DownloadFileOption, - 'url' | 'header' | 'timeout' | 'success' | 'fail' | 'complete' ->; - -/** - * 合并的请求配置参数 - */ -export type UniappConfig = { - /** - * 请求类型,upload表示上传,download表示下载,不填写表示正常请求 - */ - requestType?: 'upload' | 'download'; -} & UniappRequestConfig & - UniappUploadConfig & - UniappDownloadConfig; -``` - -### 响应数据 - -因为 uniapp 请求适配器同时兼容了`uniapp.request`、`uniapp.uploadFile`和`uniapp.downloadFile`,但它们的响应值类型稍有不同,所以响应数据类型是这样的: - -```typescript -type UniappResponse = - // uni.request的响应类型 - | UniNamespace.RequestSuccessCallbackResult - - // uni.uploadFile的响应类型 - | UniNamespace.UploadFileSuccessCallbackResult - - // uni.downloadFile的响应类型 - | UniNamespace.DownloadSuccessData; -``` - -在实际使用中,我们通常需要在全局处理响应数据,建议分开场景判断返回数据,一个简单的实例如下: - -```typescript -const alovaInst = createAlova( - baseURL: 'https://api.alovajs.org', - ...AdapterUniapp(), - responded(response) { - const { statusCode, data } = response as UniNamespace.RequestSuccessCallbackResult; - if (statusCode >= 400) { - throw new Error('请求错误'); - } - return data || null; - } -}); -``` +--- +title: Uniapp适配器 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info 提示 + +此插件只支持 vue3 版本的 uniapp 应用。 + +::: + +## 安装 + + + + +```bash +npm install @alova/adapter-uniapp --save +``` + + + + +```bash +yarn add @alova/adapter-uniapp +``` + + + + +## 使用方法 + +### 创建 alova + +调用 **AdapterUniapp** 将返回*请求适配器*、_存储适配器_,以及*VueHook*,因此你不再需要设置这三个项,使用方法完全一致。 + +```javascript +import { createAlova } from 'alova'; +import AdapterUniapp from '@alova/adapter-uniapp'; + +const alovaInst = createAlova({ + baseURL: 'https://api.alovajs.org', + ...AdapterUniapp() +}); +``` + +### 请求 + +请求的使用方法与 web 环境中使用完全一致。已经完全兼容`uni.request`,你可以在创建 method 实例的*config*中指定`uni.request`支持的[全部配置项](https://uniapp.dcloud.net.cn/api/request/request.html) + +```html + + 加载中... + 请求数据为:{{ data }} + + + +``` + +当使用`useRequest/useWatcher`立即发送请求时,它将会在`onLoad`钩子中异步执行,因此你可以在`methodHandler`中访问 options 数据,访问方式如下: + +```javascript +import { onLoad } from '@dcloudio/uni-app'; + +let options = {}; +onLoad(opt => { + options = opt; +}); +const { loading, data } = useRequest(() => getDetail(options.id)); +``` + +### 上传 + +在 method 实例的*config*中设置`requestType: 'upload'`时表示上传文件,请求适配器将会调用`uni.uploadFile`,上传的文件数据放在 method 实例的 data 中,你需要在 data 中指定`name`、`filePath或files`,以及`file`(如果需要),这 4 个参数将传到`uni.uploadFile`中,同时,你还可以在 data 中指定这 4 个参数外的其他参数,请求适配器会将它们传入到`formData`参数中。 + +同样的,已经完全兼容`uni.uploadFile`,你可以在创建 method 实例的*config*中指定`uni.uploadFile`支持的[全部配置项](https://uniapp.dcloud.net.cn/api/request/network-file.html#uploadfile),如果还有额外的参数需要设置,请在 method 实例的*config*中指定。 + +```html + + 上传中... + 上传进度:{{ uploading.loaded }}/{{ uploading.total }} + + + + + +``` + +### 下载 + +在 method 实例的*config*中设置`requestType: 'download'`时表示下载文件,请求适配器将会调用`uni.downloadFile`。 + +同样的,已经完全兼容`uni.downloadFile`,你可以在创建 method 实例的*config*中指定`uni.downloadFile`支持的[全部配置项](https://uniapp.dcloud.net.cn/api/request/network-file.html#downloadfile),如果还有额外的参数需要设置,请在 method 实例的*config*中指定。 + +```html + + 下载中... + 下载进度:{{ downloading.loaded }}/{{ downloading.total }} + + + + + +``` + +## 模拟请求适配器兼容 + +在使用 uniapp 开发应用时,我们仍然可能需要用到模拟请求,只是默认情况下,[模拟请求适配器(@alova/mock)](/tutorial/request-adapter/alova-mock)的响应数据是一个`Response`实例,即默认兼容`GlobalFetch`请求适配器,当在 uniapp 环境下使用时,我们需要让模拟请求适配器的响应数据是兼容 uniapp 适配器的,因此你需要使用**@alova/adapter-uniapp**包中导出的`uniappMockResponse`作为响应适配器。 + +```javascript +import { defineMock, createAlovaMockAdapter } from '@alova/mock'; +import AdapterUniapp, { uniappRequestAdapter, uniappMockResponse } from '@alova/adapter-uniapp'; + +const mocks = defineMock({ + // ... +}); + +// 模拟数据请求适配器 +export default createAlovaMockAdapter([mocks], { + // 指定uniapp请求适配器后,未匹配模拟接口的请求将使用这个适配器发送请求 + httpAdapter: uniappRequestAdapter, + + // 模拟响应适配器,指定后响应数据将转换为uniapp兼容的数据格式 + onMockResponse: uniappMockResponse +}); + +export const alovaInst = createAlova({ + baseURL: 'https://api.alovajs.org', + timeout: 5000, + ...AdapterUniapp({ + // 通过环境变量控制是否使用模拟请求适配器 + mockRequest: process.env.NODE_ENV === 'development' ? mockAdapter : undefined + }) + // ... +}); +``` + +## Typescript + +uniapp 请求适配器提供了完整的类型适配,method 配置、响应数据的类型将与 uniapp 的类型完全匹配。 + +### method 配置 + +在创建 method 实例时,除了 method 中通用的配置项外,你还可以使用`uniapp.request`、`uniapp.uploadFile`和`uniapp.downloadFile`中的配置项,我们已经在类型中去除了和 method 实例通用配置冲突的项。 + +```typescript +/** + * uni.request请求额外参数 + */ +export type UniappRequestConfig = Omit< + UniNamespace.RequestOptions, + 'url' | 'data' | 'header' | 'method' | 'timeout' | 'success' | 'fail' | 'complete' +>; + +/** + * uni.uploadFile额外参数 + */ +export type UniappUploadConfig = Omit< + UniNamespace.UploadFileOption, + 'url' | 'name' | 'header' | 'formData' | 'timeout' | 'success' | 'fail' | 'complete' +>; + +/** + * uni.downloadFile额外参数 + */ +export type UniappDownloadConfig = Omit< + UniNamespace.DownloadFileOption, + 'url' | 'header' | 'timeout' | 'success' | 'fail' | 'complete' +>; + +/** + * 合并的请求配置参数 + */ +export type UniappConfig = { + /** + * 请求类型,upload表示上传,download表示下载,不填写表示正常请求 + */ + requestType?: 'upload' | 'download'; +} & UniappRequestConfig & + UniappUploadConfig & + UniappDownloadConfig; +``` + +### 响应数据 + +因为 uniapp 请求适配器同时兼容了`uniapp.request`、`uniapp.uploadFile`和`uniapp.downloadFile`,但它们的响应值类型稍有不同,所以响应数据类型是这样的: + +```typescript +type UniappResponse = + // uni.request的响应类型 + | UniNamespace.RequestSuccessCallbackResult + + // uni.uploadFile的响应类型 + | UniNamespace.UploadFileSuccessCallbackResult + + // uni.downloadFile的响应类型 + | UniNamespace.DownloadSuccessData; +``` + +在实际使用中,我们通常需要在全局处理响应数据,建议分开场景判断返回数据,一个简单的实例如下: + +```typescript +const alovaInst = createAlova( + baseURL: 'https://api.alovajs.org', + ...AdapterUniapp(), + responded(response) { + const { statusCode, data } = response as UniNamespace.RequestSuccessCallbackResult; + if (statusCode >= 400) { + throw new Error('请求错误'); + } + return data || null; + } +}); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/01-vue-options.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/01-vue-options.md index 16007b3da..ae25294dc 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/01-vue-options.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/01-vue-options.md @@ -1,6 +1,5 @@ --- title: vue2/3 options -sidebar_position: 10 --- import Tabs from '@theme/Tabs'; diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/02-solid.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/02-solid.md index f204dd737..bb13ca954 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/02-solid.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/02-solid.md @@ -1,6 +1,5 @@ --- title: solid -sidebar_position: 20 --- 敬请期待... diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/03-angular.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/03-angular.md index 49a755cb1..9897e4695 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/03-angular.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/03-angular.md @@ -1,6 +1,5 @@ --- title: angular -sidebar_position: 30 --- 敬请期待... diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/04-native-mp.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/04-native-mp.md index b04786d56..92ac7538e 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/04-native-mp.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/04-native-mp.md @@ -1,6 +1,5 @@ --- title: 原生小程序 -sidebar_position: 40 --- 敬请期待... diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/05-preact.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/05-preact.md index 9fd875bd7..50451c180 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/05-preact.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/05-preact.md @@ -1,6 +1,5 @@ --- title: preact -sidebar_position: 50 --- 敬请期待... diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/06-qwik.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/06-qwik.md index 893e62274..f75e5460d 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/06-qwik.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/06-qwik.md @@ -1,6 +1,5 @@ --- title: qwik -sidebar_position: 60 --- 敬请期待... diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/07-lit.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/07-lit.md index 106cd17b5..4671412ea 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/07-lit.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/07-lit.md @@ -1,6 +1,5 @@ --- title: lit -sidebar_position: 70 --- 敬请期待... diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/08-stencil.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/08-stencil.md index 07a070572..acef20ed4 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/08-stencil.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/09-framework/08-stencil.md @@ -1,6 +1,5 @@ --- title: stencil -sidebar_position: 80 --- 敬请期待... diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/10-custom/01-overview.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/10-custom/01-overview.md index 4b9513613..f185a8a81 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/10-custom/01-overview.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/10-custom/01-overview.md @@ -1,26 +1,25 @@ ---- -title: 概览 -sidebar_position: 10 ---- - -alova 具有很高的扩展性,它除了提供核心的缓存机制、请求共享机制以及状态管理等通用特性外,还提供了各类定制功能以及中间件机制,可以适配不同 js 环境,以及自定义请求策略。 - -## 适配器 - -为了满足 js 在不同环境下的运行需求,你可以自定义请求适配器、存储适配器,甚至是 UI 框架的状态适配器,在接下来的章节中也会详细介绍。以下列出了一些适配器示例。 - -- [fetch 适配器](https://github.com/alovajs/alova/blob/main/src/predefine/GlobalFetch.ts) -- [localStorage 存储适配器](https://github.com/alovajs/alova/blob/main/src/predefine/globalLocalStorage.ts) -- [vue states hook](https://github.com/alovajs/alova/blob/main/src/predefine/VueHook.ts) - -你也可以将多个类型的适配器组成一个集合,例如[Uniapp 适配器](/tutorial/request-adapter/alova-adapter-uniapp)。 - -## 编写请求策略 - -alova 的请求策略和 alova 核心库是分开的,目的是为了开发者们也可以利用 alova 的高扩展性来编写自己的请求策略。通常情况下,一个自定义的请求策略是基于`useRequest`、`useWatcher` 和 `useFetcher` 三者的组合,以及为它们编写[中间件](/tutorial/advanced/middleware)、缓存的操纵函数来控制它们的请求方式,从而实现各种效果的请求策略。 - -**@alova/scene** 中的请求策略具有很好的代表性,强烈建议你参考源码寻找灵感。 - -- [分页请求策略源码](https://github.com/alovajs/scene/blob/main/src/hooks/pagination/usePagination.js) -- [验证码策略源码](https://github.com/alovajs/scene/blob/main/src/hooks/useCaptcha.ts) -- [表单提交策略源码](https://github.com/alovajs/scene/blob/main/src/hooks/useForm.ts) +--- +title: 概览 +--- + +alova 具有很高的扩展性,它除了提供核心的缓存机制、请求共享机制以及状态管理等通用特性外,还提供了各类定制功能以及中间件机制,可以适配不同 js 环境,以及自定义请求策略。 + +## 适配器 + +为了满足 js 在不同环境下的运行需求,你可以自定义请求适配器、存储适配器,甚至是 UI 框架的状态适配器,在接下来的章节中也会详细介绍。以下列出了一些适配器示例。 + +- [fetch 适配器](https://github.com/alovajs/alova/blob/main/src/predefine/GlobalFetch.ts) +- [localStorage 存储适配器](https://github.com/alovajs/alova/blob/main/src/predefine/globalLocalStorage.ts) +- [vue states hook](https://github.com/alovajs/alova/blob/main/src/predefine/VueHook.ts) + +你也可以将多个类型的适配器组成一个集合,例如[Uniapp 适配器](/tutorial/request-adapter/alova-adapter-uniapp)。 + +## 编写请求策略 + +alova 的请求策略和 alova 核心库是分开的,目的是为了开发者们也可以利用 alova 的高扩展性来编写自己的请求策略。通常情况下,一个自定义的请求策略是基于`useRequest`、`useWatcher` 和 `useFetcher` 三者的组合,以及为它们编写[中间件](/tutorial/advanced/middleware)、缓存的操纵函数来控制它们的请求方式,从而实现各种效果的请求策略。 + +**@alova/scene** 中的请求策略具有很好的代表性,强烈建议你参考源码寻找灵感。 + +- [分页请求策略源码](https://github.com/alovajs/scene/blob/main/src/hooks/pagination/usePagination.js) +- [验证码策略源码](https://github.com/alovajs/scene/blob/main/src/hooks/useCaptcha.ts) +- [表单提交策略源码](https://github.com/alovajs/scene/blob/main/src/hooks/useForm.ts) diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/10-custom/02-custom-http-adapter.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/10-custom/02-custom-http-adapter.md index 647b1a2e7..d1e52fbf2 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/10-custom/02-custom-http-adapter.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/10-custom/02-custom-http-adapter.md @@ -1,193 +1,192 @@ ---- -title: 自定义请求适配器 -sidebar_position: 20 ---- - -还记得如何创建一个 Alova 实例吗? - -```javascript -const alovaInstance = createAlova({ - // ... - requestAdapter: GlobalFetch() -}); -``` - -`requestAdapter`就是请求适配器,内部的请求发送和接收都将依赖请求适配器,`GlobalFetch`就是通过 fetch api 的方式来管理请求的,在大多数情况下我们可以使用它,但是,当`alova`运行在 fetch api 不可用的环境时(如 app、小程序),就需要更换一个支持当前环境的请求适配器。 - -那应该如何自定义一个请求适配器呢?很简单,它其实是一个函数,在每次发起请求时都会调用此函数,并返回一个对象,这个对象内包含如`url`、`method`、`data`、`headers`、`timeout`等请求相关的数据集合,虽然字段较多,但我们只需访问我们需要的数据即可。 - -## 请求适配器结构 - -请求适配器将接收到请求相关的参数,以及当前正在请求的 method 实例,并返回一个响应相关的函数集合。 - -```javascript -function CustomRequestAdapter(requestElements, methodInstance) { - // 发送请求... - return { - async response() { - // ...返回响应数据 - }, - async headers() { - // 返回响应头的异步函数 - }, - abort() { - // 中断请求,当外部调用abort时将触发此函数 - }, - onDownload(updateDownloadProgress) { - // 下载进度信息,内部持续调用updateDownloadProgress来更新下载进度 - }, - onUpload(updateUploadProgress) { - // 上传进度信息,内部持续调用updateUploadProgress来更新上传进度 - } - }; -} -``` - -### 请求参数详细 - -**requestElements** - -发送请求的相关元素,包含以下数据。 - -```typescript -interface RequestElements { - // 请求url,get参数已包含其中 - readonly url: string; - - // 请求类型,如GET、POST、PUT等 - readonly type: MethodType; - - // 请求头信息,对象 - readonly headers: Arg; - - // 请求体信息 - readonly data?: RequestBody; -} -``` - -**methodInstance** - -当前请求的 method 实例 - -### 返回参数详细 - -**response(必填)** - -一个异步函数,函数返回响应值,它将会传递给全局的响应拦截器(responded); - -**headers(必填)** - -一个异步函数,函数返回的响应头对象将传递给 Method 实例的 transformData 转换钩子函数; - -**abort(必填)** - -一个普通函数,它用于中断请求,所有的中断请求最终都将会调用此函数来执行; - -**onDownload(可选)** - -一个普通函数,它接收一个更新下载进度的回调函数,在此函数内自定义进度更新的频率,在此示例中模拟每隔 100 毫秒更新一次。`updateDownloadProgress`回调函数接收两个参数,第一个参数是总大小,第二个参数是已下载大小; - -**onUpload(可选)** - -一个普通函数,它接收一个更新上传进度的回调函数,在此函数内自定义进度更新的频率,在此示例中模拟每隔 100 毫秒更新一次。`updateUploadProgress`回调函数接收两个参数,第一个参数是总大小,第二个参数是已上传大小; - -## XMLHttpRequest 请求适配器示例 - -以下是一个通过 XMLHttpRequest 发送请求的适配器的示例,主要用于演示适配器的写法,代码不完整,不可运行。 - -```javascript -function XMLHttpRequestAdapter(requestElements, methodInstance) { - // 解构出需要用到的数据 - const { url, type, data, headers } = config; - - // 发送请求 - const xhr = new XMLHttpRequest(); - xhr.open(type, url); - for (const key in headers) { - xhr.setRequestHeader(key, headers[key]); - } - const responsePromise = new Promise((resolve, reject) => { - xhr.addEventListener('load', event => { - // 处理响应数据 - resolve(/* ... */); - }); - xhr.addEventListener('error', event => { - // 处理请求错误 - reject(/* ... */); - }); - }); - - xhr.send(JSON.stringify(data)); - - return { - // 返回响应数据的异步函数 - response: () => responsePromise, - - // 返回响应头的异步函数 - headers: () => responsePromise.then(() => xhr.getAllResponseHeaders()), - abort: () => { - xhr.abort(); - }, - - // 下载进度信息,内部持续调用updateDownloadProgress来更新下载进度 - onDownload: updateDownloadProgress => { - xhr.addEventListener('progress', event => { - // 数据接收进度 - updateDownloadProgress(event.total, event.loaded); - }); - }, - - // 上传进度信息,内部持续调用updateUploadProgress来更新上传进度 - onUpload: updateUploadProgress => { - xhr.upload.onprogress = event => { - updateUploadProgress(event.total, event.loaded); - }; - } - }; -} -``` - -:::note - -更完整的请求适配器细节可以查阅 [GlobalFetch 源码](https://github.com/alovajs/alova/blob/main/src/predefine/GlobalFetch.ts) 来了解。 - -::: - -## 请求适配器类型 - -全局的`beforeRequest`、`responded`拦截器,以及 method 实例创建时的配置对象的类型,都会根据请求适配器提供的类型自动推断,以下是 GlobalFetch 的类型。 - -```javascript -import type { RequestElements, Method, ProgressUpdater } from 'alova'; - -export type GlobalFetch = () => ( - elements: RequestElements, - method: Method -) => { - response: () => Promise, - headers: () => Promise, - onDownload: (handler: ProgressUpdater) => void, - abort: () => void -}; -``` - -在这个类型中分别指定了`RC`、`RE`和`RH`三个类型的值,因此在全局的拦截器中、method 实例配置中等地方将自动推断为请求适配器给定的类型。 - -它们分别表示为: - -- **RC**:*RequestConfig*的缩写,请求配置对象类型 -- **RH**:*ResponseHeader*的缩写,响应头对象类型 -- **RE**:*Response*的缩写,响应类型 - -如果你正在使用 **GlobalFetch**,他们的类型分别会被推断为: - -- **RC**:fetch api 的请求配置对象`RequestInit`; -- **RH**:响应头对象`Headers`; -- **RE**:响应对象`Response`; - -为了方便定义请求适配器的类型,alova 中也提供了一个适配器类型,你只需要根据需要传入`RC/RE/RH`泛型参数即可。 - -```typescript -import type { AlovaRequestAdapter } from 'alova'; -type CustomRequestAdpater = AlovaRequestAdapter; -``` +--- +title: 自定义请求适配器 +--- + +还记得如何创建一个 Alova 实例吗? + +```javascript +const alovaInstance = createAlova({ + // ... + requestAdapter: GlobalFetch() +}); +``` + +`requestAdapter`就是请求适配器,内部的请求发送和接收都将依赖请求适配器,`GlobalFetch`就是通过 fetch api 的方式来管理请求的,在大多数情况下我们可以使用它,但是,当`alova`运行在 fetch api 不可用的环境时(如 app、小程序),就需要更换一个支持当前环境的请求适配器。 + +那应该如何自定义一个请求适配器呢?很简单,它其实是一个函数,在每次发起请求时都会调用此函数,并返回一个对象,这个对象内包含如`url`、`method`、`data`、`headers`、`timeout`等请求相关的数据集合,虽然字段较多,但我们只需访问我们需要的数据即可。 + +## 请求适配器结构 + +请求适配器将接收到请求相关的参数,以及当前正在请求的 method 实例,并返回一个响应相关的函数集合。 + +```javascript +function CustomRequestAdapter(requestElements, methodInstance) { + // 发送请求... + return { + async response() { + // ...返回响应数据 + }, + async headers() { + // 返回响应头的异步函数 + }, + abort() { + // 中断请求,当外部调用abort时将触发此函数 + }, + onDownload(updateDownloadProgress) { + // 下载进度信息,内部持续调用updateDownloadProgress来更新下载进度 + }, + onUpload(updateUploadProgress) { + // 上传进度信息,内部持续调用updateUploadProgress来更新上传进度 + } + }; +} +``` + +### 请求参数详细 + +**requestElements** + +发送请求的相关元素,包含以下数据。 + +```typescript +interface RequestElements { + // 请求url,get参数已包含其中 + readonly url: string; + + // 请求类型,如GET、POST、PUT等 + readonly type: MethodType; + + // 请求头信息,对象 + readonly headers: Arg; + + // 请求体信息 + readonly data?: RequestBody; +} +``` + +**methodInstance** + +当前请求的 method 实例 + +### 返回参数详细 + +**response(必填)** + +一个异步函数,函数返回响应值,它将会传递给全局的响应拦截器(responded); + +**headers(必填)** + +一个异步函数,函数返回的响应头对象将传递给 Method 实例的 transformData 转换钩子函数; + +**abort(必填)** + +一个普通函数,它用于中断请求,所有的中断请求最终都将会调用此函数来执行; + +**onDownload(可选)** + +一个普通函数,它接收一个更新下载进度的回调函数,在此函数内自定义进度更新的频率,在此示例中模拟每隔 100 毫秒更新一次。`updateDownloadProgress`回调函数接收两个参数,第一个参数是总大小,第二个参数是已下载大小; + +**onUpload(可选)** + +一个普通函数,它接收一个更新上传进度的回调函数,在此函数内自定义进度更新的频率,在此示例中模拟每隔 100 毫秒更新一次。`updateUploadProgress`回调函数接收两个参数,第一个参数是总大小,第二个参数是已上传大小; + +## XMLHttpRequest 请求适配器示例 + +以下是一个通过 XMLHttpRequest 发送请求的适配器的示例,主要用于演示适配器的写法,代码不完整,不可运行。 + +```javascript +function XMLHttpRequestAdapter(requestElements, methodInstance) { + // 解构出需要用到的数据 + const { url, type, data, headers } = config; + + // 发送请求 + const xhr = new XMLHttpRequest(); + xhr.open(type, url); + for (const key in headers) { + xhr.setRequestHeader(key, headers[key]); + } + const responsePromise = new Promise((resolve, reject) => { + xhr.addEventListener('load', event => { + // 处理响应数据 + resolve(/* ... */); + }); + xhr.addEventListener('error', event => { + // 处理请求错误 + reject(/* ... */); + }); + }); + + xhr.send(JSON.stringify(data)); + + return { + // 返回响应数据的异步函数 + response: () => responsePromise, + + // 返回响应头的异步函数 + headers: () => responsePromise.then(() => xhr.getAllResponseHeaders()), + abort: () => { + xhr.abort(); + }, + + // 下载进度信息,内部持续调用updateDownloadProgress来更新下载进度 + onDownload: updateDownloadProgress => { + xhr.addEventListener('progress', event => { + // 数据接收进度 + updateDownloadProgress(event.total, event.loaded); + }); + }, + + // 上传进度信息,内部持续调用updateUploadProgress来更新上传进度 + onUpload: updateUploadProgress => { + xhr.upload.onprogress = event => { + updateUploadProgress(event.total, event.loaded); + }; + } + }; +} +``` + +:::note + +更完整的请求适配器细节可以查阅 [GlobalFetch 源码](https://github.com/alovajs/alova/blob/main/src/predefine/GlobalFetch.ts) 来了解。 + +::: + +## 请求适配器类型 + +全局的`beforeRequest`、`responded`拦截器,以及 method 实例创建时的配置对象的类型,都会根据请求适配器提供的类型自动推断,以下是 GlobalFetch 的类型。 + +```javascript +import type { RequestElements, Method, ProgressUpdater } from 'alova'; + +export type GlobalFetch = () => ( + elements: RequestElements, + method: Method +) => { + response: () => Promise, + headers: () => Promise, + onDownload: (handler: ProgressUpdater) => void, + abort: () => void +}; +``` + +在这个类型中分别指定了`RC`、`RE`和`RH`三个类型的值,因此在全局的拦截器中、method 实例配置中等地方将自动推断为请求适配器给定的类型。 + +它们分别表示为: + +- **RC**:*RequestConfig*的缩写,请求配置对象类型 +- **RH**:*ResponseHeader*的缩写,响应头对象类型 +- **RE**:*Response*的缩写,响应类型 + +如果你正在使用 **GlobalFetch**,他们的类型分别会被推断为: + +- **RC**:fetch api 的请求配置对象`RequestInit`; +- **RH**:响应头对象`Headers`; +- **RE**:响应对象`Response`; + +为了方便定义请求适配器的类型,alova 中也提供了一个适配器类型,你只需要根据需要传入`RC/RE/RH`泛型参数即可。 + +```typescript +import type { AlovaRequestAdapter } from 'alova'; +type CustomRequestAdpater = AlovaRequestAdapter; +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/10-custom/03-custom-storage-adapter.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/10-custom/03-custom-storage-adapter.md index 2ccc85e9e..4ada87ca3 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/10-custom/03-custom-storage-adapter.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/10-custom/03-custom-storage-adapter.md @@ -1,48 +1,47 @@ ---- -title: 自定义存储适配器 -sidebar_position: 30 ---- - -alova 中涉及多个需要数据持久化的功能,如持久化缓存、静默提交和离线提交。**在默认情况下,alova 会使用`localStorage`来存储持久化数据**,但考虑到非浏览器环境下,因此也支持了自定义。 - -自定义存储适配器同样非常简单,你只需要指定保存数据、获取数据,以及移除数据的函数即可,大致是这样的。 - -```javascript -const customStorageAdapter = { - set(key, value) { - // 保存数据,value为结构化数据,可调用JSON.stringify转换为字符串 - }, - get(key) { - // 获取数据,需要返回结构化数据,可调用JSON.parse转换为对象 - }, - remove(key) { - // 移除数据 - } -}; -``` - -然后在创建`alova`实例时传入这个适配器即可。 - -```javascript -const alovaInstance = createAlova({ - // ... - storageAdapter: customStorageAdapter -}); -``` - -## SessionStorage 存储适配器示例 - -```javascript -const sessionStorageAdapter = { - set(key, value) { - sessionStorage.setItem(key, JSON.stringify(value)); - }, - get(key) { - const data = sessionStorage.getItem(key); - return data ? JSON.parse(data) : data; - }, - remove(key) { - sessionStorage.removeItem(key); - } -}; -``` +--- +title: 自定义存储适配器 +--- + +alova 中涉及多个需要数据持久化的功能,如持久化缓存、静默提交和离线提交。**在默认情况下,alova 会使用`localStorage`来存储持久化数据**,但考虑到非浏览器环境下,因此也支持了自定义。 + +自定义存储适配器同样非常简单,你只需要指定保存数据、获取数据,以及移除数据的函数即可,大致是这样的。 + +```javascript +const customStorageAdapter = { + set(key, value) { + // 保存数据,value为结构化数据,可调用JSON.stringify转换为字符串 + }, + get(key) { + // 获取数据,需要返回结构化数据,可调用JSON.parse转换为对象 + }, + remove(key) { + // 移除数据 + } +}; +``` + +然后在创建`alova`实例时传入这个适配器即可。 + +```javascript +const alovaInstance = createAlova({ + // ... + storageAdapter: customStorageAdapter +}); +``` + +## SessionStorage 存储适配器示例 + +```javascript +const sessionStorageAdapter = { + set(key, value) { + sessionStorage.setItem(key, JSON.stringify(value)); + }, + get(key) { + const data = sessionStorage.getItem(key); + return data ? JSON.parse(data) : data; + }, + remove(key) { + sessionStorage.removeItem(key); + } +}; +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/10-custom/04-custom-stateshook.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/10-custom/04-custom-stateshook.md index 7266e4e2f..da7af81ba 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/10-custom/04-custom-stateshook.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/10-custom/04-custom-stateshook.md @@ -1,101 +1,100 @@ ---- -title: 自定义States Hook -sidebar_position: 50 ---- - -还记得如何创建一个 Alova 实例吗? - -```javascript -const alovaInstance = createAlova({ - // ... - statesHook: ReactHook -}); -``` - -`statesHook`将决定在请求时返回哪个 UI 库的状态,alova 目前提供了**VueHook、ReactHook、svelteHook**。 - -在大部分情况下你应该用不到这个功能,但如果你需要适配更多 alova 不支持的 MVVM 库,就需要自定义编写`statesHook`了。 - -`statesHook`是一个包含特定函数的普通对象,不过这些还是基本不涉及算法,我们来看看 **VueHook** 是怎么编写的吧。 - -## statesHook 结构 - -statesHook 使用一个对象进行表示,以下为**VueHook**的示例。 - -```javascript -import { ref, watch, onUnmounted } from 'vue'; - -const VueHook = { - // 状态创建函数 - create: rawData => ref(data), - - // 状态导出函数 - export: state => state, - - // 脱水函数 - dehydrate: state => state.value, - - // 响应式状态更新函数 - update: (newVal, states) => { - Object.keys(newVal).forEach(key => { - states[key].value = newVal[key]; - }); - }, - - // 请求发送控制函数 - effectRequest({ handler, removeStates, saveStates, immediate, frontStates, watchingStates }) { - // 组件卸载时移除对应状态 - onUnmounted(removeStates); - - // 调用useRequest和useFetcher时,watchingStates为undefined - if (!watchingStates) { - handler(); - return; - } - - // 调用useWatcher时,watchingStates为需要监听的状态数组 - // immediate为true时,表示需要立即发送请求 - watch(watchingStates, handler, { immediate }); - } -}; -``` - -## 自定义 statesHook 函数说明 - -> 以下 5 个函数均必须指定。 - -**create** - -响应式状态创建函数,`loading`、`error`、`data`、`downloading`、`uploading`等都是调用此函数创建的,如 vue3 项目下将创建为 ref 值; - -**export** - -状态导出函数,此函数接收 create 函数创建的响应式状态,并导出最终给开发者使用的状态,这里`VueHook`导出的状态是原状态; - -**dehydrate** - -脱水函数,意思是将响应式状态转换为普通数据,与 create 是相反的操作,在`updateState`中; - -**update** - -响应式状态更新函数,`alova`内部维护的状态更新都是通过此函数完成。此函数接收两个参数,第一个参数是新的数据对象,第二个参数是原响应式状态的 map 集合,这里你可以固定写一个循环更新`states`; - -**effectRequest** - -请求发送控制函数,它会在`useRequest`、`useWatcher`、`useFetcher`被调用时立即执行此函数,我们要在这个函数内要完成三件事: - -1. 当前组件卸载时,调用 removeStates 函数移除当前组件涉及到的响应式状态,避免内存溢出; -2. 当调用 useWatcher 时,绑定状态监听,状态改变时调用 sendRequest 函数,你可以用`states`是否为数组判断是否为`useWatcher`被调用,同时,`immediate`参数用于判断`useWatcher`调用时是否需要立即发送请求; -3. 当调用`useRequest`和`useFetcher`时,调用 sendRequest 发出一次请求,此时`states`为`undefined`; - -:::warning 注意 - -如果 statesHook 涉及的库是像`react`,每次重新渲染都会调用`alova`的 use hook 的,那么在`effectRequest`中还需要在每次重新渲染时触发`saveStates`函数,这是因为`react`每次重新渲染都会刷新它的状态引用,因此我们需要再次重新保存它们。 - -::: - -[ReactHook 源码点此查看](https://github.com/alovajs/alova/blob/main/src/predefine/ReactHook.ts) - -## statesHook 类型 - -如果你在自定义 statesHook 时,也希望它可以支持 typescript,可以 [点此查看](/tutorial/combine-framework/typescript) +--- +title: 自定义States Hook +--- + +还记得如何创建一个 Alova 实例吗? + +```javascript +const alovaInstance = createAlova({ + // ... + statesHook: ReactHook +}); +``` + +`statesHook`将决定在请求时返回哪个 UI 库的状态,alova 目前提供了**VueHook、ReactHook、svelteHook**。 + +在大部分情况下你应该用不到这个功能,但如果你需要适配更多 alova 不支持的 MVVM 库,就需要自定义编写`statesHook`了。 + +`statesHook`是一个包含特定函数的普通对象,不过这些还是基本不涉及算法,我们来看看 **VueHook** 是怎么编写的吧。 + +## statesHook 结构 + +statesHook 使用一个对象进行表示,以下为**VueHook**的示例。 + +```javascript +import { ref, watch, onUnmounted } from 'vue'; + +const VueHook = { + // 状态创建函数 + create: rawData => ref(data), + + // 状态导出函数 + export: state => state, + + // 脱水函数 + dehydrate: state => state.value, + + // 响应式状态更新函数 + update: (newVal, states) => { + Object.keys(newVal).forEach(key => { + states[key].value = newVal[key]; + }); + }, + + // 请求发送控制函数 + effectRequest({ handler, removeStates, saveStates, immediate, frontStates, watchingStates }) { + // 组件卸载时移除对应状态 + onUnmounted(removeStates); + + // 调用useRequest和useFetcher时,watchingStates为undefined + if (!watchingStates) { + handler(); + return; + } + + // 调用useWatcher时,watchingStates为需要监听的状态数组 + // immediate为true时,表示需要立即发送请求 + watch(watchingStates, handler, { immediate }); + } +}; +``` + +## 自定义 statesHook 函数说明 + +> 以下 5 个函数均必须指定。 + +**create** + +响应式状态创建函数,`loading`、`error`、`data`、`downloading`、`uploading`等都是调用此函数创建的,如 vue3 项目下将创建为 ref 值; + +**export** + +状态导出函数,此函数接收 create 函数创建的响应式状态,并导出最终给开发者使用的状态,这里`VueHook`导出的状态是原状态; + +**dehydrate** + +脱水函数,意思是将响应式状态转换为普通数据,与 create 是相反的操作,在`updateState`中; + +**update** + +响应式状态更新函数,`alova`内部维护的状态更新都是通过此函数完成。此函数接收两个参数,第一个参数是新的数据对象,第二个参数是原响应式状态的 map 集合,这里你可以固定写一个循环更新`states`; + +**effectRequest** + +请求发送控制函数,它会在`useRequest`、`useWatcher`、`useFetcher`被调用时立即执行此函数,我们要在这个函数内要完成三件事: + +1. 当前组件卸载时,调用 removeStates 函数移除当前组件涉及到的响应式状态,避免内存溢出; +2. 当调用 useWatcher 时,绑定状态监听,状态改变时调用 sendRequest 函数,你可以用`states`是否为数组判断是否为`useWatcher`被调用,同时,`immediate`参数用于判断`useWatcher`调用时是否需要立即发送请求; +3. 当调用`useRequest`和`useFetcher`时,调用 sendRequest 发出一次请求,此时`states`为`undefined`; + +:::warning 注意 + +如果 statesHook 涉及的库是像`react`,每次重新渲染都会调用`alova`的 use hook 的,那么在`effectRequest`中还需要在每次重新渲染时触发`saveStates`函数,这是因为`react`每次重新渲染都会刷新它的状态引用,因此我们需要再次重新保存它们。 + +::: + +[ReactHook 源码点此查看](https://github.com/alovajs/alova/blob/main/src/predefine/ReactHook.ts) + +## statesHook 类型 + +如果你在自定义 statesHook 时,也希望它可以支持 typescript,可以 [点此查看](/tutorial/combine-framework/typescript) diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/01-RSM.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/01-RSM.md index d2db81034..ea86a506c 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/01-RSM.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/01-RSM.md @@ -1,61 +1,60 @@ ---- -title: 请求场景模型(RSM) -sidebar_position: 10 ---- - -## 什么是请求场景模型 - -请求场景模型是以客户端视角的,描述客户端从触发请求意图到接收请求结果的抽象模型,分别由请求时机、请求行为、请求事件以及响应管理四个阶段组成。例如在进行一次请求时经常需要思考以下问题, - -1. 什么时候发出请求; -2. 是否要展示请求状态; -3. 是否需要对请求进行失败重试; -4. 要如何加工响应数据; -5. 是否需要对请求参数加密; -6. 是否要对高频使用的响应数据做缓存; -7. 如何进行跨页面操作数据; -8. 弱网或断网环境下需要如何处理请求; -9. ... - -`fetch`或`axios`往往更专注于如何与服务端交互,但对于上面的问题我们总是需要自己处理,这些有利于应用性能和稳定性的功能,总会让程序员们编写出低维护性的代码。请求场景模型就是从准备请求到响应数据加工完毕的所有环节进行抽象,从而覆盖以前端为视角的,整个 CS 交互生命周期的模型。`alova`就是一个以请求场景模型的库,它是对`axios`等请求库的一种补充,而非替代品。 - -> CS 交互:泛指所有客户端类型和服务端的数据交互 - -## 请求场景模型 - -![RSM](/img/rsm-cn.png) - -## 请求时机 - -描述在什么时候需要发出请求,在`alova`中以`useHook`实现。 - -- 初始化展示数据,如刚进入某个界面或子界面; -- 人机交互触发 CS 交互,需要变更数据重新发出请求,如翻页、筛选、排序、模糊搜索等; -- 以防抖方式发送请求,避免视图数据闪动,以及降低服务端压力 -- 预加载数据,如分页内预先加载下一页内容、预测用户点击某个按钮后预先拉取数据; -- 操作服务端数据,需发出增删改查请求,如提交数据、删除数据等; -- 同步服务端状态,如数据变化较快的场景下轮询请求、操作了某个数据后重新拉取数据; - -## 请求行为 - -描述以怎样的方式处理请求,在`alova`中以 Method 抽象实现。 - -- 占位请求,请求时展示 loading、骨架图、或者是上次使用的真实数据; -- 缓存高频响应,多次执行请求会使用保鲜数据; -- 多请求串行与并行; -- 重要接口的重试机制,降低网络不稳定造成的请求失败概率; -- 静默提交,当只关心提交数据时,提交请求后直接响应成功事件,后台保证请求成功; -- 离线提交,离线时将提交数据暂存到本地,网络连接后再提交; - -## 请求事件 - -表示携带请求参数发送请求,获得响应,`alova`可以与`axios`、`fetch`、`XMLHttpRequest`等任意请求库或原生方案共同协作。 - -## 响应管理 - -`alova`将响应数据状态化并统一管理,以请求层面的方式刷新视图数据、操作缓存,避免了在组件层面的操作,更加优雅和统一。 - -- 移除缓存响应数据,再次发起请求时将从服务端拉取; -- 更新缓存响应数据,可更新任意位置响应数据,非常有利于跨页面更新数据; -- 刷新响应数据,可重新刷新任意位置的响应数据,也非常有利于跨页面更新数据; -- 自定义设置缓存,在请求批量数据时,可手动对批量数据一一设置缓存,从而满足后续单条数据的缓存命中; +--- +title: 请求场景模型(RSM) +--- + +## 什么是请求场景模型 + +请求场景模型是以客户端视角的,描述客户端从触发请求意图到接收请求结果的抽象模型,分别由请求时机、请求行为、请求事件以及响应管理四个阶段组成。例如在进行一次请求时经常需要思考以下问题, + +1. 什么时候发出请求; +2. 是否要展示请求状态; +3. 是否需要对请求进行失败重试; +4. 要如何加工响应数据; +5. 是否需要对请求参数加密; +6. 是否要对高频使用的响应数据做缓存; +7. 如何进行跨页面操作数据; +8. 弱网或断网环境下需要如何处理请求; +9. ... + +`fetch`或`axios`往往更专注于如何与服务端交互,但对于上面的问题我们总是需要自己处理,这些有利于应用性能和稳定性的功能,总会让程序员们编写出低维护性的代码。请求场景模型就是从准备请求到响应数据加工完毕的所有环节进行抽象,从而覆盖以前端为视角的,整个 CS 交互生命周期的模型。`alova`就是一个以请求场景模型的库,它是对`axios`等请求库的一种补充,而非替代品。 + +> CS 交互:泛指所有客户端类型和服务端的数据交互 + +## 请求场景模型 + +![RSM](/img/rsm-cn.png) + +## 请求时机 + +描述在什么时候需要发出请求,在`alova`中以`useHook`实现。 + +- 初始化展示数据,如刚进入某个界面或子界面; +- 人机交互触发 CS 交互,需要变更数据重新发出请求,如翻页、筛选、排序、模糊搜索等; +- 以防抖方式发送请求,避免视图数据闪动,以及降低服务端压力 +- 预加载数据,如分页内预先加载下一页内容、预测用户点击某个按钮后预先拉取数据; +- 操作服务端数据,需发出增删改查请求,如提交数据、删除数据等; +- 同步服务端状态,如数据变化较快的场景下轮询请求、操作了某个数据后重新拉取数据; + +## 请求行为 + +描述以怎样的方式处理请求,在`alova`中以 Method 抽象实现。 + +- 占位请求,请求时展示 loading、骨架图、或者是上次使用的真实数据; +- 缓存高频响应,多次执行请求会使用保鲜数据; +- 多请求串行与并行; +- 重要接口的重试机制,降低网络不稳定造成的请求失败概率; +- 静默提交,当只关心提交数据时,提交请求后直接响应成功事件,后台保证请求成功; +- 离线提交,离线时将提交数据暂存到本地,网络连接后再提交; + +## 请求事件 + +表示携带请求参数发送请求,获得响应,`alova`可以与`axios`、`fetch`、`XMLHttpRequest`等任意请求库或原生方案共同协作。 + +## 响应管理 + +`alova`将响应数据状态化并统一管理,以请求层面的方式刷新视图数据、操作缓存,避免了在组件层面的操作,更加优雅和统一。 + +- 移除缓存响应数据,再次发起请求时将从服务端拉取; +- 更新缓存响应数据,可更新任意位置响应数据,非常有利于跨页面更新数据; +- 刷新响应数据,可重新刷新任意位置的响应数据,也非常有利于跨页面更新数据; +- 自定义设置缓存,在请求批量数据时,可手动对批量数据一一设置缓存,从而满足后续单条数据的缓存命中; diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/02-comparison.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/02-comparison.md index 6839ce538..ad1e92a3f 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/02-comparison.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/02-comparison.md @@ -1,93 +1,92 @@ ---- -title: 与其他库比较 -sidebar_position: 20 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -## 与 axios 对比 - -axios 提供了基于 promise 的非常简单易用的 HTTP 请求功能,只需要简单的一行代码即可发送和接收请求,并且可以在浏览器和 nodejs 环境下运行,是一个非常优秀的请求 js 库。 - -但是 axios 聚焦于请求发送和接收响应,这意味着如果你需要自行编写更多代码来主动优化请求功能,而 alova 像是 axios 的武器装备,将 axios 与 alova 组合使用可以获得更强大的请求能力,以下是 alova 为 axios 附加的请求管理能力。 - -### alova 为 axios 提供自动化请求状态管理 - -仅使用 axios 时,通常需要你自行维护请求相关状态,使用 alova 的 use hook 后可以获得自动化的请求状态管理能力。 - - - - -```javascript -// vue3代码示例 -const loading = ref(false); -const data = ref({}); -const error = ref(null); -const request = async () => { - try { - loading.value = true; - data.value = await axios.get('/xxx'); - } catch (e) { - error.value = e; - } - loading.value = false; -}; -mounted(request); -``` - - - - -```javascript -// 将axios作为alova的请求适配器 -const { loading, data, error } = useRequest(alova.Get('/xxx')); -``` - - - - -### alova 提供开箱即用的高性能请求策略 - -alova 为你提供了[多个高性能的请求策略模块](/tutorial/strategy),你可以根据不同请求场景使用不同的模块,这是 axios 不具备的。 - -### alova 为 axios 提供响应数据缓存 - -alova 分别提供了 3 种缓存模式来满足不同的缓存场景,分别为内存模式、缓存占位模式、恢复模式。它们是组件无关的,只要请求地址和参数相同都可以命中缓存,除非你关闭了它。响应数据缓存可以极大地提高请求流畅性,降低服务端压力。 - -### alova 为 axios 提供请求共享功能 - -请求共享在同时发送多个相同请求时,将会复用同一个请求,它也可以提升应用流畅性和降低服务端压力。 - -### alova 为 axios 提供数据预拉取 - -提前请求将要使用的数据,也可以极大提升应用流畅性。 - -### alova 可管理请求状态 - -你可以使用 alova 跨任意的组件层级来访问其他组件内的状态化数据,这可以让你减少跨组件通信的一些麻烦。 - -## 与 react-query、swr 对比 - -react-query 是一个强大的异步状态管理,swr 是一个用于数据请求的 React Hooks 库,它们的共同特性也是使用 use hook 来发送和管理请求,和数据缓存功能,对于它们,alova 有以下不同之处。 - -### alova 的目标不同 - -实际上,alova 的 use hook 也是参考了 react-query 和 swr 的设计,但是 alova 选择了请求策略库的方向,你可以在不同的请求场景下使用不同的请求策略模块,让你在编写更少量代码同时,也能实现更高效地 Client-Server 数据交互。 - -### Method 代理设计 - -react-query 和 swr 都是在 use hook 中直接使用`axios`或`fetch api`发送请求,而 alova 使用了 `Method` 代理的设计模式,这样设计具有以下 3 个好处: - -1. 统一的使用体验,不会因平台或 UI 框架不同而存在不同的使用方式。 -2. `axios`和`fetch api`等请求库以请求适配器的方式,与每个 api 解耦,这让 alova 提供了统一的开发体验和完美的代码迁移。 -3. 每个 method 实例都代表不同的 api,你可以将同一个 api 的请求参数与请求行为参数聚合到同一个 method 实例中,而不会分散开,更适合管理大量的 api。 -4. alova 通过对 method 实例上的请求参数序列化,实现了自动化管理响应数据缓存,你不需要指定缓存 key,而 react-query 和 swr 都需要自定义设置`queryKey`来管理缓存。 - -### 高灵活性 - -alova 通过各种适配器、中间件实现了很高的灵活性,不仅可以运行在任何 js 环境,还可以支持用户自定义不同场景下的请求模块。 - -### 轻量化 - -alova 很轻量,体积只有 react-query 和 axios 的 30%+。与 swr 体积相似,但提供更丰富的功能。 +--- +title: 与其他库比较 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## 与 axios 对比 + +axios 提供了基于 promise 的非常简单易用的 HTTP 请求功能,只需要简单的一行代码即可发送和接收请求,并且可以在浏览器和 nodejs 环境下运行,是一个非常优秀的请求 js 库。 + +但是 axios 聚焦于请求发送和接收响应,这意味着如果你需要自行编写更多代码来主动优化请求功能,而 alova 像是 axios 的武器装备,将 axios 与 alova 组合使用可以获得更强大的请求能力,以下是 alova 为 axios 附加的请求管理能力。 + +### alova 为 axios 提供自动化请求状态管理 + +仅使用 axios 时,通常需要你自行维护请求相关状态,使用 alova 的 use hook 后可以获得自动化的请求状态管理能力。 + + + + +```javascript +// vue3代码示例 +const loading = ref(false); +const data = ref({}); +const error = ref(null); +const request = async () => { + try { + loading.value = true; + data.value = await axios.get('/xxx'); + } catch (e) { + error.value = e; + } + loading.value = false; +}; +mounted(request); +``` + + + + +```javascript +// 将axios作为alova的请求适配器 +const { loading, data, error } = useRequest(alova.Get('/xxx')); +``` + + + + +### alova 提供开箱即用的高性能请求策略 + +alova 为你提供了[多个高性能的请求策略模块](/tutorial/strategy),你可以根据不同请求场景使用不同的模块,这是 axios 不具备的。 + +### alova 为 axios 提供响应数据缓存 + +alova 分别提供了 3 种缓存模式来满足不同的缓存场景,分别为内存模式、缓存占位模式、恢复模式。它们是组件无关的,只要请求地址和参数相同都可以命中缓存,除非你关闭了它。响应数据缓存可以极大地提高请求流畅性,降低服务端压力。 + +### alova 为 axios 提供请求共享功能 + +请求共享在同时发送多个相同请求时,将会复用同一个请求,它也可以提升应用流畅性和降低服务端压力。 + +### alova 为 axios 提供数据预拉取 + +提前请求将要使用的数据,也可以极大提升应用流畅性。 + +### alova 可管理请求状态 + +你可以使用 alova 跨任意的组件层级来访问其他组件内的状态化数据,这可以让你减少跨组件通信的一些麻烦。 + +## 与 react-query、swr 对比 + +react-query 是一个强大的异步状态管理,swr 是一个用于数据请求的 React Hooks 库,它们的共同特性也是使用 use hook 来发送和管理请求,和数据缓存功能,对于它们,alova 有以下不同之处。 + +### alova 的目标不同 + +实际上,alova 的 use hook 也是参考了 react-query 和 swr 的设计,但是 alova 选择了请求策略库的方向,你可以在不同的请求场景下使用不同的请求策略模块,让你在编写更少量代码同时,也能实现更高效地 Client-Server 数据交互。 + +### Method 代理设计 + +react-query 和 swr 都是在 use hook 中直接使用`axios`或`fetch api`发送请求,而 alova 使用了 `Method` 代理的设计模式,这样设计具有以下 3 个好处: + +1. 统一的使用体验,不会因平台或 UI 框架不同而存在不同的使用方式。 +2. `axios`和`fetch api`等请求库以请求适配器的方式,与每个 api 解耦,这让 alova 提供了统一的开发体验和完美的代码迁移。 +3. 每个 method 实例都代表不同的 api,你可以将同一个 api 的请求参数与请求行为参数聚合到同一个 method 实例中,而不会分散开,更适合管理大量的 api。 +4. alova 通过对 method 实例上的请求参数序列化,实现了自动化管理响应数据缓存,你不需要指定缓存 key,而 react-query 和 swr 都需要自定义设置`queryKey`来管理缓存。 + +### 高灵活性 + +alova 通过各种适配器、中间件实现了很高的灵活性,不仅可以运行在任何 js 环境,还可以支持用户自定义不同场景下的请求模块。 + +### 轻量化 + +alova 很轻量,体积只有 react-query 和 axios 的 30%+。与 swr 体积相似,但提供更丰富的功能。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/03-Q&A.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/03-Q&A.md index b1b4684db..8736ffcec 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/03-Q&A.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/03-Q&A.md @@ -1,24 +1,23 @@ ---- -title: 提问&回答 -sidebar_position: 30 ---- - -## 为什么创造 alova? - -数据请求一直是应用程序必不可少的重要部分,自从 XMLHttpRequest 诞生以来请求方案层出不穷,客户端的数据交互探索一直聚焦于请求的简单性,如`$.ajax`、`axios`、`fetch api`以及`react-query`等请求工具,编码形式从回调函数、Promise,再到 usehook 不断发展,这些 js 库在请求简单性上已经做得很好了,不过它们只提供了通用的功能,这意味着对于不同的请求场景例如共享请求、分页请求、表单提交、上传和下载文件等,开发者依然需要自己编写复杂的代码,降低开发效率,性能也无法得到保证,在越来越看重的用户体验的时代,应用的流畅性变得越来越重要。 - -同时,客户端和服务端的协作也是割裂的,前端工程师需要查阅 API 文档并手动编写 API 函数,并且服务端 API 的任何更改都需要主动通知前端工程师,这将会使产品变得更加不可控。 - -**而我们认为还有更简单的方案,即根据请求场景,例如分页、表单提交、断点续传等,选择对应的 useHook,它将帮你管理数据,控制何时应该发送请求**。从而让开发者在编写少量代码也能实现更高效地 Client-Server 数据交互。 - -同时,alova 具有很灵活的扩展能力来实现不同场景下的请求策略,你也可以自定义自己的请求场景,这部分内容在[自定义章节](/category/custom)。 - -为了覆盖更多请求场景,我们还将请求场景抽象成了 [请求场景模型(RSM)](/tutorial/others/RSM),它很好地解释了 alova 的请求策略方案。在未来,alova 将承载着我们对请求策略的探索之路继续前行。 - -## 替代请求库? - -alova 是一个请求策略库,它的创建初衷是对不同请求场景提供特定的请求策略解决方案,从而更简洁优雅地实现流畅的请求体验,而例如`$.ajax`、`axios`和`fetch-api`等对请求发送和响应接收提供了很好的支持,它们是 [RSM](/tutorial/others/RSM) 流程中必不可少的一个环节(请求事件),alova 仍然需要依靠它们进行请求,因此我们可以将 alova 看作是请求库的一种武装,让请求库变得更加强大。 - -## 为什么要深度绑定 UI 框架? - -对一个 js 库来说解耦意味着更多场景下的使用,例如 axios 可以在 nodejs 中使用,但同时意味着开发者需要写更多的模板代码,比如使用 useHooks 封装 axios 等。而 alova 摒弃了解耦带来的更多使用场景,将使用范围定位在与 UI 框架配合使用,以最精简的方式使用 alova,这是为了开发者的收益方面而考量的,在一个 UI 框架盛行的时候,深度绑定可以为开发者提供直接使用的功能,提升开发者的使用体验,而不需要太多的模板代码。 +--- +title: 提问&回答 +--- + +## 为什么创造 alova? + +数据请求一直是应用程序必不可少的重要部分,自从 XMLHttpRequest 诞生以来请求方案层出不穷,客户端的数据交互探索一直聚焦于请求的简单性,如`$.ajax`、`axios`、`fetch api`以及`react-query`等请求工具,编码形式从回调函数、Promise,再到 usehook 不断发展,这些 js 库在请求简单性上已经做得很好了,不过它们只提供了通用的功能,这意味着对于不同的请求场景例如共享请求、分页请求、表单提交、上传和下载文件等,开发者依然需要自己编写复杂的代码,降低开发效率,性能也无法得到保证,在越来越看重的用户体验的时代,应用的流畅性变得越来越重要。 + +同时,客户端和服务端的协作也是割裂的,前端工程师需要查阅 API 文档并手动编写 API 函数,并且服务端 API 的任何更改都需要主动通知前端工程师,这将会使产品变得更加不可控。 + +**而我们认为还有更简单的方案,即根据请求场景,例如分页、表单提交、断点续传等,选择对应的 useHook,它将帮你管理数据,控制何时应该发送请求**。从而让开发者在编写少量代码也能实现更高效地 Client-Server 数据交互。 + +同时,alova 具有很灵活的扩展能力来实现不同场景下的请求策略,你也可以自定义自己的请求场景,这部分内容在[自定义章节](/category/custom)。 + +为了覆盖更多请求场景,我们还将请求场景抽象成了 [请求场景模型(RSM)](/tutorial/others/RSM),它很好地解释了 alova 的请求策略方案。在未来,alova 将承载着我们对请求策略的探索之路继续前行。 + +## 替代请求库? + +alova 是一个请求策略库,它的创建初衷是对不同请求场景提供特定的请求策略解决方案,从而更简洁优雅地实现流畅的请求体验,而例如`$.ajax`、`axios`和`fetch-api`等对请求发送和响应接收提供了很好的支持,它们是 [RSM](/tutorial/others/RSM) 流程中必不可少的一个环节(请求事件),alova 仍然需要依靠它们进行请求,因此我们可以将 alova 看作是请求库的一种武装,让请求库变得更加强大。 + +## 为什么要深度绑定 UI 框架? + +对一个 js 库来说解耦意味着更多场景下的使用,例如 axios 可以在 nodejs 中使用,但同时意味着开发者需要写更多的模板代码,比如使用 useHooks 封装 axios 等。而 alova 摒弃了解耦带来的更多使用场景,将使用范围定位在与 UI 框架配合使用,以最精简的方式使用 alova,这是为了开发者的收益方面而考量的,在一个 UI 框架盛行的时候,深度绑定可以为开发者提供直接使用的功能,提升开发者的使用体验,而不需要太多的模板代码。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/04-future.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/04-future.md index dbf3efd7d..8a17643ce 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/04-future.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/04-future.md @@ -1,36 +1,35 @@ ---- -title: alova的未来 -sidebar_position: 40 ---- - -alovajs 的定位是轻量级的请求策略库,目前在请求功能和请求策略方面提供了较好的支持,但 alovajs 的未来不止于此。 - -## 更多的请求策略 - -这是一直不变的方向,我们会持续探索基于常见业务下的高效易用的请求策略。 - -## 更多的 UI 框架支持 - -alovajs 虽然是一个基于 UI 框架的请求工具,但它的灵活设计支持我们在各种 UI 框架中使用,最终将会兼容以下的 UI 框架和 js 环境: - -- 函数风格框架,如`react/react-native/vue-composntion/svelte/solid/preact/qwik`。 -- SSR 框架,如`next/nuxt/sveltekit`。 -- class 风格框架,如`angular/lit/stencil`。 -- options 风格框架,如`vue-options/原生小程序(中国🇨🇳)`。 -- 多端适配框架,如`Uniapp/Taro`(中国 🇨🇳)。 - -详情请[前往 UI 框架](/category/framework)中查看。 - -## API 的自动管理和维护 - -在未来,alovajs 还致力于解决前端 API 的问题,并更进一步地简化前端开发的工作流,这就是 alovajs 的下一个发展方向:**API 的自动管理和维护**,具体包含以下三点。 - -1. 自动生成 ts 类型完整的、描述完整的请求函数,无论是 js 项目还是 ts 项目,都调用无需引入,让开发者就像直接调用`location.reload`这样方便,并且请求函数可直接看到完整的描述和请求参数类型提示,这些都可以由 openAPI 自动生成。 - -2. 由于自动生成的请求函数拥有完整的描述和类型提示,开发一个 vscode 插件通过关键字来快速检索需要使用的 API,你也不再需要去查阅 API 文档了。 - -3. 解决前后端协作断层的问题,接口的任何变动前端可知,可以在启动项目时被通知,如果在构建项目时发现存在变动,则会抛出错误停止构建,如果是 ts 项目也会在编译时抛出错误,也可以通过 vscode 插件查看变动记录。 - -## 开发清单 - -[点此查看所有规划的开发清单](/contributing/become-core-member#%E5%8F%AF%E5%8F%82%E4%B8%8E%E7%9A%84%E4%BB%93%E5%BA%93%E5%88%97%E8%A1%A8) +--- +title: alova的未来 +--- + +alovajs 的定位是轻量级的请求策略库,目前在请求功能和请求策略方面提供了较好的支持,但 alovajs 的未来不止于此。 + +## 更多的请求策略 + +这是一直不变的方向,我们会持续探索基于常见业务下的高效易用的请求策略。 + +## 更多的 UI 框架支持 + +alovajs 虽然是一个基于 UI 框架的请求工具,但它的灵活设计支持我们在各种 UI 框架中使用,最终将会兼容以下的 UI 框架和 js 环境: + +- 函数风格框架,如`react/react-native/vue-composntion/svelte/solid/preact/qwik`。 +- SSR 框架,如`next/nuxt/sveltekit`。 +- class 风格框架,如`angular/lit/stencil`。 +- options 风格框架,如`vue-options/原生小程序(中国🇨🇳)`。 +- 多端适配框架,如`Uniapp/Taro`(中国 🇨🇳)。 + +详情请[前往 UI 框架](/category/framework)中查看。 + +## API 的自动管理和维护 + +在未来,alovajs 还致力于解决前端 API 的问题,并更进一步地简化前端开发的工作流,这就是 alovajs 的下一个发展方向:**API 的自动管理和维护**,具体包含以下三点。 + +1. 自动生成 ts 类型完整的、描述完整的请求函数,无论是 js 项目还是 ts 项目,都调用无需引入,让开发者就像直接调用`location.reload`这样方便,并且请求函数可直接看到完整的描述和请求参数类型提示,这些都可以由 openAPI 自动生成。 + +2. 由于自动生成的请求函数拥有完整的描述和类型提示,开发一个 vscode 插件通过关键字来快速检索需要使用的 API,你也不再需要去查阅 API 文档了。 + +3. 解决前后端协作断层的问题,接口的任何变动前端可知,可以在启动项目时被通知,如果在构建项目时发现存在变动,则会抛出错误停止构建,如果是 ts 项目也会在编译时抛出错误,也可以通过 vscode 插件查看变动记录。 + +## 开发清单 + +[点此查看所有规划的开发清单](/contributing/become-core-member#%E5%8F%AF%E5%8F%82%E4%B8%8E%E7%9A%84%E4%BB%93%E5%BA%93%E5%88%97%E8%A1%A8) diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/05-react-native.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/05-react-native.md index cd224705a..b27da0b23 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/05-react-native.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/05-react-native.md @@ -1,15 +1,14 @@ ---- -title: 开发React-Native应用 -sidebar_position: 50 ---- - -你同样可以使用 alova 开发 React-Native 应用,甚至可以直接使用 GlobalFetch 请求适配器来作为请求事件处理。 - -但是有以下的注意事项: - -## metro 版本 - -在 alova 中的`package.json`中使用了`exports`来定义多个导出项,因此需要确保这两点: - -1. metro 版本高于 0.76.0 -2. 在`metro.config.js`中开启`resolver.unstable_enablePackageExports`。[详情点此查看](https://facebook.github.io/metro/docs/configuration/#unstable_enablepackageexports-experimental) +--- +title: 开发React-Native应用 +--- + +你同样可以使用 alova 开发 React-Native 应用,甚至可以直接使用 GlobalFetch 请求适配器来作为请求事件处理。 + +但是有以下的注意事项: + +## metro 版本 + +在 alova 中的`package.json`中使用了`exports`来定义多个导出项,因此需要确保这两点: + +1. metro 版本高于 0.76.0 +2. 在`metro.config.js`中开启`resolver.unstable_enablePackageExports`。[详情点此查看](https://facebook.github.io/metro/docs/configuration/#unstable_enablepackageexports-experimental) diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/06-use-in-static.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/06-use-in-static.md index e3eb87d6f..b89899892 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/06-use-in-static.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/06-use-in-static.md @@ -1,6 +1,5 @@ --- title: 在静态 html 中使用 -sidebar_position: 60 --- import Tabs from '@theme/Tabs'; @@ -36,7 +35,9 @@ import TabItem from '@theme/TabItem'; Vue.createApp({ setup() { - return alova.useRequest(alovaInstance.Get('https://jsonplaceholder.typicode.com/todos/1')); + return alova.useRequest( + alovaInstance.Get('https://jsonplaceholder.typicode.com/todos/1') + ); } }).mount('#app'); @@ -124,7 +125,9 @@ svelte 依赖于编译工具,不能通过 CDN 直接使用,详情见 [svelte el: '#app', mixins: AlovaVueOptions.mapAlovaHook(function () { return { - todo: alova.useRequest(alovaInstance.Get('https://jsonplaceholder.typicode.com/todos/1')) + todo: alova.useRequest( + alovaInstance.Get('https://jsonplaceholder.typicode.com/todos/1') + ) }; }), data() { diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/07-hide-recommend-tips.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/07-hide-recommend-tips.md index f538b1e3b..c89aa9d67 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/07-hide-recommend-tips.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/07-hide-recommend-tips.md @@ -1,46 +1,45 @@ ---- -title: 隐藏推荐提示 -sidebar_position: 70 ---- - -:::info 版本要求 - -v2.7.0- - -::: - -alova 可以配合扩展库获得更好的开发体验,为了让更多开发者获得更好的开发体验,在使用时将会在控制台中推荐 alova 的扩展。 - -![tips](/img/alova-tips.png) - -这些提示代码将会在构建生产环境包时自动去除,如果你希望在开发环境隐藏它们,可以按以下方式: - -## Vite - -在`.env.development`文件中设置环境变量 **VITE_ALOVA_TIPS=0** - -```bash title=.env.development -VITE_ALOVA_TIPS=0 -``` - -:::warning 警告 -如果信息仍然存在,请尝试将 vite 的依赖缓存删除,它在`node_modules/.vite/deps`。 -::: - -## Webpack - -### Vue - -在`.env.development`文件中设置环境变量 **VUE_APP_ALOVA_TIPS=0** - -```bash title=.env.development -VUE_APP_ALOVA_TIPS=0 -``` - -### React - -在`.env.development`文件中设置环境变量 **REACT_APP_ALOVA_TIPS=0** - -```bash title=.env.development -REACT_APP_ALOVA_TIPS=0 -``` +--- +title: 隐藏推荐提示 +--- + +:::info 版本要求 + +v2.7.0- + +::: + +alova 可以配合扩展库获得更好的开发体验,为了让更多开发者获得更好的开发体验,在使用时将会在控制台中推荐 alova 的扩展。 + +![tips](/img/alova-tips.png) + +这些提示代码将会在构建生产环境包时自动去除,如果你希望在开发环境隐藏它们,可以按以下方式: + +## Vite + +在`.env.development`文件中设置环境变量 **VITE_ALOVA_TIPS=0** + +```bash title=.env.development +VITE_ALOVA_TIPS=0 +``` + +:::warning 警告 +如果信息仍然存在,请尝试将 vite 的依赖缓存删除,它在`node_modules/.vite/deps`。 +::: + +## Webpack + +### Vue + +在`.env.development`文件中设置环境变量 **VUE_APP_ALOVA_TIPS=0** + +```bash title=.env.development +VUE_APP_ALOVA_TIPS=0 +``` + +### React + +在`.env.development`文件中设置环境变量 **REACT_APP_ALOVA_TIPS=0** + +```bash title=.env.development +REACT_APP_ALOVA_TIPS=0 +``` diff --git a/i18n/zh-CN/docusaurus-theme-classic/footer.json b/i18n/zh-CN/docusaurus-theme-classic/footer.json index 37f92d257..32ab342fb 100644 --- a/i18n/zh-CN/docusaurus-theme-classic/footer.json +++ b/i18n/zh-CN/docusaurus-theme-classic/footer.json @@ -1,34 +1,54 @@ -{ - "link.title.Nav": { - "message": "导航", - "description": "The title of the footer links column with title=Nav in the footer" - }, - "link.title.Community": { - "message": "社区", - "description": "The title of the footer links column with title=Community in the footer" - }, - "link.title.More": { - "message": "更多", - "description": "The title of the footer links column with title=More in the footer" - }, - "link.item.label.Docs": { - "message": "文档", - "description": "The label of footer link with label=Guide linking to /tutorial/getting-started" - }, - "link.item.label.Example": { - "message": "示例", - "description": "The label of footer link with label=Guide linking to /tutorial/getting-started/example" - }, - "link.item.label.GitHub": { - "message": "GitHub", - "description": "The label of footer link with label=GitHub linking to https://github.com/alovajs/alova" - }, - "link.item.label.Issues": { - "message": "Issues", - "description": "The label of footer link with label=Issues linking to https://github.com/alovajs/alova/issues" - }, - "link.item.label.Pull request": { - "message": "Pull request", - "description": "The label of footer link with label=Pull request linking to https://github.com/alovajs/alova/pulls" - } -} +{ + "link.title.Document": { + "message": "文档", + "description": "The title of the footer links column with title=Document in the footer" + }, + "link.title.Resource": { + "message": "资源", + "description": "The title of the footer links column with title=Resource in the footer" + }, + "link.title.Community": { + "message": "社区", + "description": "The title of the footer links column with title=Community in the footer" + }, + "link.title.More": { + "message": "更多", + "description": "The title of the footer links column with title=More in the footer" + }, + "link.item.label.Docs": { + "message": "文档", + "description": "The label of footer link with label=Guide linking to /tutorial/getting-started" + }, + "link.item.label.Contributing": { + "message": "贡献指南", + "description": "The label of the footer links column with label=Contributing in the footer" + }, + "link.item.label.Request Adapter": { + "message": "请求适配器", + "description": "The label of the footer links column with label=Request Adapter in the footer" + }, + "link.item.label.Storage Adapter": { + "message": "存储适配器", + "description": "The label of the footer links column with label=Storage Adapter in the footer" + }, + "link.item.label.Framework Support": { + "message": "框架支持", + "description": "The label of the footer links column with label=Framework Support in the footer" + }, + "link.item.label.Example": { + "message": "示例", + "description": "The label of footer link with label=Guide linking to /tutorial/getting-started/example" + }, + "link.item.label.GitHub": { + "message": "GitHub", + "description": "The label of footer link with label=GitHub linking to https://github.com/alovajs/alova" + }, + "link.item.label.Issues": { + "message": "Issues", + "description": "The label of footer link with label=Issues linking to https://github.com/alovajs/alova/issues" + }, + "link.item.label.Pull request": { + "message": "Pull request", + "description": "The label of footer link with label=Pull request linking to https://github.com/alovajs/alova/pulls" + } +} diff --git a/i18n/zh-CN/docusaurus-theme-classic/navbar.json b/i18n/zh-CN/docusaurus-theme-classic/navbar.json index af83c08db..c4b20a56f 100644 --- a/i18n/zh-CN/docusaurus-theme-classic/navbar.json +++ b/i18n/zh-CN/docusaurus-theme-classic/navbar.json @@ -19,6 +19,26 @@ "message": "关于", "description": "Navbar item with label About" }, + "item.label.Getting Started": { + "message": "开始", + "description": "Navbar item with label Getting Started" + }, + "item.label.Request Adapter": { + "message": "请求适配器", + "description": "Navbar item with label Request Adapter" + }, + "item.label.Storage Adapter": { + "message": "存储适配器", + "description": "Navbar item with label Storage Adapter" + }, + "item.label.UI Frameworks": { + "message": "框架支持", + "description": "Navbar item with label UI Frameworks" + }, + "item.label.Error Reference": { + "message": "错误码对照", + "description": "Navbar item with label Error Reference" + }, "item.label.Contributing Guidelines": { "message": "贡献指南", "description": "Navbar item with label Contributing Guidelines" diff --git a/package-lock.json b/package-lock.json index a644bff91..733377739 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,27547 +1,27745 @@ -{ - "name": "alova-website", - "version": "1.0.0", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "alova-website", - "version": "1.0.0", - "dependencies": { - "@codesandbox/sandpack-react": "^2.9.0", - "@codesandbox/sandpack-themes": "^2.0.21", - "@docusaurus/core": "^3.4.0", - "@docusaurus/preset-classic": "^3.4.0", - "@docusaurus/theme-mermaid": "^3.4.0", - "@docusaurus/tsconfig": "^3.4.0", - "@mdx-js/react": "^3.0.0", - "clsx": "^2.0.0", - "prism-react-renderer": "^2.1.0", - "raw-loader": "^4.0.2", - "react": "^18.2.0", - "react-dom": "^18.2.0" - }, - "devDependencies": { - "@docusaurus/module-type-aliases": "^3.1.1", - "prettier": "^2.8.8", - "typescript": "^5.2.2" - }, - "engines": { - "node": ">=16.14" - } - }, - "node_modules/@algolia/autocomplete-core": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.9.3.tgz", - "integrity": "sha512-009HdfugtGCdC4JdXUbVJClA0q0zh24yyePn+KUGk3rP7j8FEe/m5Yo/z65gn6nP/cM39PxpzqKrL7A6fP6PPw==", - "dependencies": { - "@algolia/autocomplete-plugin-algolia-insights": "1.9.3", - "@algolia/autocomplete-shared": "1.9.3" - } - }, - "node_modules/@algolia/autocomplete-plugin-algolia-insights": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.9.3.tgz", - "integrity": "sha512-a/yTUkcO/Vyy+JffmAnTWbr4/90cLzw+CC3bRbhnULr/EM0fGNvM13oQQ14f2moLMcVDyAx/leczLlAOovhSZg==", - "dependencies": { - "@algolia/autocomplete-shared": "1.9.3" - }, - "peerDependencies": { - "search-insights": ">= 1 < 3" - } - }, - "node_modules/@algolia/autocomplete-preset-algolia": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.9.3.tgz", - "integrity": "sha512-d4qlt6YmrLMYy95n5TB52wtNDr6EgAIPH81dvvvW8UmuWRgxEtY0NJiPwl/h95JtG2vmRM804M0DSwMCNZlzRA==", - "dependencies": { - "@algolia/autocomplete-shared": "1.9.3" - }, - "peerDependencies": { - "@algolia/client-search": ">= 4.9.1 < 6", - "algoliasearch": ">= 4.9.1 < 6" - } - }, - "node_modules/@algolia/autocomplete-shared": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.9.3.tgz", - "integrity": "sha512-Wnm9E4Ye6Rl6sTTqjoymD+l8DjSTHsHboVRYrKgEt8Q7UHm9nYbqhN/i0fhUYA3OAEH7WA8x3jfpnmJm3rKvaQ==", - "peerDependencies": { - "@algolia/client-search": ">= 4.9.1 < 6", - "algoliasearch": ">= 4.9.1 < 6" - } - }, - "node_modules/@algolia/cache-browser-local-storage": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.23.3.tgz", - "integrity": "sha512-vRHXYCpPlTDE7i6UOy2xE03zHF2C8MEFjPN2v7fRbqVpcOvAUQK81x3Kc21xyb5aSIpYCjWCZbYZuz8Glyzyyg==", - "dependencies": { - "@algolia/cache-common": "4.23.3" - } - }, - "node_modules/@algolia/cache-common": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.23.3.tgz", - "integrity": "sha512-h9XcNI6lxYStaw32pHpB1TMm0RuxphF+Ik4o7tcQiodEdpKK+wKufY6QXtba7t3k8eseirEMVB83uFFF3Nu54A==" - }, - "node_modules/@algolia/cache-in-memory": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.23.3.tgz", - "integrity": "sha512-yvpbuUXg/+0rbcagxNT7un0eo3czx2Uf0y4eiR4z4SD7SiptwYTpbuS0IHxcLHG3lq22ukx1T6Kjtk/rT+mqNg==", - "dependencies": { - "@algolia/cache-common": "4.23.3" - } - }, - "node_modules/@algolia/client-account": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.23.3.tgz", - "integrity": "sha512-hpa6S5d7iQmretHHF40QGq6hz0anWEHGlULcTIT9tbUssWUriN9AUXIFQ8Ei4w9azD0hc1rUok9/DeQQobhQMA==", - "dependencies": { - "@algolia/client-common": "4.23.3", - "@algolia/client-search": "4.23.3", - "@algolia/transporter": "4.23.3" - } - }, - "node_modules/@algolia/client-analytics": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.23.3.tgz", - "integrity": "sha512-LBsEARGS9cj8VkTAVEZphjxTjMVCci+zIIiRhpFun9jGDUlS1XmhCW7CTrnaWeIuCQS/2iPyRqSy1nXPjcBLRA==", - "dependencies": { - "@algolia/client-common": "4.23.3", - "@algolia/client-search": "4.23.3", - "@algolia/requester-common": "4.23.3", - "@algolia/transporter": "4.23.3" - } - }, - "node_modules/@algolia/client-common": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.23.3.tgz", - "integrity": "sha512-l6EiPxdAlg8CYhroqS5ybfIczsGUIAC47slLPOMDeKSVXYG1n0qGiz4RjAHLw2aD0xzh2EXZ7aRguPfz7UKDKw==", - "dependencies": { - "@algolia/requester-common": "4.23.3", - "@algolia/transporter": "4.23.3" - } - }, - "node_modules/@algolia/client-personalization": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.23.3.tgz", - "integrity": "sha512-3E3yF3Ocr1tB/xOZiuC3doHQBQ2zu2MPTYZ0d4lpfWads2WTKG7ZzmGnsHmm63RflvDeLK/UVx7j2b3QuwKQ2g==", - "dependencies": { - "@algolia/client-common": "4.23.3", - "@algolia/requester-common": "4.23.3", - "@algolia/transporter": "4.23.3" - } - }, - "node_modules/@algolia/client-search": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.23.3.tgz", - "integrity": "sha512-P4VAKFHqU0wx9O+q29Q8YVuaowaZ5EM77rxfmGnkHUJggh28useXQdopokgwMeYw2XUht49WX5RcTQ40rZIabw==", - "dependencies": { - "@algolia/client-common": "4.23.3", - "@algolia/requester-common": "4.23.3", - "@algolia/transporter": "4.23.3" - } - }, - "node_modules/@algolia/events": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@algolia/events/-/events-4.0.1.tgz", - "integrity": "sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ==" - }, - "node_modules/@algolia/logger-common": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.23.3.tgz", - "integrity": "sha512-y9kBtmJwiZ9ZZ+1Ek66P0M68mHQzKRxkW5kAAXYN/rdzgDN0d2COsViEFufxJ0pb45K4FRcfC7+33YB4BLrZ+g==" - }, - "node_modules/@algolia/logger-console": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.23.3.tgz", - "integrity": "sha512-8xoiseoWDKuCVnWP8jHthgaeobDLolh00KJAdMe9XPrWPuf1by732jSpgy2BlsLTaT9m32pHI8CRfrOqQzHv3A==", - "dependencies": { - "@algolia/logger-common": "4.23.3" - } - }, - "node_modules/@algolia/recommend": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-4.23.3.tgz", - "integrity": "sha512-9fK4nXZF0bFkdcLBRDexsnGzVmu4TSYZqxdpgBW2tEyfuSSY54D4qSRkLmNkrrz4YFvdh2GM1gA8vSsnZPR73w==", - "dependencies": { - "@algolia/cache-browser-local-storage": "4.23.3", - "@algolia/cache-common": "4.23.3", - "@algolia/cache-in-memory": "4.23.3", - "@algolia/client-common": "4.23.3", - "@algolia/client-search": "4.23.3", - "@algolia/logger-common": "4.23.3", - "@algolia/logger-console": "4.23.3", - "@algolia/requester-browser-xhr": "4.23.3", - "@algolia/requester-common": "4.23.3", - "@algolia/requester-node-http": "4.23.3", - "@algolia/transporter": "4.23.3" - } - }, - "node_modules/@algolia/requester-browser-xhr": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.23.3.tgz", - "integrity": "sha512-jDWGIQ96BhXbmONAQsasIpTYWslyjkiGu0Quydjlowe+ciqySpiDUrJHERIRfELE5+wFc7hc1Q5hqjGoV7yghw==", - "dependencies": { - "@algolia/requester-common": "4.23.3" - } - }, - "node_modules/@algolia/requester-common": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.23.3.tgz", - "integrity": "sha512-xloIdr/bedtYEGcXCiF2muajyvRhwop4cMZo+K2qzNht0CMzlRkm8YsDdj5IaBhshqfgmBb3rTg4sL4/PpvLYw==" - }, - "node_modules/@algolia/requester-node-http": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.23.3.tgz", - "integrity": "sha512-zgu++8Uj03IWDEJM3fuNl34s746JnZOWn1Uz5taV1dFyJhVM/kTNw9Ik7YJWiUNHJQXcaD8IXD1eCb0nq/aByA==", - "dependencies": { - "@algolia/requester-common": "4.23.3" - } - }, - "node_modules/@algolia/transporter": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.23.3.tgz", - "integrity": "sha512-Wjl5gttqnf/gQKJA+dafnD0Y6Yw97yvfY8R9h0dQltX1GXTgNs1zWgvtWW0tHl1EgMdhAyw189uWiZMnL3QebQ==", - "dependencies": { - "@algolia/cache-common": "4.23.3", - "@algolia/logger-common": "4.23.3", - "@algolia/requester-common": "4.23.3" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", - "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", - "dependencies": { - "@babel/highlight": "^7.23.4", - "chalk": "^2.4.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/code-frame/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/code-frame/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/code-frame/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.2.tgz", - "integrity": "sha512-0S9TQMmDHlqAZ2ITT95irXKfxN9bncq8ZCoJhun3nHL/lLUxd2NKBJYoNGWH7S0hz6fRQwWlAWn/ILM0C70KZQ==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.5.tgz", - "integrity": "sha512-Cwc2XjUrG4ilcfOw4wBAK+enbdgwAcAJCfGUItPBKR7Mjw4aEfAFYrLxeRp4jWgtNIKn3n2AlBOfwwafl+42/g==", - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.5", - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.23.5", - "@babel/parser": "^7.23.5", - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.5", - "@babel/types": "^7.23.5", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/generator": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.5.tgz", - "integrity": "sha512-BPssCHrBD+0YrxviOa3QzpqwhNIXKEtOa2jQrm4FlmkC2apYgRnQcmPWiGZDlGxiNtltnUFolMe8497Esry+jA==", - "dependencies": { - "@babel/types": "^7.23.5", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", - "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz", - "integrity": "sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==", - "dependencies": { - "@babel/types": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", - "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", - "dependencies": { - "@babel/compat-data": "^7.22.9", - "@babel/helper-validator-option": "^7.22.15", - "browserslist": "^4.21.9", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.15.tgz", - "integrity": "sha512-jKkwA59IXcvSaiK2UN45kKwSC9o+KuoXsBDvHvU/7BecYIp8GQ2UwrVvFgJASUT+hBnwJx6MhvMCuMzwZZ7jlg==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", - "@babel/helper-member-expression-to-functions": "^7.22.15", - "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz", - "integrity": "sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "regexpu-core": "^5.3.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.3.tgz", - "integrity": "sha512-WBrLmuPP47n7PNwsZ57pqam6G/RGo1vw/87b0Blc53tZNGZ4x7YvZ6HgQe2vo1W/FR20OgjeZuGXzudPiXHFug==", - "dependencies": { - "@babel/helper-compilation-targets": "^7.22.6", - "@babel/helper-plugin-utils": "^7.22.5", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", - "dependencies": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz", - "integrity": "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==", - "dependencies": { - "@babel/types": "^7.23.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", - "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", - "dependencies": { - "@babel/types": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", - "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", - "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.20" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", - "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.7.tgz", - "integrity": "sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz", - "integrity": "sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-wrap-function": "^7.22.20" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz", - "integrity": "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==", - "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-member-expression-to-functions": "^7.22.15", - "@babel/helper-optimise-call-expression": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", - "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", - "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", - "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-wrap-function": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz", - "integrity": "sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==", - "dependencies": { - "@babel/helper-function-name": "^7.22.5", - "@babel/template": "^7.22.15", - "@babel/types": "^7.22.19" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.5.tgz", - "integrity": "sha512-oO7us8FzTEsG3U6ag9MfdF1iA/7Z6dz+MtFhifZk8C8o453rGJFFWUP1t+ULM9TUIAzC9uxXEiXjOiVMyd7QPg==", - "dependencies": { - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.5", - "@babel/types": "^7.23.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", - "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", - "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/parser": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.5.tgz", - "integrity": "sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ==", - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.15.tgz", - "integrity": "sha512-FB9iYlz7rURmRJyXRKEnalYPPdn87H5no108cyuQQyMwlpJ2SJtpIUBI27kdTin956pz+LPypkPVPUTlxOmrsg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.15.tgz", - "integrity": "sha512-Hyph9LseGvAeeXzikV88bczhsrLrIZqDPxO+sSmAunMPaGrBGhfMWzCPYTtiW9t+HzSE2wtV8e5cc5P6r1xMDQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/plugin-transform-optional-chaining": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.13.0" - } - }, - "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.0-placeholder-for-preset-env.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", - "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.22.5.tgz", - "integrity": "sha512-rdV97N7KqsRzeNGoWUOK6yUsWarLjE5Su/Snk9IYPU9CwkWHs4t+rTGOvffTR8XGkJMTAdLfO0xVnXm8wugIJg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.22.5.tgz", - "integrity": "sha512-KwvoWDeNKPETmozyFE0P2rOLqh39EoQHNjqizrI5B8Vt0ZNS7M56s7dAiAqbYfiAYOuIzIh96z3iR2ktgu3tEg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", - "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", - "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-unicode-sets-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", - "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.22.5.tgz", - "integrity": "sha512-26lTNXoVRdAnsaDXPpvCNUq+OVWEVC6bx7Vvz9rC53F2bagUWW4u4ii2+h8Fejfh7RYqPxn+libeFBBck9muEw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.2.tgz", - "integrity": "sha512-BBYVGxbDVHfoeXbOwcagAkOQAm9NxoTdMGfTqghu1GrvadSaw6iW3Je6IcL5PNOw8VwjxqBECXy50/iCQSY/lQ==", - "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-remap-async-to-generator": "^7.22.20", - "@babel/plugin-syntax-async-generators": "^7.8.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.22.5.tgz", - "integrity": "sha512-b1A8D8ZzE/VhNDoV1MSJTnpKkCG5bJo+19R4o4oy03zM7ws8yEMK755j61Dc3EyvdysbqH5BOOTquJ7ZX9C6vQ==", - "dependencies": { - "@babel/helper-module-imports": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-remap-async-to-generator": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.22.5.tgz", - "integrity": "sha512-tdXZ2UdknEKQWKJP1KMNmuF5Lx3MymtMN/pvA+p/VEkhK8jVcQ1fzSy8KM9qRYhAf2/lV33hoMPKI/xaI9sADA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.0.tgz", - "integrity": "sha512-cOsrbmIOXmf+5YbL99/S49Y3j46k/T16b9ml8bm9lP6N9US5iQ2yBK7gpui1pg0V/WMcXdkfKbTb7HXq9u+v4g==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.22.5.tgz", - "integrity": "sha512-nDkQ0NfkOhPTq8YCLiWNxp1+f9fCobEjCb0n8WdbNUBc4IB5V7P1QnX9IjpSoquKrXF5SKojHleVNs2vGeHCHQ==", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.11.tgz", - "integrity": "sha512-GMM8gGmqI7guS/llMFk1bJDkKfn3v3C4KHK9Yg1ey5qcHcOlKb0QvcMrgzvxo+T03/4szNh5lghY+fEC98Kq9g==", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.11", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-class-static-block": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.12.0" - } - }, - "node_modules/@babel/plugin-transform-classes": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.15.tgz", - "integrity": "sha512-VbbC3PGjBdE0wAWDdHM9G8Gm977pnYI0XpqMd6LrKISj8/DJXEsWqgRuTYaNE9Bv0JGhTZUzHDlMk18IpOuoqw==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", - "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.9", - "@babel/helper-split-export-declaration": "^7.22.6", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.22.5.tgz", - "integrity": "sha512-4GHWBgRf0krxPX+AaPtgBAlTgTeZmqDynokHOX7aqqAB4tHs3U2Y02zH6ETFdLZGcg9UQSD1WCmkVrE9ErHeOg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/template": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.0.tgz", - "integrity": "sha512-vaMdgNXFkYrB+8lbgniSYWHsgqK5gjaMNcc84bMIOMRLH0L9AqYq3hwMdvnyqj1OPqea8UtjPEuS/DCenah1wg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.22.5.tgz", - "integrity": "sha512-5/Yk9QxCQCl+sOIB1WelKnVRxTJDSAIxtJLL2/pqL14ZVlbH0fUQUZa/T5/UnQtBNgghR7mfB8ERBKyKPCi7Vw==", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.22.5.tgz", - "integrity": "sha512-dEnYD+9BBgld5VBXHnF/DbYGp3fqGMsyxKbtD1mDyIA7AkTSpKXFhCVuj/oQVOoALfBs77DudA0BE4d5mcpmqw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.11.tgz", - "integrity": "sha512-g/21plo58sfteWjaO0ZNVb+uEOkJNjAaHhbejrnBmu011l/eNDScmkbjCC3l4FKb10ViaGU4aOkFznSu2zRHgA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.22.5.tgz", - "integrity": "sha512-vIpJFNM/FjZ4rh1myqIya9jXwrwwgFRHPjT3DkUA9ZLHuzox8jiXkOLvwm1H+PQIP3CqfC++WPKeuDi0Sjdj1g==", - "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.11.tgz", - "integrity": "sha512-xa7aad7q7OiT8oNZ1mU7NrISjlSkVdMbNxn9IuLZyL9AJEhs1Apba3I+u5riX1dIkdptP5EKDG5XDPByWxtehw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-for-of": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.15.tgz", - "integrity": "sha512-me6VGeHsx30+xh9fbDLLPi0J1HzmeIIyenoOQHuw2D4m2SAU3NrspX5XxJLBpqn5yrLzrlw2Iy3RA//Bx27iOA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-function-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.22.5.tgz", - "integrity": "sha512-UIzQNMS0p0HHiQm3oelztj+ECwFnj+ZRV4KnguvlsD2of1whUeM6o7wGNj6oLwcDoAXQ8gEqfgC24D+VdIcevg==", - "dependencies": { - "@babel/helper-compilation-targets": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.11.tgz", - "integrity": "sha512-CxT5tCqpA9/jXFlme9xIBCc5RPtdDq3JpkkhgHQqtDdiTnTI0jtZ0QzXhr5DILeYifDPp2wvY2ad+7+hLMW5Pw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-json-strings": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-literals": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.22.5.tgz", - "integrity": "sha512-fTLj4D79M+mepcw3dgFBTIDYpbcB9Sm0bpm4ppXPaO+U+PKFFyV9MGRvS0gvGw62sd10kT5lRMKXAADb9pWy8g==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.11.tgz", - "integrity": "sha512-qQwRTP4+6xFCDV5k7gZBF3C31K34ut0tbEcTKxlX/0KXxm9GLcO14p570aWxFvVzx6QAfPgq7gaeIHXJC8LswQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.22.5.tgz", - "integrity": "sha512-RZEdkNtzzYCFl9SE9ATaUMTj2hqMb4StarOJLrZRbqqU4HSBE7UlBw9WBWQiDzrJZJdUWiMTVDI6Gv/8DPvfew==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.23.0.tgz", - "integrity": "sha512-xWT5gefv2HGSm4QHtgc1sYPbseOyf+FFDo2JbpE25GWl5BqTGO9IMwTYJRoIdjsF85GE+VegHxSCUt5EvoYTAw==", - "dependencies": { - "@babel/helper-module-transforms": "^7.23.0", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.0.tgz", - "integrity": "sha512-32Xzss14/UVc7k9g775yMIvkVK8xwKE0DPdP5JTapr3+Z9w4tzeOuLNY6BXDQR6BdnzIlXnCGAzsk/ICHBLVWQ==", - "dependencies": { - "@babel/helper-module-transforms": "^7.23.0", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-simple-access": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.0.tgz", - "integrity": "sha512-qBej6ctXZD2f+DhlOC9yO47yEYgUh5CZNz/aBoH4j/3NOlRfJXJbY7xDQCqQVf9KbrqGzIWER1f23doHGrIHFg==", - "dependencies": { - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-module-transforms": "^7.23.0", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.20" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.22.5.tgz", - "integrity": "sha512-+S6kzefN/E1vkSsKx8kmQuqeQsvCKCd1fraCM7zXm4SFoggI099Tr4G8U81+5gtMdUeMQ4ipdQffbKLX0/7dBQ==", - "dependencies": { - "@babel/helper-module-transforms": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz", - "integrity": "sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-new-target": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.22.5.tgz", - "integrity": "sha512-AsF7K0Fx/cNKVyk3a+DW0JLo+Ua598/NxMRvxDnkpCIGFh43+h/v2xyhRUYf6oD8gE4QtL83C7zZVghMjHd+iw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.11.tgz", - "integrity": "sha512-YZWOw4HxXrotb5xsjMJUDlLgcDXSfO9eCmdl1bgW4+/lAGdkjaEvOnQ4p5WKKdUgSzO39dgPl0pTnfxm0OAXcg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.11.tgz", - "integrity": "sha512-3dzU4QGPsILdJbASKhF/V2TVP+gJya1PsueQCxIPCEcerqF21oEcrob4mzjsp2Py/1nLfF5m+xYNMDpmA8vffg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.15.tgz", - "integrity": "sha512-fEB+I1+gAmfAyxZcX1+ZUwLeAuuf8VIg67CTznZE0MqVFumWkh8xWtn58I4dxdVf080wn7gzWoF8vndOViJe9Q==", - "dependencies": { - "@babel/compat-data": "^7.22.9", - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-object-super": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.22.5.tgz", - "integrity": "sha512-klXqyaT9trSjIUrcsYIfETAzmOEZL3cBYqOYLJxBHfMFFggmXOv+NYSX/Jbs9mzMVESw/WycLFPRx8ba/b2Ipw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.11.tgz", - "integrity": "sha512-rli0WxesXUeCJnMYhzAglEjLWVDF6ahb45HuprcmQuLidBJFWjNnOzssk2kuc6e33FlLaiZhG/kUIzUMWdBKaQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.0.tgz", - "integrity": "sha512-sBBGXbLJjxTzLBF5rFWaikMnOGOk/BmK6vVByIdEggZ7Vn6CvWXZyRkkLFK6WE0IF8jSliyOkUN6SScFgzCM0g==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-parameters": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.15.tgz", - "integrity": "sha512-hjk7qKIqhyzhhUvRT683TYQOFa/4cQKwQy7ALvTpODswN40MljzNDa0YldevS6tGbxwaEKVn502JmY0dP7qEtQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.22.5.tgz", - "integrity": "sha512-PPjh4gyrQnGe97JTalgRGMuU4icsZFnWkzicB/fUtzlKUqvsWBKEpPPfr5a2JiyirZkHxnAqkQMO5Z5B2kK3fA==", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.11.tgz", - "integrity": "sha512-sSCbqZDBKHetvjSwpyWzhuHkmW5RummxJBVbYLkGkaiTOWGxml7SXt0iWa03bzxFIx7wOj3g/ILRd0RcJKBeSQ==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.22.11", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.22.5.tgz", - "integrity": "sha512-TiOArgddK3mK/x1Qwf5hay2pxI6wCZnvQqrFSqbtg1GLl2JcNMitVH/YnqjP+M31pLUeTfzY1HAXFDnUBV30rQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-constant-elements": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.24.7.tgz", - "integrity": "sha512-7LidzZfUXyfZ8/buRW6qIIHBY8wAZ1OrY9c/wTr8YhZ6vMPo+Uc/CVFLYY1spZrEQlD4w5u8wjqk5NQ3OVqQKA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-display-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.22.5.tgz", - "integrity": "sha512-PVk3WPYudRF5z4GKMEYUrLjPl38fJSKNaEOkFuoprioowGuWN6w2RKznuFNSlJx7pzzXXStPUnNSOEO0jL5EVw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.22.15.tgz", - "integrity": "sha512-oKckg2eZFa8771O/5vi7XeTvmM6+O9cxZu+kanTU7tD4sin5nO/G8jGJhq8Hvt2Z0kUoEDRayuZLaUlYl8QuGA==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-jsx": "^7.22.5", - "@babel/types": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-development": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.22.5.tgz", - "integrity": "sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A==", - "dependencies": { - "@babel/plugin-transform-react-jsx": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-pure-annotations": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.22.5.tgz", - "integrity": "sha512-gP4k85wx09q+brArVinTXhWiyzLl9UpmGva0+mWyKxk6JZequ05x3eUcIUE+FyttPKJFRRVtAvQaJ6YF9h1ZpA==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.10.tgz", - "integrity": "sha512-F28b1mDt8KcT5bUyJc/U9nwzw6cV+UmTeRlXYIl2TNqMMJif0Jeey9/RQ3C4NOd2zp0/TRsDns9ttj2L523rsw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "regenerator-transform": "^0.15.2" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.22.5.tgz", - "integrity": "sha512-DTtGKFRQUDm8svigJzZHzb/2xatPc6TzNvAIJ5GqOKDsGFYgAskjRulbR/vGsPKq3OPqtexnz327qYpP57RFyA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-runtime": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.23.2.tgz", - "integrity": "sha512-XOntj6icgzMS58jPVtQpiuF6ZFWxQiJavISGx5KGjRj+3gqZr8+N6Kx+N9BApWzgS+DOjIZfXXj0ZesenOWDyA==", - "dependencies": { - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "babel-plugin-polyfill-corejs2": "^0.4.6", - "babel-plugin-polyfill-corejs3": "^0.8.5", - "babel-plugin-polyfill-regenerator": "^0.5.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.22.5.tgz", - "integrity": "sha512-vM4fq9IXHscXVKzDv5itkO1X52SmdFBFcMIBZ2FRn2nqVYqw6dBexUgMvAjHW+KXpPPViD/Yo3GrDEBaRC0QYA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-spread": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.22.5.tgz", - "integrity": "sha512-5ZzDQIGyvN4w8+dMmpohL6MBo+l2G7tfC/O2Dg7/hjpgeWvUx8FzfeOKxGog9IimPa4YekaQ9PlDqTLOljkcxg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.22.5.tgz", - "integrity": "sha512-zf7LuNpHG0iEeiyCNwX4j3gDg1jgt1k3ZdXBKbZSoA3BbGQGvMiSvfbZRR3Dr3aeJe3ooWFZxOOG3IRStYp2Bw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.22.5.tgz", - "integrity": "sha512-5ciOehRNf+EyUeewo8NkbQiUs4d6ZxiHo6BcBcnFlgiJfu16q0bQUw9Jvo0b0gBKFG1SMhDSjeKXSYuJLeFSMA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.22.5.tgz", - "integrity": "sha512-bYkI5lMzL4kPii4HHEEChkD0rkc+nvnlR6+o/qdqR6zrm0Sv/nodmyLhlq2DO0YKLUNd2VePmPRjJXSBh9OIdA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-typescript": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.15.tgz", - "integrity": "sha512-1uirS0TnijxvQLnlv5wQBwOX3E1wCFX7ITv+9pBV2wKEk4K+M5tqDaoNXnTH8tjEIYHLO98MwiTWO04Ggz4XuA==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-typescript": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.10.tgz", - "integrity": "sha512-lRfaRKGZCBqDlRU3UIFovdp9c9mEvlylmpod0/OatICsSfuQ9YFthRo1tpTkGsklEefZdqlEFdY4A2dwTb6ohg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.22.5.tgz", - "integrity": "sha512-HCCIb+CbJIAE6sXn5CjFQXMwkCClcOfPCzTlilJ8cUatfzwHlWQkbtV0zD338u9dZskwvuOYTuuaMaA8J5EI5A==", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.22.5.tgz", - "integrity": "sha512-028laaOKptN5vHJf9/Arr/HiJekMd41hOEZYvNsrsXqJ7YPYuX2bQxh31fkZzGmq3YqHRJzYFFAVYvKfMPKqyg==", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.22.5.tgz", - "integrity": "sha512-lhMfi4FC15j13eKrh3DnYHjpGj6UKQHtNKTbtc1igvAhRy4+kLhV07OpLcsN0VgDEw/MjAvJO4BdMJsHwMhzCg==", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/preset-env": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.23.2.tgz", - "integrity": "sha512-BW3gsuDD+rvHL2VO2SjAUNTBe5YrjsTiDyqamPDWY723na3/yPQ65X5oQkFVJZ0o50/2d+svm1rkPoJeR1KxVQ==", - "dependencies": { - "@babel/compat-data": "^7.23.2", - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-option": "^7.22.15", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.22.15", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.22.15", - "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.22.5", - "@babel/plugin-syntax-import-attributes": "^7.22.5", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", - "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.22.5", - "@babel/plugin-transform-async-generator-functions": "^7.23.2", - "@babel/plugin-transform-async-to-generator": "^7.22.5", - "@babel/plugin-transform-block-scoped-functions": "^7.22.5", - "@babel/plugin-transform-block-scoping": "^7.23.0", - "@babel/plugin-transform-class-properties": "^7.22.5", - "@babel/plugin-transform-class-static-block": "^7.22.11", - "@babel/plugin-transform-classes": "^7.22.15", - "@babel/plugin-transform-computed-properties": "^7.22.5", - "@babel/plugin-transform-destructuring": "^7.23.0", - "@babel/plugin-transform-dotall-regex": "^7.22.5", - "@babel/plugin-transform-duplicate-keys": "^7.22.5", - "@babel/plugin-transform-dynamic-import": "^7.22.11", - "@babel/plugin-transform-exponentiation-operator": "^7.22.5", - "@babel/plugin-transform-export-namespace-from": "^7.22.11", - "@babel/plugin-transform-for-of": "^7.22.15", - "@babel/plugin-transform-function-name": "^7.22.5", - "@babel/plugin-transform-json-strings": "^7.22.11", - "@babel/plugin-transform-literals": "^7.22.5", - "@babel/plugin-transform-logical-assignment-operators": "^7.22.11", - "@babel/plugin-transform-member-expression-literals": "^7.22.5", - "@babel/plugin-transform-modules-amd": "^7.23.0", - "@babel/plugin-transform-modules-commonjs": "^7.23.0", - "@babel/plugin-transform-modules-systemjs": "^7.23.0", - "@babel/plugin-transform-modules-umd": "^7.22.5", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", - "@babel/plugin-transform-new-target": "^7.22.5", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.11", - "@babel/plugin-transform-numeric-separator": "^7.22.11", - "@babel/plugin-transform-object-rest-spread": "^7.22.15", - "@babel/plugin-transform-object-super": "^7.22.5", - "@babel/plugin-transform-optional-catch-binding": "^7.22.11", - "@babel/plugin-transform-optional-chaining": "^7.23.0", - "@babel/plugin-transform-parameters": "^7.22.15", - "@babel/plugin-transform-private-methods": "^7.22.5", - "@babel/plugin-transform-private-property-in-object": "^7.22.11", - "@babel/plugin-transform-property-literals": "^7.22.5", - "@babel/plugin-transform-regenerator": "^7.22.10", - "@babel/plugin-transform-reserved-words": "^7.22.5", - "@babel/plugin-transform-shorthand-properties": "^7.22.5", - "@babel/plugin-transform-spread": "^7.22.5", - "@babel/plugin-transform-sticky-regex": "^7.22.5", - "@babel/plugin-transform-template-literals": "^7.22.5", - "@babel/plugin-transform-typeof-symbol": "^7.22.5", - "@babel/plugin-transform-unicode-escapes": "^7.22.10", - "@babel/plugin-transform-unicode-property-regex": "^7.22.5", - "@babel/plugin-transform-unicode-regex": "^7.22.5", - "@babel/plugin-transform-unicode-sets-regex": "^7.22.5", - "@babel/preset-modules": "0.1.6-no-external-plugins", - "@babel/types": "^7.23.0", - "babel-plugin-polyfill-corejs2": "^0.4.6", - "babel-plugin-polyfill-corejs3": "^0.8.5", - "babel-plugin-polyfill-regenerator": "^0.5.3", - "core-js-compat": "^3.31.0", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-env/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/preset-modules": { - "version": "0.1.6-no-external-plugins", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", - "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/@babel/preset-react": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.22.15.tgz", - "integrity": "sha512-Csy1IJ2uEh/PecCBXXoZGAZBeCATTuePzCSB7dLYWS0vOEj6CNpjxIhW4duWwZodBNueH7QO14WbGn8YyeuN9w==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-option": "^7.22.15", - "@babel/plugin-transform-react-display-name": "^7.22.5", - "@babel/plugin-transform-react-jsx": "^7.22.15", - "@babel/plugin-transform-react-jsx-development": "^7.22.5", - "@babel/plugin-transform-react-pure-annotations": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-typescript": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.23.2.tgz", - "integrity": "sha512-u4UJc1XsS1GhIGteM8rnGiIvf9rJpiVgMEeCnwlLA7WJPC+jcXWJAGxYmeqs5hOZD8BbAfnV5ezBOxQbb4OUxA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-option": "^7.22.15", - "@babel/plugin-syntax-jsx": "^7.22.5", - "@babel/plugin-transform-modules-commonjs": "^7.23.0", - "@babel/plugin-transform-typescript": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/regjsgen": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" - }, - "node_modules/@babel/runtime": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz", - "integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/runtime-corejs3": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.23.2.tgz", - "integrity": "sha512-54cIh74Z1rp4oIjsHjqN+WM4fMyCBYe+LpZ9jWm51CZ1fbH3SkAzQD/3XLoNkjbJ7YEmjobLXyvQrFypRHOrXw==", - "dependencies": { - "core-js-pure": "^3.30.2", - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", - "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.5.tgz", - "integrity": "sha512-czx7Xy5a6sapWWRx61m1Ke1Ra4vczu1mCTtJam5zRTBOonfdJ+S/B6HYmGYu3fJtr8GGET3si6IhgWVBhJ/m8w==", - "dependencies": { - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.5", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.5", - "@babel/types": "^7.23.5", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.5.tgz", - "integrity": "sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w==", - "dependencies": { - "@babel/helper-string-parser": "^7.23.4", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@braintree/sanitize-url": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-6.0.4.tgz", - "integrity": "sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A==" - }, - "node_modules/@codemirror/autocomplete": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.10.2.tgz", - "integrity": "sha512-3dCL7b0j2GdtZzWN5j7HDpRAJ26ip07R4NGYz7QYthIYMiX8I4E4TNrYcdTayPJGeVQtd/xe7lWU4XL7THFb/w==", - "dependencies": { - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.17.0", - "@lezer/common": "^1.0.0" - }, - "peerDependencies": { - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "@lezer/common": "^1.0.0" - } - }, - "node_modules/@codemirror/commands": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.3.0.tgz", - "integrity": "sha512-tFfcxRIlOWiQDFhjBSWJ10MxcvbCIsRr6V64SgrcaY0MwNk32cUOcCuNlWo8VjV4qRQCgNgUAnIeo0svkk4R5Q==", - "dependencies": { - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.2.0", - "@codemirror/view": "^6.0.0", - "@lezer/common": "^1.1.0" - } - }, - "node_modules/@codemirror/lang-css": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.2.1.tgz", - "integrity": "sha512-/UNWDNV5Viwi/1lpr/dIXJNWiwDxpw13I4pTUAsNxZdg6E0mI2kTQb0P2iHczg1Tu+H4EBgJR+hYhKiHKko7qg==", - "dependencies": { - "@codemirror/autocomplete": "^6.0.0", - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@lezer/common": "^1.0.2", - "@lezer/css": "^1.0.0" - } - }, - "node_modules/@codemirror/lang-html": { - "version": "6.4.6", - "resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.6.tgz", - "integrity": "sha512-E4C8CVupBksXvgLSme/zv31x91g06eZHSph7NczVxZW+/K+3XgJGWNT//2WLzaKSBoxpAjaOi5ZnPU1SHhjh3A==", - "dependencies": { - "@codemirror/autocomplete": "^6.0.0", - "@codemirror/lang-css": "^6.0.0", - "@codemirror/lang-javascript": "^6.0.0", - "@codemirror/language": "^6.4.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.17.0", - "@lezer/common": "^1.0.0", - "@lezer/css": "^1.1.0", - "@lezer/html": "^1.3.0" - } - }, - "node_modules/@codemirror/lang-javascript": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.1.tgz", - "integrity": "sha512-jlFOXTejVyiQCW3EQwvKH0m99bUYIw40oPmFjSX2VS78yzfe0HELZ+NEo9Yfo1MkGRpGlj3Gnu4rdxV1EnAs5A==", - "dependencies": { - "@codemirror/autocomplete": "^6.0.0", - "@codemirror/language": "^6.6.0", - "@codemirror/lint": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.17.0", - "@lezer/common": "^1.0.0", - "@lezer/javascript": "^1.0.0" - } - }, - "node_modules/@codemirror/language": { - "version": "6.9.2", - "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.9.2.tgz", - "integrity": "sha512-QGTQXSpAKDIzaSE96zNK1UfIUhPgkT1CLjh1N5qVzZuxgsEOhz5RqaN8QCIdyOQklGLx3MgHd9YrE3X3+Pl1ow==", - "dependencies": { - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "@lezer/common": "^1.1.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0", - "style-mod": "^4.0.0" - } - }, - "node_modules/@codemirror/lint": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.4.2.tgz", - "integrity": "sha512-wzRkluWb1ptPKdzlsrbwwjYCPLgzU6N88YBAmlZi8WFyuiEduSd05MnJYNogzyc8rPK7pj6m95ptUApc8sHKVA==", - "dependencies": { - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "crelt": "^1.0.5" - } - }, - "node_modules/@codemirror/state": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.3.1.tgz", - "integrity": "sha512-88e4HhMtKJyw6fKprGaN/yZfiaoGYOi2nM45YCUC6R/kex9sxFWBDGatS1vk4lMgnWmdIIB9tk8Gj1LmL8YfvA==" - }, - "node_modules/@codemirror/view": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.22.0.tgz", - "integrity": "sha512-6zLj4YIoIpfTGKrDMTbeZRpa8ih4EymMCKmddEDcJWrCdp/N1D46B38YEz4creTb4T177AVS9EyXkLeC/HL2jA==", - "dependencies": { - "@codemirror/state": "^6.1.4", - "style-mod": "^4.1.0", - "w3c-keyname": "^2.2.4" - } - }, - "node_modules/@codesandbox/nodebox": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/@codesandbox/nodebox/-/nodebox-0.1.8.tgz", - "integrity": "sha512-2VRS6JDSk+M+pg56GA6CryyUSGPjBEe8Pnae0QL3jJF1mJZJVMDKr93gJRtBbLkfZN6LD/DwMtf+2L0bpWrjqg==", - "dependencies": { - "outvariant": "^1.4.0", - "strict-event-emitter": "^0.4.3" - } - }, - "node_modules/@codesandbox/sandpack-client": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/@codesandbox/sandpack-client/-/sandpack-client-2.9.0.tgz", - "integrity": "sha512-KkG/YusBsL0RnI3P079cckHrRleLaGQVM5Plzn6xWOPM04Dce52MMkr6XIU77ZMjZm/zZ5pmgXLW7M6HoyIy/A==", - "dependencies": { - "@codesandbox/nodebox": "0.1.8", - "buffer": "^6.0.3", - "dequal": "^2.0.2", - "outvariant": "1.4.0", - "static-browser-server": "1.0.3" - } - }, - "node_modules/@codesandbox/sandpack-react": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/@codesandbox/sandpack-react/-/sandpack-react-2.9.0.tgz", - "integrity": "sha512-A0quGugVyWbRktpdq2Nc6W1+XV0QnHq1lbqDCHAs2ijfWxvhhNaqMr6lWDaG/NTUGRNLUNETbipcNaBeC0dxhw==", - "dependencies": { - "@codemirror/autocomplete": "^6.4.0", - "@codemirror/commands": "^6.1.3", - "@codemirror/lang-css": "^6.0.1", - "@codemirror/lang-html": "^6.4.0", - "@codemirror/lang-javascript": "^6.1.2", - "@codemirror/language": "^6.3.2", - "@codemirror/state": "^6.2.0", - "@codemirror/view": "^6.7.1", - "@codesandbox/sandpack-client": "^2.9.0", - "@lezer/highlight": "^1.1.3", - "@react-hook/intersection-observer": "^3.1.1", - "@stitches/core": "^1.2.6", - "anser": "^2.1.1", - "clean-set": "^1.1.2", - "codesandbox-import-util-types": "^2.2.3", - "dequal": "^2.0.2", - "escape-carriage": "^1.3.1", - "lz-string": "^1.4.4", - "react-devtools-inline": "4.4.0", - "react-is": "^17.0.2" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17 || ^18", - "react-dom": "^16.8.0 || ^17 || ^18" - } - }, - "node_modules/@codesandbox/sandpack-themes": { - "version": "2.0.21", - "resolved": "https://registry.npmjs.org/@codesandbox/sandpack-themes/-/sandpack-themes-2.0.21.tgz", - "integrity": "sha512-CMH/MO/dh6foPYb/3eSn2Cu/J3+1+/81Fsaj7VggICkCrmRk0qG5dmgjGAearPTnRkOGORIPHuRqwNXgw0E6YQ==" - }, - "node_modules/@colors/colors": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", - "optional": true, - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/@discoveryjs/json-ext": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", - "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/@docsearch/css": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.6.0.tgz", - "integrity": "sha512-+sbxb71sWre+PwDK7X2T8+bhS6clcVMLwBPznX45Qu6opJcgRjAp7gYSDzVFp187J+feSj5dNBN1mJoi6ckkUQ==" - }, - "node_modules/@docsearch/react": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.6.0.tgz", - "integrity": "sha512-HUFut4ztcVNmqy9gp/wxNbC7pTOHhgVVkHVGCACTuLhUKUhKAF9KYHJtMiLUJxEqiFLQiuri1fWF8zqwM/cu1w==", - "dependencies": { - "@algolia/autocomplete-core": "1.9.3", - "@algolia/autocomplete-preset-algolia": "1.9.3", - "@docsearch/css": "3.6.0", - "algoliasearch": "^4.19.1" - }, - "peerDependencies": { - "@types/react": ">= 16.8.0 < 19.0.0", - "react": ">= 16.8.0 < 19.0.0", - "react-dom": ">= 16.8.0 < 19.0.0", - "search-insights": ">= 1 < 3" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "react": { - "optional": true - }, - "react-dom": { - "optional": true - }, - "search-insights": { - "optional": true - } - } - }, - "node_modules/@docusaurus/core": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.4.0.tgz", - "integrity": "sha512-g+0wwmN2UJsBqy2fQRQ6fhXruoEa62JDeEa5d8IdTJlMoaDaEDfHh7WjwGRn4opuTQWpjAwP/fbcgyHKlE+64w==", - "dependencies": { - "@babel/core": "^7.23.3", - "@babel/generator": "^7.23.3", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-transform-runtime": "^7.22.9", - "@babel/preset-env": "^7.22.9", - "@babel/preset-react": "^7.22.5", - "@babel/preset-typescript": "^7.22.5", - "@babel/runtime": "^7.22.6", - "@babel/runtime-corejs3": "^7.22.6", - "@babel/traverse": "^7.22.8", - "@docusaurus/cssnano-preset": "3.4.0", - "@docusaurus/logger": "3.4.0", - "@docusaurus/mdx-loader": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-common": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", - "autoprefixer": "^10.4.14", - "babel-loader": "^9.1.3", - "babel-plugin-dynamic-import-node": "^2.3.3", - "boxen": "^6.2.1", - "chalk": "^4.1.2", - "chokidar": "^3.5.3", - "clean-css": "^5.3.2", - "cli-table3": "^0.6.3", - "combine-promises": "^1.1.0", - "commander": "^5.1.0", - "copy-webpack-plugin": "^11.0.0", - "core-js": "^3.31.1", - "css-loader": "^6.8.1", - "css-minimizer-webpack-plugin": "^5.0.1", - "cssnano": "^6.1.2", - "del": "^6.1.1", - "detect-port": "^1.5.1", - "escape-html": "^1.0.3", - "eta": "^2.2.0", - "eval": "^0.1.8", - "file-loader": "^6.2.0", - "fs-extra": "^11.1.1", - "html-minifier-terser": "^7.2.0", - "html-tags": "^3.3.1", - "html-webpack-plugin": "^5.5.3", - "leven": "^3.1.0", - "lodash": "^4.17.21", - "mini-css-extract-plugin": "^2.7.6", - "p-map": "^4.0.0", - "postcss": "^8.4.26", - "postcss-loader": "^7.3.3", - "prompts": "^2.4.2", - "react-dev-utils": "^12.0.1", - "react-helmet-async": "^1.3.0", - "react-loadable": "npm:@docusaurus/react-loadable@6.0.0", - "react-loadable-ssr-addon-v5-slorber": "^1.0.1", - "react-router": "^5.3.4", - "react-router-config": "^5.1.1", - "react-router-dom": "^5.3.4", - "rtl-detect": "^1.0.4", - "semver": "^7.5.4", - "serve-handler": "^6.1.5", - "shelljs": "^0.8.5", - "terser-webpack-plugin": "^5.3.9", - "tslib": "^2.6.0", - "update-notifier": "^6.0.2", - "url-loader": "^4.1.1", - "webpack": "^5.88.1", - "webpack-bundle-analyzer": "^4.9.0", - "webpack-dev-server": "^4.15.1", - "webpack-merge": "^5.9.0", - "webpackbar": "^5.0.2" - }, - "bin": { - "docusaurus": "bin/docusaurus.mjs" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" - } - }, - "node_modules/@docusaurus/cssnano-preset": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-3.4.0.tgz", - "integrity": "sha512-qwLFSz6v/pZHy/UP32IrprmH5ORce86BGtN0eBtG75PpzQJAzp9gefspox+s8IEOr0oZKuQ/nhzZ3xwyc3jYJQ==", - "dependencies": { - "cssnano-preset-advanced": "^6.1.2", - "postcss": "^8.4.38", - "postcss-sort-media-queries": "^5.2.0", - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=18.0" - } - }, - "node_modules/@docusaurus/logger": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-3.4.0.tgz", - "integrity": "sha512-bZwkX+9SJ8lB9kVRkXw+xvHYSMGG4bpYHKGXeXFvyVc79NMeeBSGgzd4TQLHH+DYeOJoCdl8flrFJVxlZ0wo/Q==", - "dependencies": { - "chalk": "^4.1.2", - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=18.0" - } - }, - "node_modules/@docusaurus/mdx-loader": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.4.0.tgz", - "integrity": "sha512-kSSbrrk4nTjf4d+wtBA9H+FGauf2gCax89kV8SUSJu3qaTdSIKdWERlngsiHaCFgZ7laTJ8a67UFf+xlFPtuTw==", - "dependencies": { - "@docusaurus/logger": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", - "@mdx-js/mdx": "^3.0.0", - "@slorber/remark-comment": "^1.0.0", - "escape-html": "^1.0.3", - "estree-util-value-to-estree": "^3.0.1", - "file-loader": "^6.2.0", - "fs-extra": "^11.1.1", - "image-size": "^1.0.2", - "mdast-util-mdx": "^3.0.0", - "mdast-util-to-string": "^4.0.0", - "rehype-raw": "^7.0.0", - "remark-directive": "^3.0.0", - "remark-emoji": "^4.0.0", - "remark-frontmatter": "^5.0.0", - "remark-gfm": "^4.0.0", - "stringify-object": "^3.3.0", - "tslib": "^2.6.0", - "unified": "^11.0.3", - "unist-util-visit": "^5.0.0", - "url-loader": "^4.1.1", - "vfile": "^6.0.1", - "webpack": "^5.88.1" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" - } - }, - "node_modules/@docusaurus/module-type-aliases": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.4.0.tgz", - "integrity": "sha512-A1AyS8WF5Bkjnb8s+guTDuYmUiwJzNrtchebBHpc0gz0PyHJNMaybUlSrmJjHVcGrya0LKI4YcR3lBDQfXRYLw==", - "dependencies": { - "@docusaurus/types": "3.4.0", - "@types/history": "^4.7.11", - "@types/react": "*", - "@types/react-router-config": "*", - "@types/react-router-dom": "*", - "react-helmet-async": "*", - "react-loadable": "npm:@docusaurus/react-loadable@6.0.0" - }, - "peerDependencies": { - "react": "*", - "react-dom": "*" - } - }, - "node_modules/@docusaurus/plugin-content-blog": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.4.0.tgz", - "integrity": "sha512-vv6ZAj78ibR5Jh7XBUT4ndIjmlAxkijM3Sx5MAAzC1gyv0vupDQNhzuFg1USQmQVj3P5I6bquk12etPV3LJ+Xw==", - "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/logger": "3.4.0", - "@docusaurus/mdx-loader": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-common": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", - "cheerio": "^1.0.0-rc.12", - "feed": "^4.2.2", - "fs-extra": "^11.1.1", - "lodash": "^4.17.21", - "reading-time": "^1.5.0", - "srcset": "^4.0.0", - "tslib": "^2.6.0", - "unist-util-visit": "^5.0.0", - "utility-types": "^3.10.0", - "webpack": "^5.88.1" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" - } - }, - "node_modules/@docusaurus/plugin-content-docs": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.4.0.tgz", - "integrity": "sha512-HkUCZffhBo7ocYheD9oZvMcDloRnGhBMOZRyVcAQRFmZPmNqSyISlXA1tQCIxW+r478fty97XXAGjNYzBjpCsg==", - "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/logger": "3.4.0", - "@docusaurus/mdx-loader": "3.4.0", - "@docusaurus/module-type-aliases": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-common": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", - "@types/react-router-config": "^5.0.7", - "combine-promises": "^1.1.0", - "fs-extra": "^11.1.1", - "js-yaml": "^4.1.0", - "lodash": "^4.17.21", - "tslib": "^2.6.0", - "utility-types": "^3.10.0", - "webpack": "^5.88.1" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" - } - }, - "node_modules/@docusaurus/plugin-content-pages": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.4.0.tgz", - "integrity": "sha512-h2+VN/0JjpR8fIkDEAoadNjfR3oLzB+v1qSXbIAKjQ46JAHx3X22n9nqS+BWSQnTnp1AjkjSvZyJMekmcwxzxg==", - "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/mdx-loader": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", - "fs-extra": "^11.1.1", - "tslib": "^2.6.0", - "webpack": "^5.88.1" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" - } - }, - "node_modules/@docusaurus/plugin-debug": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-3.4.0.tgz", - "integrity": "sha512-uV7FDUNXGyDSD3PwUaf5YijX91T5/H9SX4ErEcshzwgzWwBtK37nUWPU3ZLJfeTavX3fycTOqk9TglpOLaWkCg==", - "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils": "3.4.0", - "fs-extra": "^11.1.1", - "react-json-view-lite": "^1.2.0", - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" - } - }, - "node_modules/@docusaurus/plugin-google-analytics": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.4.0.tgz", - "integrity": "sha512-mCArluxEGi3cmYHqsgpGGt3IyLCrFBxPsxNZ56Mpur0xSlInnIHoeLDH7FvVVcPJRPSQ9/MfRqLsainRw+BojA==", - "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" - } - }, - "node_modules/@docusaurus/plugin-google-gtag": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.4.0.tgz", - "integrity": "sha512-Dsgg6PLAqzZw5wZ4QjUYc8Z2KqJqXxHxq3vIoyoBWiLEEfigIs7wHR+oiWUQy3Zk9MIk6JTYj7tMoQU0Jm3nqA==", - "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", - "@types/gtag.js": "^0.0.12", - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" - } - }, - "node_modules/@docusaurus/plugin-google-tag-manager": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.4.0.tgz", - "integrity": "sha512-O9tX1BTwxIhgXpOLpFDueYA9DWk69WCbDRrjYoMQtFHSkTyE7RhNgyjSPREUWJb9i+YUg3OrsvrBYRl64FCPCQ==", - "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" - } - }, - "node_modules/@docusaurus/plugin-sitemap": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.4.0.tgz", - "integrity": "sha512-+0VDvx9SmNrFNgwPoeoCha+tRoAjopwT0+pYO1xAbyLcewXSemq+eLxEa46Q1/aoOaJQ0qqHELuQM7iS2gp33Q==", - "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/logger": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-common": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", - "fs-extra": "^11.1.1", - "sitemap": "^7.1.1", - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" - } - }, - "node_modules/@docusaurus/preset-classic": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-3.4.0.tgz", - "integrity": "sha512-Ohj6KB7siKqZaQhNJVMBBUzT3Nnp6eTKqO+FXO3qu/n1hJl3YLwVKTWBg28LF7MWrKu46UuYavwMRxud0VyqHg==", - "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/plugin-content-blog": "3.4.0", - "@docusaurus/plugin-content-docs": "3.4.0", - "@docusaurus/plugin-content-pages": "3.4.0", - "@docusaurus/plugin-debug": "3.4.0", - "@docusaurus/plugin-google-analytics": "3.4.0", - "@docusaurus/plugin-google-gtag": "3.4.0", - "@docusaurus/plugin-google-tag-manager": "3.4.0", - "@docusaurus/plugin-sitemap": "3.4.0", - "@docusaurus/theme-classic": "3.4.0", - "@docusaurus/theme-common": "3.4.0", - "@docusaurus/theme-search-algolia": "3.4.0", - "@docusaurus/types": "3.4.0" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" - } - }, - "node_modules/@docusaurus/theme-classic": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-3.4.0.tgz", - "integrity": "sha512-0IPtmxsBYv2adr1GnZRdMkEQt1YW6tpzrUPj02YxNpvJ5+ju4E13J5tB4nfdaen/tfR1hmpSPlTFPvTf4kwy8Q==", - "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/mdx-loader": "3.4.0", - "@docusaurus/module-type-aliases": "3.4.0", - "@docusaurus/plugin-content-blog": "3.4.0", - "@docusaurus/plugin-content-docs": "3.4.0", - "@docusaurus/plugin-content-pages": "3.4.0", - "@docusaurus/theme-common": "3.4.0", - "@docusaurus/theme-translations": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-common": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", - "@mdx-js/react": "^3.0.0", - "clsx": "^2.0.0", - "copy-text-to-clipboard": "^3.2.0", - "infima": "0.2.0-alpha.43", - "lodash": "^4.17.21", - "nprogress": "^0.2.0", - "postcss": "^8.4.26", - "prism-react-renderer": "^2.3.0", - "prismjs": "^1.29.0", - "react-router-dom": "^5.3.4", - "rtlcss": "^4.1.0", - "tslib": "^2.6.0", - "utility-types": "^3.10.0" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" - } - }, - "node_modules/@docusaurus/theme-common": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.4.0.tgz", - "integrity": "sha512-0A27alXuv7ZdCg28oPE8nH/Iz73/IUejVaCazqu9elS4ypjiLhK3KfzdSQBnL/g7YfHSlymZKdiOHEo8fJ0qMA==", - "dependencies": { - "@docusaurus/mdx-loader": "3.4.0", - "@docusaurus/module-type-aliases": "3.4.0", - "@docusaurus/plugin-content-blog": "3.4.0", - "@docusaurus/plugin-content-docs": "3.4.0", - "@docusaurus/plugin-content-pages": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-common": "3.4.0", - "@types/history": "^4.7.11", - "@types/react": "*", - "@types/react-router-config": "*", - "clsx": "^2.0.0", - "parse-numeric-range": "^1.3.0", - "prism-react-renderer": "^2.3.0", - "tslib": "^2.6.0", - "utility-types": "^3.10.0" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" - } - }, - "node_modules/@docusaurus/theme-mermaid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-mermaid/-/theme-mermaid-3.4.0.tgz", - "integrity": "sha512-3w5QW0HEZ2O6x2w6lU3ZvOe1gNXP2HIoKDMJBil1VmLBc9PmpAG17VmfhI/p3L2etNmOiVs5GgniUqvn8AFEGQ==", - "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/module-type-aliases": "3.4.0", - "@docusaurus/theme-common": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", - "mermaid": "^10.4.0", - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" - } - }, - "node_modules/@docusaurus/theme-search-algolia": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.4.0.tgz", - "integrity": "sha512-aiHFx7OCw4Wck1z6IoShVdUWIjntC8FHCw9c5dR8r3q4Ynh+zkS8y2eFFunN/DL6RXPzpnvKCg3vhLQYJDmT9Q==", - "dependencies": { - "@docsearch/react": "^3.5.2", - "@docusaurus/core": "3.4.0", - "@docusaurus/logger": "3.4.0", - "@docusaurus/plugin-content-docs": "3.4.0", - "@docusaurus/theme-common": "3.4.0", - "@docusaurus/theme-translations": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", - "algoliasearch": "^4.18.0", - "algoliasearch-helper": "^3.13.3", - "clsx": "^2.0.0", - "eta": "^2.2.0", - "fs-extra": "^11.1.1", - "lodash": "^4.17.21", - "tslib": "^2.6.0", - "utility-types": "^3.10.0" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" - } - }, - "node_modules/@docusaurus/theme-translations": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.4.0.tgz", - "integrity": "sha512-zSxCSpmQCCdQU5Q4CnX/ID8CSUUI3fvmq4hU/GNP/XoAWtXo9SAVnM3TzpU8Gb//H3WCsT8mJcTfyOk3d9ftNg==", - "dependencies": { - "fs-extra": "^11.1.1", - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=18.0" - } - }, - "node_modules/@docusaurus/tsconfig": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/tsconfig/-/tsconfig-3.4.0.tgz", - "integrity": "sha512-0qENiJ+TRaeTzcg4olrnh0BQ7eCxTgbYWBnWUeQDc84UYkt/T3pDNnm3SiQkqPb+YQ1qtYFlC0RriAElclo8Dg==" - }, - "node_modules/@docusaurus/types": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.4.0.tgz", - "integrity": "sha512-4jcDO8kXi5Cf9TcyikB/yKmz14f2RZ2qTRerbHAsS+5InE9ZgSLBNLsewtFTcTOXSVcbU3FoGOzcNWAmU1TR0A==", - "dependencies": { - "@mdx-js/mdx": "^3.0.0", - "@types/history": "^4.7.11", - "@types/react": "*", - "commander": "^5.1.0", - "joi": "^17.9.2", - "react-helmet-async": "^1.3.0", - "utility-types": "^3.10.0", - "webpack": "^5.88.1", - "webpack-merge": "^5.9.0" - }, - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" - } - }, - "node_modules/@docusaurus/utils": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.4.0.tgz", - "integrity": "sha512-fRwnu3L3nnWaXOgs88BVBmG1yGjcQqZNHG+vInhEa2Sz2oQB+ZjbEMO5Rh9ePFpZ0YDiDUhpaVjwmS+AU2F14g==", - "dependencies": { - "@docusaurus/logger": "3.4.0", - "@docusaurus/utils-common": "3.4.0", - "@svgr/webpack": "^8.1.0", - "escape-string-regexp": "^4.0.0", - "file-loader": "^6.2.0", - "fs-extra": "^11.1.1", - "github-slugger": "^1.5.0", - "globby": "^11.1.0", - "gray-matter": "^4.0.3", - "jiti": "^1.20.0", - "js-yaml": "^4.1.0", - "lodash": "^4.17.21", - "micromatch": "^4.0.5", - "prompts": "^2.4.2", - "resolve-pathname": "^3.0.0", - "shelljs": "^0.8.5", - "tslib": "^2.6.0", - "url-loader": "^4.1.1", - "utility-types": "^3.10.0", - "webpack": "^5.88.1" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "@docusaurus/types": "*" - }, - "peerDependenciesMeta": { - "@docusaurus/types": { - "optional": true - } - } - }, - "node_modules/@docusaurus/utils-common": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.4.0.tgz", - "integrity": "sha512-NVx54Wr4rCEKsjOH5QEVvxIqVvm+9kh7q8aYTU5WzUU9/Hctd6aTrcZ3G0Id4zYJ+AeaG5K5qHA4CY5Kcm2iyQ==", - "dependencies": { - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "@docusaurus/types": "*" - }, - "peerDependenciesMeta": { - "@docusaurus/types": { - "optional": true - } - } - }, - "node_modules/@docusaurus/utils-validation": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.4.0.tgz", - "integrity": "sha512-hYQ9fM+AXYVTWxJOT1EuNaRnrR2WGpRdLDQG07O8UOpsvCPWUVOeo26Rbm0JWY2sGLfzAb+tvJ62yF+8F+TV0g==", - "dependencies": { - "@docusaurus/logger": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-common": "3.4.0", - "fs-extra": "^11.2.0", - "joi": "^17.9.2", - "js-yaml": "^4.1.0", - "lodash": "^4.17.21", - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=18.0" - } - }, - "node_modules/@hapi/hoek": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", - "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" - }, - "node_modules/@hapi/topo": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", - "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", - "dependencies": { - "@hapi/hoek": "^9.0.0" - } - }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", - "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.20", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", - "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@leichtgewicht/ip-codec": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", - "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" - }, - "node_modules/@lezer/common": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.1.0.tgz", - "integrity": "sha512-XPIN3cYDXsoJI/oDWoR2tD++juVrhgIago9xyKhZ7IhGlzdDM9QgC8D8saKNCz5pindGcznFr2HBSsEQSWnSjw==" - }, - "node_modules/@lezer/css": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.1.3.tgz", - "integrity": "sha512-SjSM4pkQnQdJDVc80LYzEaMiNy9txsFbI7HsMgeVF28NdLaAdHNtQ+kB/QqDUzRBV/75NTXjJ/R5IdC8QQGxMg==", - "dependencies": { - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0" - } - }, - "node_modules/@lezer/highlight": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.1.6.tgz", - "integrity": "sha512-cmSJYa2us+r3SePpRCjN5ymCqCPv+zyXmDl0ciWtVaNiORT/MxM7ZgOMQZADD0o51qOaOg24qc/zBViOIwAjJg==", - "dependencies": { - "@lezer/common": "^1.0.0" - } - }, - "node_modules/@lezer/html": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.6.tgz", - "integrity": "sha512-Kk9HJARZTc0bAnMQUqbtuhFVsB4AnteR2BFUWfZV7L/x1H0aAKz6YabrfJ2gk/BEgjh9L3hg5O4y2IDZRBdzuQ==", - "dependencies": { - "@lezer/common": "^1.0.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0" - } - }, - "node_modules/@lezer/javascript": { - "version": "1.4.9", - "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.4.9.tgz", - "integrity": "sha512-7Uv8mBBE6l44spgWEZvEMdDqGV+FIuY7kJ1o5TFm+jxIuxydO3PcKJYiINij09igd1D/9P7l2KDqpkN8c3bM6A==", - "dependencies": { - "@lezer/highlight": "^1.1.3", - "@lezer/lr": "^1.3.0" - } - }, - "node_modules/@lezer/lr": { - "version": "1.3.14", - "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.3.14.tgz", - "integrity": "sha512-z5mY4LStlA3yL7aHT/rqgG614cfcvklS+8oFRFBYrs4YaWLJyKKM4+nN6KopToX0o9Hj6zmH6M5kinOYuy06ug==", - "dependencies": { - "@lezer/common": "^1.0.0" - } - }, - "node_modules/@mdx-js/mdx": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-3.0.1.tgz", - "integrity": "sha512-eIQ4QTrOWyL3LWEe/bu6Taqzq2HQvHcyTMaOrI95P2/LmJE7AsfPfgJGuFLPVqBUE1BC1rik3VIhU+s9u72arA==", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdx": "^2.0.0", - "collapse-white-space": "^2.0.0", - "devlop": "^1.0.0", - "estree-util-build-jsx": "^3.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "estree-util-to-js": "^2.0.0", - "estree-walker": "^3.0.0", - "hast-util-to-estree": "^3.0.0", - "hast-util-to-jsx-runtime": "^2.0.0", - "markdown-extensions": "^2.0.0", - "periscopic": "^3.0.0", - "remark-mdx": "^3.0.0", - "remark-parse": "^11.0.0", - "remark-rehype": "^11.0.0", - "source-map": "^0.7.0", - "unified": "^11.0.0", - "unist-util-position-from-estree": "^2.0.0", - "unist-util-stringify-position": "^4.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/@mdx-js/react": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.0.0.tgz", - "integrity": "sha512-nDctevR9KyYFyV+m+/+S4cpzCWHqj+iHDHq3QrsWezcC+B17uZdIWgCguESUkwFhM3n/56KxWVE3V6EokrmONQ==", - "dependencies": { - "@types/mdx": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - }, - "peerDependencies": { - "@types/react": ">=16", - "react": ">=16" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@open-draft/deferred-promise": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz", - "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==" - }, - "node_modules/@pnpm/config.env-replace": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", - "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==", - "engines": { - "node": ">=12.22.0" - } - }, - "node_modules/@pnpm/network.ca-file": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz", - "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==", - "dependencies": { - "graceful-fs": "4.2.10" - }, - "engines": { - "node": ">=12.22.0" - } - }, - "node_modules/@pnpm/network.ca-file/node_modules/graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" - }, - "node_modules/@pnpm/npm-conf": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-2.2.2.tgz", - "integrity": "sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==", - "dependencies": { - "@pnpm/config.env-replace": "^1.1.0", - "@pnpm/network.ca-file": "^1.0.1", - "config-chain": "^1.1.11" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@polka/url": { - "version": "1.0.0-next.23", - "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.23.tgz", - "integrity": "sha512-C16M+IYz0rgRhWZdCmK+h58JMv8vijAA61gmz2rspCSwKwzBebpdcsiUmwrtJRdphuY30i6BSLEOP8ppbNLyLg==" - }, - "node_modules/@react-hook/intersection-observer": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@react-hook/intersection-observer/-/intersection-observer-3.1.1.tgz", - "integrity": "sha512-OTDx8/wFaRvzFtKl1dEUEXSOqK2zVJHporiTTdC2xO++0e9FEx9wIrPis5q3lqtXeZH9zYGLbk+aB75qNFbbuw==", - "dependencies": { - "@react-hook/passive-layout-effect": "^1.2.0", - "intersection-observer": "^0.10.0" - }, - "peerDependencies": { - "react": ">=16.8" - } - }, - "node_modules/@react-hook/passive-layout-effect": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@react-hook/passive-layout-effect/-/passive-layout-effect-1.2.1.tgz", - "integrity": "sha512-IwEphTD75liO8g+6taS+4oqz+nnroocNfWVHWz7j+N+ZO2vYrc6PV1q7GQhuahL0IOR7JccFTsFKQ/mb6iZWAg==", - "peerDependencies": { - "react": ">=16.8" - } - }, - "node_modules/@sideway/address": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz", - "integrity": "sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==", - "dependencies": { - "@hapi/hoek": "^9.0.0" - } - }, - "node_modules/@sideway/formula": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", - "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==" - }, - "node_modules/@sideway/pinpoint": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", - "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" - }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==" - }, - "node_modules/@sindresorhus/is": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", - "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" - } - }, - "node_modules/@slorber/remark-comment": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@slorber/remark-comment/-/remark-comment-1.0.0.tgz", - "integrity": "sha512-RCE24n7jsOj1M0UPvIQCHTe7fI0sFL4S2nwKVWwHyVr/wI/H8GosgsJGyhnsZoGFnD/P2hLf1mSbrrgSLN93NA==", - "dependencies": { - "micromark-factory-space": "^1.0.0", - "micromark-util-character": "^1.1.0", - "micromark-util-symbol": "^1.0.1" - } - }, - "node_modules/@stitches/core": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@stitches/core/-/core-1.2.8.tgz", - "integrity": "sha512-Gfkvwk9o9kE9r9XNBmJRfV8zONvXThnm1tcuojL04Uy5uRyqg93DC83lDebl0rocZCfKSjUv+fWYtMQmEDJldg==" - }, - "node_modules/@svgr/babel-plugin-add-jsx-attribute": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", - "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==", - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-remove-jsx-attribute": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz", - "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==", - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz", - "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==", - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz", - "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==", - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-svg-dynamic-title": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz", - "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==", - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-svg-em-dimensions": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz", - "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==", - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-transform-react-native-svg": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz", - "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==", - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-transform-svg-component": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz", - "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==", - "engines": { - "node": ">=12" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-preset": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz", - "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==", - "dependencies": { - "@svgr/babel-plugin-add-jsx-attribute": "8.0.0", - "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0", - "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0", - "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0", - "@svgr/babel-plugin-svg-dynamic-title": "8.0.0", - "@svgr/babel-plugin-svg-em-dimensions": "8.0.0", - "@svgr/babel-plugin-transform-react-native-svg": "8.1.0", - "@svgr/babel-plugin-transform-svg-component": "8.0.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/core": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", - "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", - "dependencies": { - "@babel/core": "^7.21.3", - "@svgr/babel-preset": "8.1.0", - "camelcase": "^6.2.0", - "cosmiconfig": "^8.1.3", - "snake-case": "^3.0.4" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/hast-util-to-babel-ast": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz", - "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==", - "dependencies": { - "@babel/types": "^7.21.3", - "entities": "^4.4.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/plugin-jsx": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz", - "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==", - "dependencies": { - "@babel/core": "^7.21.3", - "@svgr/babel-preset": "8.1.0", - "@svgr/hast-util-to-babel-ast": "8.0.0", - "svg-parser": "^2.0.4" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@svgr/core": "*" - } - }, - "node_modules/@svgr/plugin-svgo": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-8.1.0.tgz", - "integrity": "sha512-Ywtl837OGO9pTLIN/onoWLmDQ4zFUycI1g76vuKGEz6evR/ZTJlJuz3G/fIkb6OVBJ2g0o6CGJzaEjfmEo3AHA==", - "dependencies": { - "cosmiconfig": "^8.1.3", - "deepmerge": "^4.3.1", - "svgo": "^3.0.2" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@svgr/core": "*" - } - }, - "node_modules/@svgr/webpack": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-8.1.0.tgz", - "integrity": "sha512-LnhVjMWyMQV9ZmeEy26maJk+8HTIbd59cH4F2MJ439k9DqejRisfFNGAPvRYlKETuh9LrImlS8aKsBgKjMA8WA==", - "dependencies": { - "@babel/core": "^7.21.3", - "@babel/plugin-transform-react-constant-elements": "^7.21.3", - "@babel/preset-env": "^7.20.2", - "@babel/preset-react": "^7.18.6", - "@babel/preset-typescript": "^7.21.0", - "@svgr/core": "8.1.0", - "@svgr/plugin-jsx": "8.1.0", - "@svgr/plugin-svgo": "8.1.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@szmarczak/http-timer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", - "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", - "dependencies": { - "defer-to-connect": "^2.0.1" - }, - "engines": { - "node": ">=14.16" - } - }, - "node_modules/@trysound/sax": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", - "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/@types/acorn": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@types/acorn/-/acorn-4.0.6.tgz", - "integrity": "sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ==", - "dependencies": { - "@types/estree": "*" - } - }, - "node_modules/@types/body-parser": { - "version": "1.19.5", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", - "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/bonjour": { - "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz", - "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/connect-history-api-fallback": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.3.tgz", - "integrity": "sha512-6mfQ6iNvhSKCZJoY6sIG3m0pKkdUcweVNOLuBBKvoWGzl2yRxOJcYOTRyLKt3nxXvBLJWa6QkW//tgbIwJehmA==", - "dependencies": { - "@types/express-serve-static-core": "*", - "@types/node": "*" - } - }, - "node_modules/@types/d3-scale": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz", - "integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==", - "dependencies": { - "@types/d3-time": "*" - } - }, - "node_modules/@types/d3-scale-chromatic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.0.3.tgz", - "integrity": "sha512-laXM4+1o5ImZv3RpFAsTRn3TEkzqkytiOY0Dz0sq5cnd1dtNlk6sHLon4OvqaiJb28T0S/TdsBI3Sjsy+keJrw==" - }, - "node_modules/@types/d3-time": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.3.tgz", - "integrity": "sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==" - }, - "node_modules/@types/debug": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.11.tgz", - "integrity": "sha512-R2qflTjHDs4CL6D/6TkqBeIHr54WzZfIxN729xvCNlYIVp2LknlnCro5Yo3frNaX2E5gO9pZ3/QAPVdGmu+q9w==", - "dependencies": { - "@types/ms": "*" - } - }, - "node_modules/@types/eslint": { - "version": "8.44.7", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.7.tgz", - "integrity": "sha512-f5ORu2hcBbKei97U73mf+l9t4zTGl74IqZ0GQk4oVea/VS8tQZYkUveSYojk+frraAVYId0V2WC9O4PTNru2FQ==", - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, - "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" - }, - "node_modules/@types/estree-jsx": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", - "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", - "dependencies": { - "@types/estree": "*" - } - }, - "node_modules/@types/express": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", - "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "4.17.41", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.41.tgz", - "integrity": "sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@types/gtag.js": { - "version": "0.0.12", - "resolved": "https://registry.npmjs.org/@types/gtag.js/-/gtag.js-0.0.12.tgz", - "integrity": "sha512-YQV9bUsemkzG81Ea295/nF/5GijnD2Af7QhEofh7xu+kvCN6RdodgNwwGWXB5GMI3NoyvQo0odNctoH/qLMIpg==" - }, - "node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/history": { - "version": "4.7.11", - "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.11.tgz", - "integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==" - }, - "node_modules/@types/html-minifier-terser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", - "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==" - }, - "node_modules/@types/http-cache-semantics": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", - "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==" - }, - "node_modules/@types/http-errors": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", - "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==" - }, - "node_modules/@types/http-proxy": { - "version": "1.17.14", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.14.tgz", - "integrity": "sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==" - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" - }, - "node_modules/@types/mdast": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.3.tgz", - "integrity": "sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/mdx": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.10.tgz", - "integrity": "sha512-Rllzc5KHk0Al5/WANwgSPl1/CwjqCy+AZrGd78zuK+jO9aDM6ffblZ+zIjgPNAaEBmlO0RYDvLNh7wD0zKVgEg==" - }, - "node_modules/@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==" - }, - "node_modules/@types/ms": { - "version": "0.7.34", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", - "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==" - }, - "node_modules/@types/node": { - "version": "20.9.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.0.tgz", - "integrity": "sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw==", - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@types/node-forge": { - "version": "1.3.9", - "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.9.tgz", - "integrity": "sha512-meK88cx/sTalPSLSoCzkiUB4VPIFHmxtXm5FaaqRDqBX2i/Sy8bJ4odsan0b20RBjPh06dAQ+OTTdnyQyhJZyQ==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/parse-json": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", - "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==" - }, - "node_modules/@types/prismjs": { - "version": "1.26.3", - "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.3.tgz", - "integrity": "sha512-A0D0aTXvjlqJ5ZILMz3rNfDBOx9hHxLZYv2by47Sm/pqW35zzjusrZTryatjN/Rf8Us2gZrJD+KeHbUSTux1Cw==" - }, - "node_modules/@types/prop-types": { - "version": "15.7.10", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.10.tgz", - "integrity": "sha512-mxSnDQxPqsZxmeShFH+uwQ4kO4gcJcGahjjMFeLbKE95IAZiiZyiEepGZjtXJ7hN/yfu0bu9xN2ajcU0JcxX6A==" - }, - "node_modules/@types/qs": { - "version": "6.9.10", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.10.tgz", - "integrity": "sha512-3Gnx08Ns1sEoCrWssEgTSJs/rsT2vhGP+Ja9cnnk9k4ALxinORlQneLXFeFKOTJMOeZUFD1s7w+w2AphTpvzZw==" - }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==" - }, - "node_modules/@types/react": { - "version": "18.2.37", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.37.tgz", - "integrity": "sha512-RGAYMi2bhRgEXT3f4B92WTohopH6bIXw05FuGlmJEnv/omEn190+QYEIYxIAuIBdKgboYYdVved2p1AxZVQnaw==", - "dependencies": { - "@types/prop-types": "*", - "@types/scheduler": "*", - "csstype": "^3.0.2" - } - }, - "node_modules/@types/react-router": { - "version": "5.1.20", - "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.20.tgz", - "integrity": "sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==", - "dependencies": { - "@types/history": "^4.7.11", - "@types/react": "*" - } - }, - "node_modules/@types/react-router-config": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/@types/react-router-config/-/react-router-config-5.0.10.tgz", - "integrity": "sha512-Wn6c/tXdEgi9adCMtDwx8Q2vGty6TsPTc/wCQQ9kAlye8UqFxj0vGFWWuhywNfkwqth+SOgJxQTLTZukrqDQmQ==", - "dependencies": { - "@types/history": "^4.7.11", - "@types/react": "*", - "@types/react-router": "^5.1.0" - } - }, - "node_modules/@types/react-router-dom": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.3.3.tgz", - "integrity": "sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==", - "dependencies": { - "@types/history": "^4.7.11", - "@types/react": "*", - "@types/react-router": "*" - } - }, - "node_modules/@types/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" - }, - "node_modules/@types/sax": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz", - "integrity": "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/scheduler": { - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.6.tgz", - "integrity": "sha512-Vlktnchmkylvc9SnwwwozTv04L/e1NykF5vgoQ0XTmI8DD+wxfjQuHuvHS3p0r2jz2x2ghPs2h1FVeDirIteWA==" - }, - "node_modules/@types/send": { - "version": "0.17.4", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", - "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "node_modules/@types/serve-index": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz", - "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", - "dependencies": { - "@types/express": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz", - "integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==", - "dependencies": { - "@types/http-errors": "*", - "@types/mime": "*", - "@types/node": "*" - } - }, - "node_modules/@types/sockjs": { - "version": "0.3.36", - "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", - "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/unist": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", - "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==" - }, - "node_modules/@types/ws": { - "version": "8.5.9", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.9.tgz", - "integrity": "sha512-jbdrY0a8lxfdTp/+r7Z4CkycbOFN8WX+IOchLJr3juT/xzbJ8URyTVSJ/hvNdadTgM1mnedb47n+Y31GsFnQlg==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/yargs": { - "version": "17.0.32", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", - "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==" - }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==" - }, - "node_modules/@webassemblyjs/ast": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", - "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", - "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6" - } - }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", - "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==" - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", - "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==" - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", - "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==" - }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", - "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", - "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.6", - "@webassemblyjs/helper-api-error": "1.11.6", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", - "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==" - }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", - "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", - "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6" - } - }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", - "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", - "dependencies": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", - "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", - "dependencies": { - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", - "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==" - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", - "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", - "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/helper-wasm-section": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6", - "@webassemblyjs/wasm-opt": "1.11.6", - "@webassemblyjs/wasm-parser": "1.11.6", - "@webassemblyjs/wast-printer": "1.11.6" - } - }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", - "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", - "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" - } - }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", - "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", - "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6", - "@webassemblyjs/wasm-parser": "1.11.6" - } - }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", - "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", - "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-api-error": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" - } - }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", - "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", - "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" - }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/accepts/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/accepts/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/acorn": { - "version": "8.11.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", - "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-import-assertions": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", - "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", - "peerDependencies": { - "acorn": "^8" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.0.tgz", - "integrity": "sha512-FS7hV565M5l1R08MXqo8odwMTB02C2UqzB17RVgu9EyuYFBqJZ3/ZY97sQD5FewVu1UyDFc1yztUDrAwT0EypA==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/address": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/address/-/address-1.2.2.tgz", - "integrity": "sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/algoliasearch": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.23.3.tgz", - "integrity": "sha512-Le/3YgNvjW9zxIQMRhUHuhiUjAlKY/zsdZpfq4dlLqg6mEm0nL6yk+7f2hDOtLpxsgE4jSzDmvHL7nXdBp5feg==", - "dependencies": { - "@algolia/cache-browser-local-storage": "4.23.3", - "@algolia/cache-common": "4.23.3", - "@algolia/cache-in-memory": "4.23.3", - "@algolia/client-account": "4.23.3", - "@algolia/client-analytics": "4.23.3", - "@algolia/client-common": "4.23.3", - "@algolia/client-personalization": "4.23.3", - "@algolia/client-search": "4.23.3", - "@algolia/logger-common": "4.23.3", - "@algolia/logger-console": "4.23.3", - "@algolia/recommend": "4.23.3", - "@algolia/requester-browser-xhr": "4.23.3", - "@algolia/requester-common": "4.23.3", - "@algolia/requester-node-http": "4.23.3", - "@algolia/transporter": "4.23.3" - } - }, - "node_modules/algoliasearch-helper": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.21.0.tgz", - "integrity": "sha512-hjVOrL15I3Y3K8xG0icwG1/tWE+MocqBrhW6uVBWpU+/kVEMK0BnM2xdssj6mZM61eJ4iRxHR0djEI3ENOpR8w==", - "dependencies": { - "@algolia/events": "^4.0.1" - }, - "peerDependencies": { - "algoliasearch": ">= 3.1 < 6" - } - }, - "node_modules/anser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/anser/-/anser-2.1.1.tgz", - "integrity": "sha512-nqLm4HxOTpeLOxcmB3QWmV5TcDFhW9y/fyQ+hivtDFcK4OQ+pQ5fzPnXHM1Mfcm0VkLtvVi1TCPr++Qy0Q/3EQ==" - }, - "node_modules/ansi-align": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", - "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", - "dependencies": { - "string-width": "^4.1.0" - } - }, - "node_modules/ansi-align/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "node_modules/ansi-align/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-html-community": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", - "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", - "engines": [ - "node >= 0.8.0" - ], - "bin": { - "ansi-html": "bin/ansi-html" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "node_modules/array-flatten": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", - "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/astring": { - "version": "1.8.6", - "resolved": "https://registry.npmjs.org/astring/-/astring-1.8.6.tgz", - "integrity": "sha512-ISvCdHdlTDlH5IpxQJIex7BWBywFWgjJSVdwst+/iQCoEYnyOaQ95+X1JGshuBjGp6nxKUy1jMgE3zPqN7fQdg==", - "bin": { - "astring": "bin/astring" - } - }, - "node_modules/at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/autoprefixer": { - "version": "10.4.19", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz", - "integrity": "sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/autoprefixer" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "browserslist": "^4.23.0", - "caniuse-lite": "^1.0.30001599", - "fraction.js": "^4.3.7", - "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", - "postcss-value-parser": "^4.2.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/babel-loader": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.1.3.tgz", - "integrity": "sha512-xG3ST4DglodGf8qSwv0MdeWLhrDsw/32QMdTO5T1ZIp9gQur0HkCyFs7Awskr10JKXFXwpAhiCuYX5oGXnRGbw==", - "dependencies": { - "find-cache-dir": "^4.0.0", - "schema-utils": "^4.0.0" - }, - "engines": { - "node": ">= 14.15.0" - }, - "peerDependencies": { - "@babel/core": "^7.12.0", - "webpack": ">=5" - } - }, - "node_modules/babel-plugin-dynamic-import-node": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", - "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", - "dependencies": { - "object.assign": "^4.1.0" - } - }, - "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.6.tgz", - "integrity": "sha512-jhHiWVZIlnPbEUKSSNb9YoWcQGdlTLq7z1GHL4AjFxaoOUMuuEVJ+Y4pAaQUGOGk93YsVCKPbqbfw3m0SM6H8Q==", - "dependencies": { - "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.4.3", - "semver": "^6.3.1" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.8.6", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.6.tgz", - "integrity": "sha512-leDIc4l4tUgU7str5BWLS2h8q2N4Nf6lGZP6UrNDxdtfF2g69eJ5L0H7S8A5Ln/arfFAfHor5InAdZuIOwZdgQ==", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.4.3", - "core-js-compat": "^3.33.1" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.3.tgz", - "integrity": "sha512-8sHeDOmXC8csczMrYEOf0UTNa4yE2SxV5JGeT/LP1n0OYVDUUFPxG9vdk2AlDlIit4t+Kf0xCtpgXPBwnn/9pw==", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.4.3" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/bail": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", - "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/batch": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==" - }, - "node_modules/big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "engines": { - "node": "*" - } - }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.1", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/body-parser/node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/body-parser/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/body-parser/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/bonjour-service": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.1.1.tgz", - "integrity": "sha512-Z/5lQRMOG9k7W+FkeGTNjh7htqn/2LMnfOvBZ8pynNZCM9MwkQkI3zeI4oz09uWdcgmgHugVvBqxGg4VQJ5PCg==", - "dependencies": { - "array-flatten": "^2.1.2", - "dns-equal": "^1.0.0", - "fast-deep-equal": "^3.1.3", - "multicast-dns": "^7.2.5" - } - }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" - }, - "node_modules/boxen": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-6.2.1.tgz", - "integrity": "sha512-H4PEsJXfFI/Pt8sjDWbHlQPx4zL/bvSQjcilJmaulGt5mLDorHOHpmdXAJcBcmru7PhYSp/cDMWRko4ZUMFkSw==", - "dependencies": { - "ansi-align": "^3.0.1", - "camelcase": "^6.2.0", - "chalk": "^4.1.2", - "cli-boxes": "^3.0.0", - "string-width": "^5.0.1", - "type-fest": "^2.5.0", - "widest-line": "^4.0.1", - "wrap-ansi": "^8.0.1" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", - "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "caniuse-lite": "^1.0.30001587", - "electron-to-chromium": "^1.4.668", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" - }, - "node_modules/bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/cacheable-lookup": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", - "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", - "engines": { - "node": ">=14.16" - } - }, - "node_modules/cacheable-request": { - "version": "10.2.14", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz", - "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==", - "dependencies": { - "@types/http-cache-semantics": "^4.0.2", - "get-stream": "^6.0.1", - "http-cache-semantics": "^4.1.1", - "keyv": "^4.5.3", - "mimic-response": "^4.0.0", - "normalize-url": "^8.0.0", - "responselike": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - } - }, - "node_modules/cacheable-request/node_modules/normalize-url": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.0.tgz", - "integrity": "sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw==", - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/call-bind": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", - "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", - "dependencies": { - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.1", - "set-function-length": "^1.1.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/camel-case": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", - "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", - "dependencies": { - "pascal-case": "^3.1.2", - "tslib": "^2.0.3" - } - }, - "node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/caniuse-api": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", - "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", - "dependencies": { - "browserslist": "^4.0.0", - "caniuse-lite": "^1.0.0", - "lodash.memoize": "^4.1.2", - "lodash.uniq": "^4.5.0" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001632", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001632.tgz", - "integrity": "sha512-udx3o7yHJfUxMLkGohMlVHCvFvWmirKh9JAH/d7WOLPetlH+LTL5cocMZ0t7oZx/mdlOWXti97xLZWc8uURRHg==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ] - }, - "node_modules/ccount": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", - "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "engines": { - "node": ">=10" - } - }, - "node_modules/character-entities": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", - "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-html4": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", - "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-legacy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", - "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-reference-invalid": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", - "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/cheerio": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", - "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", - "dependencies": { - "cheerio-select": "^2.1.0", - "dom-serializer": "^2.0.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "htmlparser2": "^8.0.1", - "parse5": "^7.0.0", - "parse5-htmlparser2-tree-adapter": "^7.0.0" - }, - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/cheeriojs/cheerio?sponsor=1" - } - }, - "node_modules/cheerio-select": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", - "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", - "dependencies": { - "boolbase": "^1.0.0", - "css-select": "^5.1.0", - "css-what": "^6.1.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", - "engines": { - "node": ">=6.0" - } - }, - "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "engines": { - "node": ">=8" - } - }, - "node_modules/clean-css": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.2.tgz", - "integrity": "sha512-JVJbM+f3d3Q704rF4bqQ5UUyTtuJ0JRKNbTKVEeujCCBoMdkEi+V+e8oktO9qGQNSvHrFTM6JZRXrUvGR1czww==", - "dependencies": { - "source-map": "~0.6.0" - }, - "engines": { - "node": ">= 10.0" - } - }, - "node_modules/clean-css/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/clean-set": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/clean-set/-/clean-set-1.1.2.tgz", - "integrity": "sha512-cA8uCj0qSoG9e0kevyOWXwPaELRPVg5Pxp6WskLMwerx257Zfnh8Nl0JBH59d7wQzij2CK7qEfJQK3RjuKKIug==" - }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "engines": { - "node": ">=6" - } - }, - "node_modules/cli-boxes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", - "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-table3": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz", - "integrity": "sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==", - "dependencies": { - "string-width": "^4.2.0" - }, - "engines": { - "node": "10.* || >= 12.*" - }, - "optionalDependencies": { - "@colors/colors": "1.5.0" - } - }, - "node_modules/cli-table3/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "node_modules/cli-table3/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/clone-deep": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", - "dependencies": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/clone-deep/node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/clsx": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz", - "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==", - "engines": { - "node": ">=6" - } - }, - "node_modules/codesandbox-import-util-types": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/codesandbox-import-util-types/-/codesandbox-import-util-types-2.2.3.tgz", - "integrity": "sha512-Qj00p60oNExthP2oR3vvXmUGjukij+rxJGuiaKM6tyUmSyimdZsqHI/TUvFFClAffk9s7hxGnQgWQ8KCce27qQ==" - }, - "node_modules/collapse-white-space": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-2.1.0.tgz", - "integrity": "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/colord": { - "version": "2.9.3", - "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", - "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==" - }, - "node_modules/colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==" - }, - "node_modules/combine-promises": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/combine-promises/-/combine-promises-1.2.0.tgz", - "integrity": "sha512-VcQB1ziGD0NXrhKxiwyNbCDmRzs/OShMs2GqW2DlU2A/Sd0nQxE1oWDAE5O0ygSx5mgQOn9eIFh7yKPgFRVkPQ==", - "engines": { - "node": ">=10" - } - }, - "node_modules/comma-separated-tokens": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", - "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/commander": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", - "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/common-path-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", - "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==" - }, - "node_modules/compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "dependencies": { - "mime-db": ">= 1.43.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/compressible/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", - "dependencies": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", - "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/compression/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/compression/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/compression/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "node_modules/config-chain": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", - "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", - "dependencies": { - "ini": "^1.3.4", - "proto-list": "~1.2.1" - } - }, - "node_modules/configstore": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-6.0.0.tgz", - "integrity": "sha512-cD31W1v3GqUlQvbBCGcXmd2Nj9SvLDOP1oQ0YFuLETufzSPaKp11rYBsSOm7rCsW3OnIRAFM3OxRhceaXNYHkA==", - "dependencies": { - "dot-prop": "^6.0.1", - "graceful-fs": "^4.2.6", - "unique-string": "^3.0.0", - "write-file-atomic": "^3.0.3", - "xdg-basedir": "^5.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/yeoman/configstore?sponsor=1" - } - }, - "node_modules/connect-history-api-fallback": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", - "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/consola": { - "version": "2.15.3", - "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", - "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==" - }, - "node_modules/content-disposition": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", - "integrity": "sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" - }, - "node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" - }, - "node_modules/copy-text-to-clipboard": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/copy-text-to-clipboard/-/copy-text-to-clipboard-3.2.0.tgz", - "integrity": "sha512-RnJFp1XR/LOBDckxTib5Qjr/PMfkatD0MUCQgdpqS8MdKiNUzBjAQBEN6oUy+jW7LI93BBG3DtMB2KOOKpGs2Q==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/copy-webpack-plugin": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz", - "integrity": "sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==", - "dependencies": { - "fast-glob": "^3.2.11", - "glob-parent": "^6.0.1", - "globby": "^13.1.1", - "normalize-path": "^3.0.0", - "schema-utils": "^4.0.0", - "serialize-javascript": "^6.0.0" - }, - "engines": { - "node": ">= 14.15.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - } - }, - "node_modules/copy-webpack-plugin/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/copy-webpack-plugin/node_modules/globby": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", - "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", - "dependencies": { - "dir-glob": "^3.0.1", - "fast-glob": "^3.3.0", - "ignore": "^5.2.4", - "merge2": "^1.4.1", - "slash": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/copy-webpack-plugin/node_modules/slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/core-js": { - "version": "3.33.2", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.33.2.tgz", - "integrity": "sha512-XeBzWI6QL3nJQiHmdzbAOiMYqjrb7hwU7A39Qhvd/POSa/t9E1AeZyEZx3fNvp/vtM8zXwhoL0FsiS0hD0pruQ==", - "hasInstallScript": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/core-js-compat": { - "version": "3.33.2", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.33.2.tgz", - "integrity": "sha512-axfo+wxFVxnqf8RvxTzoAlzW4gRoacrHeoFlc9n0x50+7BEyZL/Rt3hicaED1/CEd7I6tPCPVUYcJwCMO5XUYw==", - "dependencies": { - "browserslist": "^4.22.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/core-js-pure": { - "version": "3.33.2", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.33.2.tgz", - "integrity": "sha512-a8zeCdyVk7uF2elKIGz67AjcXOxjRbwOLz8SbklEso1V+2DoW4OkAMZN9S9GBgvZIaqQi/OemFX4OiSoQEmg1Q==", - "hasInstallScript": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" - }, - "node_modules/cose-base": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz", - "integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==", - "dependencies": { - "layout-base": "^1.0.0" - } - }, - "node_modules/cosmiconfig": { - "version": "8.3.6", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", - "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", - "dependencies": { - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0", - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - }, - "peerDependencies": { - "typescript": ">=4.9.5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/crelt": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", - "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==" - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/crypto-random-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz", - "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==", - "dependencies": { - "type-fest": "^1.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/crypto-random-string/node_modules/type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/css-declaration-sorter": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.2.0.tgz", - "integrity": "sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow==", - "engines": { - "node": "^14 || ^16 || >=18" - }, - "peerDependencies": { - "postcss": "^8.0.9" - } - }, - "node_modules/css-loader": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.8.1.tgz", - "integrity": "sha512-xDAXtEVGlD0gJ07iclwWVkLoZOpEvAWaSyf6W18S2pOC//K8+qUDIx8IIT3D+HjnmkJPQeesOPv5aiUaJsCM2g==", - "dependencies": { - "icss-utils": "^5.1.0", - "postcss": "^8.4.21", - "postcss-modules-extract-imports": "^3.0.0", - "postcss-modules-local-by-default": "^4.0.3", - "postcss-modules-scope": "^3.0.0", - "postcss-modules-values": "^4.0.0", - "postcss-value-parser": "^4.2.0", - "semver": "^7.3.8" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - } - }, - "node_modules/css-minimizer-webpack-plugin": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-5.0.1.tgz", - "integrity": "sha512-3caImjKFQkS+ws1TGcFn0V1HyDJFq1Euy589JlD6/3rV2kj+w7r5G9WDMgSHvpvXHNZ2calVypZWuEDQd9wfLg==", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.18", - "cssnano": "^6.0.1", - "jest-worker": "^29.4.3", - "postcss": "^8.4.24", - "schema-utils": "^4.0.1", - "serialize-javascript": "^6.0.1" - }, - "engines": { - "node": ">= 14.15.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - }, - "peerDependenciesMeta": { - "@parcel/css": { - "optional": true - }, - "@swc/css": { - "optional": true - }, - "clean-css": { - "optional": true - }, - "csso": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "lightningcss": { - "optional": true - } - } - }, - "node_modules/css-select": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", - "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.1.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css-tree": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", - "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", - "dependencies": { - "mdn-data": "2.0.30", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" - } - }, - "node_modules/css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/cssnano": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-6.1.2.tgz", - "integrity": "sha512-rYk5UeX7VAM/u0lNqewCdasdtPK81CgX8wJFLEIXHbV2oldWRgJAsZrdhRXkV1NJzA2g850KiFm9mMU2HxNxMA==", - "dependencies": { - "cssnano-preset-default": "^6.1.2", - "lilconfig": "^3.1.1" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/cssnano" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/cssnano-preset-advanced": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/cssnano-preset-advanced/-/cssnano-preset-advanced-6.1.2.tgz", - "integrity": "sha512-Nhao7eD8ph2DoHolEzQs5CfRpiEP0xa1HBdnFZ82kvqdmbwVBUr2r1QuQ4t1pi+D1ZpqpcO4T+wy/7RxzJ/WPQ==", - "dependencies": { - "autoprefixer": "^10.4.19", - "browserslist": "^4.23.0", - "cssnano-preset-default": "^6.1.2", - "postcss-discard-unused": "^6.0.5", - "postcss-merge-idents": "^6.0.3", - "postcss-reduce-idents": "^6.0.3", - "postcss-zindex": "^6.0.2" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/cssnano-preset-default": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-6.1.2.tgz", - "integrity": "sha512-1C0C+eNaeN8OcHQa193aRgYexyJtU8XwbdieEjClw+J9d94E41LwT6ivKH0WT+fYwYWB0Zp3I3IZ7tI/BbUbrg==", - "dependencies": { - "browserslist": "^4.23.0", - "css-declaration-sorter": "^7.2.0", - "cssnano-utils": "^4.0.2", - "postcss-calc": "^9.0.1", - "postcss-colormin": "^6.1.0", - "postcss-convert-values": "^6.1.0", - "postcss-discard-comments": "^6.0.2", - "postcss-discard-duplicates": "^6.0.3", - "postcss-discard-empty": "^6.0.3", - "postcss-discard-overridden": "^6.0.2", - "postcss-merge-longhand": "^6.0.5", - "postcss-merge-rules": "^6.1.1", - "postcss-minify-font-values": "^6.1.0", - "postcss-minify-gradients": "^6.0.3", - "postcss-minify-params": "^6.1.0", - "postcss-minify-selectors": "^6.0.4", - "postcss-normalize-charset": "^6.0.2", - "postcss-normalize-display-values": "^6.0.2", - "postcss-normalize-positions": "^6.0.2", - "postcss-normalize-repeat-style": "^6.0.2", - "postcss-normalize-string": "^6.0.2", - "postcss-normalize-timing-functions": "^6.0.2", - "postcss-normalize-unicode": "^6.1.0", - "postcss-normalize-url": "^6.0.2", - "postcss-normalize-whitespace": "^6.0.2", - "postcss-ordered-values": "^6.0.2", - "postcss-reduce-initial": "^6.1.0", - "postcss-reduce-transforms": "^6.0.2", - "postcss-svgo": "^6.0.3", - "postcss-unique-selectors": "^6.0.4" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/cssnano-utils": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-4.0.2.tgz", - "integrity": "sha512-ZR1jHg+wZ8o4c3zqf1SIUSTIvm/9mU343FMR6Obe/unskbvpGhZOo1J6d/r8D1pzkRQYuwbcH3hToOuoA2G7oQ==", - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/csso": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", - "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", - "dependencies": { - "css-tree": "~2.2.0" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", - "npm": ">=7.0.0" - } - }, - "node_modules/csso/node_modules/css-tree": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", - "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", - "dependencies": { - "mdn-data": "2.0.28", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", - "npm": ">=7.0.0" - } - }, - "node_modules/csso/node_modules/mdn-data": { - "version": "2.0.28", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", - "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==" - }, - "node_modules/csstype": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", - "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" - }, - "node_modules/cytoscape": { - "version": "3.27.0", - "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.27.0.tgz", - "integrity": "sha512-pPZJilfX9BxESwujODz5pydeGi+FBrXq1rcaB1mfhFXXFJ9GjE6CNndAk+8jPzoXGD+16LtSS4xlYEIUiW4Abg==", - "dependencies": { - "heap": "^0.2.6", - "lodash": "^4.17.21" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/cytoscape-cose-bilkent": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz", - "integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==", - "dependencies": { - "cose-base": "^1.0.0" - }, - "peerDependencies": { - "cytoscape": "^3.2.0" - } - }, - "node_modules/cytoscape-fcose": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz", - "integrity": "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==", - "dependencies": { - "cose-base": "^2.2.0" - }, - "peerDependencies": { - "cytoscape": "^3.2.0" - } - }, - "node_modules/cytoscape-fcose/node_modules/cose-base": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-2.2.0.tgz", - "integrity": "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==", - "dependencies": { - "layout-base": "^2.0.0" - } - }, - "node_modules/cytoscape-fcose/node_modules/layout-base": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-2.0.1.tgz", - "integrity": "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==" - }, - "node_modules/d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "dependencies": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, - "node_modules/d3": { - "version": "7.8.5", - "resolved": "https://registry.npmjs.org/d3/-/d3-7.8.5.tgz", - "integrity": "sha512-JgoahDG51ncUfJu6wX/1vWQEqOflgXyl4MaHqlcSruTez7yhaRKR9i8VjjcQGeS2en/jnFivXuaIMnseMMt0XA==", - "dependencies": { - "d3-array": "3", - "d3-axis": "3", - "d3-brush": "3", - "d3-chord": "3", - "d3-color": "3", - "d3-contour": "4", - "d3-delaunay": "6", - "d3-dispatch": "3", - "d3-drag": "3", - "d3-dsv": "3", - "d3-ease": "3", - "d3-fetch": "3", - "d3-force": "3", - "d3-format": "3", - "d3-geo": "3", - "d3-hierarchy": "3", - "d3-interpolate": "3", - "d3-path": "3", - "d3-polygon": "3", - "d3-quadtree": "3", - "d3-random": "3", - "d3-scale": "4", - "d3-scale-chromatic": "3", - "d3-selection": "3", - "d3-shape": "3", - "d3-time": "3", - "d3-time-format": "4", - "d3-timer": "3", - "d3-transition": "3", - "d3-zoom": "3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-array": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", - "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", - "dependencies": { - "internmap": "1 - 2" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-axis": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", - "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-brush": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", - "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", - "dependencies": { - "d3-dispatch": "1 - 3", - "d3-drag": "2 - 3", - "d3-interpolate": "1 - 3", - "d3-selection": "3", - "d3-transition": "3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-chord": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", - "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", - "dependencies": { - "d3-path": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-color": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", - "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-contour": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", - "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", - "dependencies": { - "d3-array": "^3.2.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-delaunay": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", - "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", - "dependencies": { - "delaunator": "5" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-dispatch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", - "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-drag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", - "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", - "dependencies": { - "d3-dispatch": "1 - 3", - "d3-selection": "3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-dsv": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", - "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", - "dependencies": { - "commander": "7", - "iconv-lite": "0.6", - "rw": "1" - }, - "bin": { - "csv2json": "bin/dsv2json.js", - "csv2tsv": "bin/dsv2dsv.js", - "dsv2dsv": "bin/dsv2dsv.js", - "dsv2json": "bin/dsv2json.js", - "json2csv": "bin/json2dsv.js", - "json2dsv": "bin/json2dsv.js", - "json2tsv": "bin/json2dsv.js", - "tsv2csv": "bin/dsv2dsv.js", - "tsv2json": "bin/dsv2json.js" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-dsv/node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "engines": { - "node": ">= 10" - } - }, - "node_modules/d3-dsv/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/d3-ease": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", - "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-fetch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", - "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", - "dependencies": { - "d3-dsv": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-force": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", - "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", - "dependencies": { - "d3-dispatch": "1 - 3", - "d3-quadtree": "1 - 3", - "d3-timer": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-format": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", - "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-geo": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.0.tgz", - "integrity": "sha512-JEo5HxXDdDYXCaWdwLRt79y7giK8SbhZJbFWXqbRTolCHFI5jRqteLzCsq51NKbUoX0PjBVSohxrx+NoOUujYA==", - "dependencies": { - "d3-array": "2.5.0 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-hierarchy": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", - "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-interpolate": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", - "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", - "dependencies": { - "d3-color": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-path": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", - "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-polygon": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", - "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-quadtree": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", - "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-random": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", - "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-sankey": { - "version": "0.12.3", - "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz", - "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==", - "dependencies": { - "d3-array": "1 - 2", - "d3-shape": "^1.2.0" - } - }, - "node_modules/d3-sankey/node_modules/d3-array": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", - "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", - "dependencies": { - "internmap": "^1.0.0" - } - }, - "node_modules/d3-sankey/node_modules/d3-path": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", - "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==" - }, - "node_modules/d3-sankey/node_modules/d3-shape": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", - "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", - "dependencies": { - "d3-path": "1" - } - }, - "node_modules/d3-sankey/node_modules/internmap": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", - "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==" - }, - "node_modules/d3-scale": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", - "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", - "dependencies": { - "d3-array": "2.10.0 - 3", - "d3-format": "1 - 3", - "d3-interpolate": "1.2.0 - 3", - "d3-time": "2.1.1 - 3", - "d3-time-format": "2 - 4" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-scale-chromatic": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz", - "integrity": "sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==", - "dependencies": { - "d3-color": "1 - 3", - "d3-interpolate": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-selection": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", - "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-shape": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", - "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", - "dependencies": { - "d3-path": "^3.1.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-time": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", - "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", - "dependencies": { - "d3-array": "2 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-time-format": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", - "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", - "dependencies": { - "d3-time": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-timer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", - "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-transition": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", - "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", - "dependencies": { - "d3-color": "1 - 3", - "d3-dispatch": "1 - 3", - "d3-ease": "1 - 3", - "d3-interpolate": "1 - 3", - "d3-timer": "1 - 3" - }, - "engines": { - "node": ">=12" - }, - "peerDependencies": { - "d3-selection": "2 - 3" - } - }, - "node_modules/d3-zoom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", - "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", - "dependencies": { - "d3-dispatch": "1 - 3", - "d3-drag": "2 - 3", - "d3-interpolate": "1 - 3", - "d3-selection": "2 - 3", - "d3-transition": "2 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/dagre-d3-es": { - "version": "7.0.10", - "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.10.tgz", - "integrity": "sha512-qTCQmEhcynucuaZgY5/+ti3X/rnszKZhEQH/ZdWdtP1tA/y3VoHJzcVrO9pjjJCNpigfscAtoUB5ONcd2wNn0A==", - "dependencies": { - "d3": "^7.8.2", - "lodash-es": "^4.17.21" - } - }, - "node_modules/dayjs": { - "version": "1.11.10", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", - "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==" - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decode-named-character-reference": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", - "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", - "dependencies": { - "character-entities": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/decompress-response/node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/default-gateway": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", - "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", - "dependencies": { - "execa": "^5.0.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/defer-to-connect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", - "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", - "engines": { - "node": ">=10" - } - }, - "node_modules/define-data-property": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", - "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", - "dependencies": { - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", - "engines": { - "node": ">=8" - } - }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/del": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz", - "integrity": "sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==", - "dependencies": { - "globby": "^11.0.1", - "graceful-fs": "^4.2.4", - "is-glob": "^4.0.1", - "is-path-cwd": "^2.2.0", - "is-path-inside": "^3.0.2", - "p-map": "^4.0.0", - "rimraf": "^3.0.2", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/delaunator": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.0.tgz", - "integrity": "sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==", - "dependencies": { - "robust-predicates": "^3.0.0" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/detect-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", - "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==" - }, - "node_modules/detect-port": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.5.1.tgz", - "integrity": "sha512-aBzdj76lueB6uUst5iAs7+0H/oOjqI5D16XUWxlWMIMROhcM0rfsNVk93zTngq1dDNpoXRr++Sus7ETAExppAQ==", - "dependencies": { - "address": "^1.0.1", - "debug": "4" - }, - "bin": { - "detect": "bin/detect-port.js", - "detect-port": "bin/detect-port.js" - } - }, - "node_modules/detect-port-alt": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz", - "integrity": "sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==", - "dependencies": { - "address": "^1.0.1", - "debug": "^2.6.0" - }, - "bin": { - "detect": "bin/detect-port", - "detect-port": "bin/detect-port" - }, - "engines": { - "node": ">= 4.2.1" - } - }, - "node_modules/detect-port-alt/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/detect-port-alt/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/devlop": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", - "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", - "dependencies": { - "dequal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/diff": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", - "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/dns-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", - "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==" - }, - "node_modules/dns-packet": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", - "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", - "dependencies": { - "@leichtgewicht/ip-codec": "^2.0.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/dom-converter": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", - "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", - "dependencies": { - "utila": "~0.4" - } - }, - "node_modules/dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ] - }, - "node_modules/domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "dependencies": { - "domelementtype": "^2.3.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/dompurify": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.6.tgz", - "integrity": "sha512-ilkD8YEnnGh1zJ240uJsW7AzE+2qpbOUYjacomn3AvJ6J4JhKGSZ2nh4wUIXPZrEPppaCLx5jFe8T89Rk8tQ7w==" - }, - "node_modules/domutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", - "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", - "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/dot-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", - "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/dot-prop": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", - "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", - "dependencies": { - "is-obj": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/dot-prop/node_modules/is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/dotenv": { - "version": "16.3.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", - "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/motdotla/dotenv?sponsor=1" - } - }, - "node_modules/duplexer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" - }, - "node_modules/electron-to-chromium": { - "version": "1.4.699", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.699.tgz", - "integrity": "sha512-I7q3BbQi6e4tJJN5CRcyvxhK0iJb34TV8eJQcgh+fR2fQ8miMgZcEInckCo1U9exDHbfz7DLDnFn8oqH/VcRKw==" - }, - "node_modules/elkjs": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/elkjs/-/elkjs-0.8.2.tgz", - "integrity": "sha512-L6uRgvZTH+4OF5NE/MBbzQx/WYpru1xCBE9respNj6qznEewGUIfhzmm7horWWxbNO2M0WckQypGctR8lH79xQ==" - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" - }, - "node_modules/emojilib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/emojilib/-/emojilib-2.4.0.tgz", - "integrity": "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==" - }, - "node_modules/emojis-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", - "engines": { - "node": ">= 4" - } - }, - "node_modules/emoticon": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/emoticon/-/emoticon-4.0.1.tgz", - "integrity": "sha512-dqx7eA9YaqyvYtUhJwT4rC1HIp82j5ybS1/vQ42ur+jBe17dJMwZE4+gvL1XadSFfxaPFFGt3Xsw+Y8akThDlw==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/enhanced-resolve": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", - "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-module-lexer": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.1.tgz", - "integrity": "sha512-JUFAyicQV9mXc3YRxPnDlrfBKpqt6hUYzz9/boprUJHs4e4KVr3XwOF70doO6gwXUor6EWZJAyWAfKki84t20Q==" - }, - "node_modules/es5-ext": { - "version": "0.10.62", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", - "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", - "hasInstallScript": true, - "dependencies": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "next-tick": "^1.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", - "dependencies": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "node_modules/es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", - "dependencies": { - "d": "^1.0.1", - "ext": "^1.1.2" - } - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-carriage": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/escape-carriage/-/escape-carriage-1.3.1.tgz", - "integrity": "sha512-GwBr6yViW3ttx1kb7/Oh+gKQ1/TrhYwxKqVmg5gS+BK+Qe2KrOa/Vh7w3HPBvgGf0LfcDGoY9I6NHKoA5Hozhw==" - }, - "node_modules/escape-goat": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-4.0.0.tgz", - "integrity": "sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estree-util-attach-comments": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/estree-util-attach-comments/-/estree-util-attach-comments-3.0.0.tgz", - "integrity": "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==", - "dependencies": { - "@types/estree": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-build-jsx": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/estree-util-build-jsx/-/estree-util-build-jsx-3.0.1.tgz", - "integrity": "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "estree-walker": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-is-identifier-name": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", - "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-to-js": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/estree-util-to-js/-/estree-util-to-js-2.0.0.tgz", - "integrity": "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "astring": "^1.8.0", - "source-map": "^0.7.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-value-to-estree": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/estree-util-value-to-estree/-/estree-util-value-to-estree-3.1.1.tgz", - "integrity": "sha512-5mvUrF2suuv5f5cGDnDphIy4/gW86z82kl5qG6mM9z04SEQI4FB5Apmaw/TGEf3l55nLtMs5s51dmhUzvAHQCA==", - "dependencies": { - "@types/estree": "^1.0.0", - "is-plain-obj": "^4.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/remcohaszing" - } - }, - "node_modules/estree-util-visit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/estree-util-visit/-/estree-util-visit-2.0.0.tgz", - "integrity": "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eta": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/eta/-/eta-2.2.0.tgz", - "integrity": "sha512-UVQ72Rqjy/ZKQalzV5dCCJP80GrmPrMxh6NlNf+erV6ObL0ZFkhCstWRawS85z3smdr3d2wXPsZEY7rDPfGd2g==", - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "url": "https://github.com/eta-dev/eta?sponsor=1" - } - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/eval": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/eval/-/eval-0.1.8.tgz", - "integrity": "sha512-EzV94NYKoO09GLXGjXj9JIlXijVck4ONSr5wiCWDvhsvj5jxSrzTmRU/9C1DyB6uToszLs8aifA6NQ7lEQdvFw==", - "dependencies": { - "@types/node": "*", - "require-like": ">= 0.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/express": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", - "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.1", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.5.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.11.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/express/node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" - }, - "node_modules/express/node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/express/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/express/node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" - }, - "node_modules/express/node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/ext": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", - "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", - "dependencies": { - "type": "^2.7.2" - } - }, - "node_modules/ext/node_modules/type": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", - "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" - }, - "node_modules/fast-url-parser": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz", - "integrity": "sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ==", - "dependencies": { - "punycode": "^1.3.2" - } - }, - "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fault": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fault/-/fault-2.0.1.tgz", - "integrity": "sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==", - "dependencies": { - "format": "^0.2.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/faye-websocket": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", - "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", - "dependencies": { - "websocket-driver": ">=0.5.1" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/feed": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/feed/-/feed-4.2.2.tgz", - "integrity": "sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ==", - "dependencies": { - "xml-js": "^1.6.11" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/file-loader": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", - "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", - "dependencies": { - "loader-utils": "^2.0.0", - "schema-utils": "^3.0.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" - } - }, - "node_modules/file-loader/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/file-loader/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/file-loader/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "node_modules/file-loader/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/filesize": { - "version": "8.0.7", - "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", - "integrity": "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/find-cache-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-4.0.0.tgz", - "integrity": "sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==", - "dependencies": { - "common-path-prefix": "^3.0.0", - "pkg-dir": "^7.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/find-up": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", - "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", - "dependencies": { - "locate-path": "^7.1.0", - "path-exists": "^5.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "bin": { - "flat": "cli.js" - } - }, - "node_modules/follow-redirects": { - "version": "1.15.3", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", - "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/fork-ts-checker-webpack-plugin": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.3.tgz", - "integrity": "sha512-SbH/l9ikmMWycd5puHJKTkZJKddF4iRLyW3DeZ08HTI7NGyLS38MXd/KGgeWumQO7YNQbW2u/NtPT2YowbPaGQ==", - "dependencies": { - "@babel/code-frame": "^7.8.3", - "@types/json-schema": "^7.0.5", - "chalk": "^4.1.0", - "chokidar": "^3.4.2", - "cosmiconfig": "^6.0.0", - "deepmerge": "^4.2.2", - "fs-extra": "^9.0.0", - "glob": "^7.1.6", - "memfs": "^3.1.2", - "minimatch": "^3.0.4", - "schema-utils": "2.7.0", - "semver": "^7.3.2", - "tapable": "^1.0.0" - }, - "engines": { - "node": ">=10", - "yarn": ">=1.0.0" - }, - "peerDependencies": { - "eslint": ">= 6", - "typescript": ">= 2.7", - "vue-template-compiler": "*", - "webpack": ">= 4" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - }, - "vue-template-compiler": { - "optional": true - } - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/cosmiconfig": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", - "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", - "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.1.0", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.7.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/schema-utils": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", - "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", - "dependencies": { - "@types/json-schema": "^7.0.4", - "ajv": "^6.12.2", - "ajv-keywords": "^3.4.1" - }, - "engines": { - "node": ">= 8.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/tapable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", - "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/form-data-encoder": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", - "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==", - "engines": { - "node": ">= 14.17" - } - }, - "node_modules/format": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", - "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", - "engines": { - "node": ">=0.4.x" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fraction.js": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", - "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", - "engines": { - "node": "*" - }, - "funding": { - "type": "patreon", - "url": "https://github.com/sponsors/rawify" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fs-extra": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", - "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, - "node_modules/fs-monkey": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.5.tgz", - "integrity": "sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew==" - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", - "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", - "dependencies": { - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-own-enumerable-property-symbols": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", - "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==" - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/github-slugger": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.5.0.tgz", - "integrity": "sha512-wIh+gKBI9Nshz2o46B0B3f5k/W+WI9ZAv6y5Dn5WJ5SK1t0TnDimB4WE5rmTD05ZAIn8HALCZVmCsvj0w0v0lw==" - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" - }, - "node_modules/global-dirs": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", - "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==", - "dependencies": { - "ini": "2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/global-dirs/node_modules/ini": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", - "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", - "engines": { - "node": ">=10" - } - }, - "node_modules/global-modules": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", - "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", - "dependencies": { - "global-prefix": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/global-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", - "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", - "dependencies": { - "ini": "^1.3.5", - "kind-of": "^6.0.2", - "which": "^1.3.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/global-prefix/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/got": { - "version": "12.6.1", - "resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz", - "integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==", - "dependencies": { - "@sindresorhus/is": "^5.2.0", - "@szmarczak/http-timer": "^5.0.1", - "cacheable-lookup": "^7.0.0", - "cacheable-request": "^10.2.8", - "decompress-response": "^6.0.0", - "form-data-encoder": "^2.1.2", - "get-stream": "^6.0.1", - "http2-wrapper": "^2.1.10", - "lowercase-keys": "^3.0.0", - "p-cancelable": "^3.0.0", - "responselike": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sindresorhus/got?sponsor=1" - } - }, - "node_modules/got/node_modules/@sindresorhus/is": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", - "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==", - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" - }, - "node_modules/gray-matter": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", - "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==", - "dependencies": { - "js-yaml": "^3.13.1", - "kind-of": "^6.0.2", - "section-matter": "^1.0.0", - "strip-bom-string": "^1.0.0" - }, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/gray-matter/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/gray-matter/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/gzip-size": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", - "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", - "dependencies": { - "duplexer": "^0.1.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/handle-thing": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", - "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==" - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", - "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", - "dependencies": { - "get-intrinsic": "^1.2.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-yarn": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-3.0.0.tgz", - "integrity": "sha512-IrsVwUHhEULx3R8f/aA8AHuEzAorplsab/v8HBzEiIukwq5i/EC+xmOW+HfP1OaDP+2JkgT1yILHN2O3UFIbcA==", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/hasown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/hast-util-from-parse5": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.1.tgz", - "integrity": "sha512-Er/Iixbc7IEa7r/XLtuG52zoqn/b3Xng/w6aZQ0xGVxzhw5xUFxcRqdPzP6yFi/4HBYRaifaI5fQ1RH8n0ZeOQ==", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "devlop": "^1.0.0", - "hastscript": "^8.0.0", - "property-information": "^6.0.0", - "vfile": "^6.0.0", - "vfile-location": "^5.0.0", - "web-namespaces": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-parse-selector": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", - "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-raw": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.0.3.tgz", - "integrity": "sha512-ICWvVOF2fq4+7CMmtCPD5CM4QKjPbHpPotE6+8tDooV0ZuyJVUzHsrNX+O5NaRbieTf0F7FfeBOMAwi6Td0+yQ==", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "@ungap/structured-clone": "^1.0.0", - "hast-util-from-parse5": "^8.0.0", - "hast-util-to-parse5": "^8.0.0", - "html-void-elements": "^3.0.0", - "mdast-util-to-hast": "^13.0.0", - "parse5": "^7.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0", - "web-namespaces": "^2.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-estree": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/hast-util-to-estree/-/hast-util-to-estree-3.1.0.tgz", - "integrity": "sha512-lfX5g6hqVh9kjS/B9E2gSkvHH4SZNiQFiqWS0x9fENzEl+8W12RqdRxX6d/Cwxi30tPQs3bIO+aolQJNp1bIyw==", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "devlop": "^1.0.0", - "estree-util-attach-comments": "^3.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "hast-util-whitespace": "^3.0.0", - "mdast-util-mdx-expression": "^2.0.0", - "mdast-util-mdx-jsx": "^3.0.0", - "mdast-util-mdxjs-esm": "^2.0.0", - "property-information": "^6.0.0", - "space-separated-tokens": "^2.0.0", - "style-to-object": "^0.4.0", - "unist-util-position": "^5.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-jsx-runtime": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.0.tgz", - "integrity": "sha512-H/y0+IWPdsLLS738P8tDnrQ8Z+dj12zQQ6WC11TIM21C8WFVoIxcqWXf2H3hiTVZjF1AWqoimGwrTWecWrnmRQ==", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "hast-util-whitespace": "^3.0.0", - "mdast-util-mdx-expression": "^2.0.0", - "mdast-util-mdx-jsx": "^3.0.0", - "mdast-util-mdxjs-esm": "^2.0.0", - "property-information": "^6.0.0", - "space-separated-tokens": "^2.0.0", - "style-to-object": "^1.0.0", - "unist-util-position": "^5.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-jsx-runtime/node_modules/inline-style-parser": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.2.tgz", - "integrity": "sha512-EcKzdTHVe8wFVOGEYXiW9WmJXPjqi1T+234YpJr98RiFYKHV3cdy1+3mkTE+KHTHxFFLH51SfaGOoUdW+v7ViQ==" - }, - "node_modules/hast-util-to-jsx-runtime/node_modules/style-to-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.5.tgz", - "integrity": "sha512-rDRwHtoDD3UMMrmZ6BzOW0naTjMsVZLIjsGleSKS/0Oz+cgCfAPRspaqJuE8rDzpKha/nEvnM0IF4seEAZUTKQ==", - "dependencies": { - "inline-style-parser": "0.2.2" - } - }, - "node_modules/hast-util-to-parse5": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.0.tgz", - "integrity": "sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==", - "dependencies": { - "@types/hast": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "devlop": "^1.0.0", - "property-information": "^6.0.0", - "space-separated-tokens": "^2.0.0", - "web-namespaces": "^2.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-whitespace": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", - "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hastscript": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-8.0.0.tgz", - "integrity": "sha512-dMOtzCEd3ABUeSIISmrETiKuyydk1w0pa+gE/uormcTpSYuaNJPbX1NU3JLyscSLjwAQM8bWMhhIlnCqnRvDTw==", - "dependencies": { - "@types/hast": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "hast-util-parse-selector": "^4.0.0", - "property-information": "^6.0.0", - "space-separated-tokens": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "bin": { - "he": "bin/he" - } - }, - "node_modules/heap": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.7.tgz", - "integrity": "sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==" - }, - "node_modules/history": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", - "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==", - "dependencies": { - "@babel/runtime": "^7.1.2", - "loose-envify": "^1.2.0", - "resolve-pathname": "^3.0.0", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0", - "value-equal": "^1.0.1" - } - }, - "node_modules/hoist-non-react-statics": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", - "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "dependencies": { - "react-is": "^16.7.0" - } - }, - "node_modules/hoist-non-react-statics/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, - "node_modules/hpack.js": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", - "dependencies": { - "inherits": "^2.0.1", - "obuf": "^1.0.0", - "readable-stream": "^2.0.1", - "wbuf": "^1.1.0" - } - }, - "node_modules/hpack.js/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "node_modules/hpack.js/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/hpack.js/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/hpack.js/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/html-entities": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.4.0.tgz", - "integrity": "sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/mdevils" - }, - { - "type": "patreon", - "url": "https://patreon.com/mdevils" - } - ] - }, - "node_modules/html-minifier-terser": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-7.2.0.tgz", - "integrity": "sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA==", - "dependencies": { - "camel-case": "^4.1.2", - "clean-css": "~5.3.2", - "commander": "^10.0.0", - "entities": "^4.4.0", - "param-case": "^3.0.4", - "relateurl": "^0.2.7", - "terser": "^5.15.1" - }, - "bin": { - "html-minifier-terser": "cli.js" - }, - "engines": { - "node": "^14.13.1 || >=16.0.0" - } - }, - "node_modules/html-minifier-terser/node_modules/commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", - "engines": { - "node": ">=14" - } - }, - "node_modules/html-tags": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz", - "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/html-void-elements": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", - "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/html-webpack-plugin": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.3.tgz", - "integrity": "sha512-6YrDKTuqaP/TquFH7h4srYWsZx+x6k6+FbsTm0ziCwGHDP78Unr1r9F/H4+sGmMbX08GQcJ+K64x55b+7VM/jg==", - "dependencies": { - "@types/html-minifier-terser": "^6.0.0", - "html-minifier-terser": "^6.0.2", - "lodash": "^4.17.21", - "pretty-error": "^4.0.0", - "tapable": "^2.0.0" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/html-webpack-plugin" - }, - "peerDependencies": { - "webpack": "^5.20.0" - } - }, - "node_modules/html-webpack-plugin/node_modules/commander": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", - "engines": { - "node": ">= 12" - } - }, - "node_modules/html-webpack-plugin/node_modules/html-minifier-terser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", - "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", - "dependencies": { - "camel-case": "^4.1.2", - "clean-css": "^5.2.2", - "commander": "^8.3.0", - "he": "^1.2.0", - "param-case": "^3.0.4", - "relateurl": "^0.2.7", - "terser": "^5.10.0" - }, - "bin": { - "html-minifier-terser": "cli.js" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/htmlparser2": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", - "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "entities": "^4.4.0" - } - }, - "node_modules/http-cache-semantics": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==" - }, - "node_modules/http-deceiver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==" - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/http-parser-js": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", - "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==" - }, - "node_modules/http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", - "dependencies": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/http-proxy-middleware": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", - "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", - "dependencies": { - "@types/http-proxy": "^1.17.8", - "http-proxy": "^1.18.1", - "is-glob": "^4.0.1", - "is-plain-obj": "^3.0.0", - "micromatch": "^4.0.2" - }, - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "@types/express": "^4.17.13" - }, - "peerDependenciesMeta": { - "@types/express": { - "optional": true - } - } - }, - "node_modules/http-proxy-middleware/node_modules/is-plain-obj": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", - "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/http2-wrapper": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.0.tgz", - "integrity": "sha512-kZB0wxMo0sh1PehyjJUWRFEd99KC5TLjZ2cULC4f9iqJBAmKQQXEICjxl5iPJRwP40dpeHFqqhm7tYCvODpqpQ==", - "dependencies": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.2.0" - }, - "engines": { - "node": ">=10.19.0" - } - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/icss-utils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", - "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", - "engines": { - "node": ">= 4" - } - }, - "node_modules/image-size": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.1.1.tgz", - "integrity": "sha512-541xKlUw6jr/6gGuk92F+mYM5zaFAc5ahphvkqvNe2bQ6gVBkd6bfrmVJ2t4KDAfikAYZyIqTnktX3i6/aQDrQ==", - "dependencies": { - "queue": "6.0.2" - }, - "bin": { - "image-size": "bin/image-size.js" - }, - "engines": { - "node": ">=16.x" - } - }, - "node_modules/immer": { - "version": "9.0.21", - "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", - "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/immer" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-lazy": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", - "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/infima": { - "version": "0.2.0-alpha.43", - "resolved": "https://registry.npmjs.org/infima/-/infima-0.2.0-alpha.43.tgz", - "integrity": "sha512-2uw57LvUqW0rK/SWYnd/2rRfxNA5DDNOh33jxF7fy46VWoNhGxiUQyVZHbBMjQ33mQem0cjdDVwgWVAmlRfgyQ==", - "engines": { - "node": ">=12" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" - }, - "node_modules/inline-style-parser": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", - "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==" - }, - "node_modules/internmap": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", - "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", - "engines": { - "node": ">=12" - } - }, - "node_modules/interpret": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/intersection-observer": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/intersection-observer/-/intersection-observer-0.10.0.tgz", - "integrity": "sha512-fn4bQ0Xq8FTej09YC/jqKZwtijpvARlRp6wxL5WTA6yPe2YWSJ5RJh7Nm79rK2qB0wr6iDQzH60XGq5V/7u8YQ==" - }, - "node_modules/invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "dependencies": { - "loose-envify": "^1.0.0" - } - }, - "node_modules/ipaddr.js": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.1.0.tgz", - "integrity": "sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ==", - "engines": { - "node": ">= 10" - } - }, - "node_modules/is-alphabetical": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", - "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-alphanumerical": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", - "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", - "dependencies": { - "is-alphabetical": "^2.0.0", - "is-decimal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-ci": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", - "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", - "dependencies": { - "ci-info": "^3.2.0" - }, - "bin": { - "is-ci": "bin.js" - } - }, - "node_modules/is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", - "dependencies": { - "hasown": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-decimal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", - "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-hexadecimal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", - "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-installed-globally": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", - "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", - "dependencies": { - "global-dirs": "^3.0.0", - "is-path-inside": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-npm": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-6.0.0.tgz", - "integrity": "sha512-JEjxbSmtPSt1c8XTkVrlujcXdKV1/tvuQ7GwKcAlyiVLeYFQ2VHat8xfrDJsIkhCdF/tZ7CiIR3sy141c6+gPQ==", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-path-cwd": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", - "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-plain-obj": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", - "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-reference": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz", - "integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==", - "dependencies": { - "@types/estree": "*" - } - }, - "node_modules/is-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", - "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-root": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", - "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" - }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-yarn-global": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.4.1.tgz", - "integrity": "sha512-/kppl+R+LO5VmhYSEWARUFjodS25D68gvj8W7z0I7OWhUla5xWu8KL6CtB2V0R6yqhnRgbcaREMr4EEM6htLPQ==", - "engines": { - "node": ">=12" - } - }, - "node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" - }, - "node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/jest-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", - "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "dependencies": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/jiti": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", - "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==", - "bin": { - "jiti": "bin/jiti.js" - } - }, - "node_modules/joi": { - "version": "17.11.0", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.11.0.tgz", - "integrity": "sha512-NgB+lZLNoqISVy1rZocE9PZI36bL/77ie924Ri43yEvi9GUUMPeyVIr8KdFTMUlby1p0PBYMk9spIxEUQYqrJQ==", - "dependencies": { - "@hapi/hoek": "^9.0.0", - "@hapi/topo": "^5.0.0", - "@sideway/address": "^4.1.3", - "@sideway/formula": "^3.0.1", - "@sideway/pinpoint": "^2.0.0" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" - }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/khroma": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz", - "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==" - }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "engines": { - "node": ">=6" - } - }, - "node_modules/latest-version": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-7.0.0.tgz", - "integrity": "sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg==", - "dependencies": { - "package-json": "^8.1.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/launch-editor": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.6.1.tgz", - "integrity": "sha512-eB/uXmFVpY4zezmGp5XtU21kwo7GBbKB+EQ+UZeWtGb9yAM5xt/Evk+lYH3eRNAtId+ej4u7TYPFZ07w4s7rRw==", - "dependencies": { - "picocolors": "^1.0.0", - "shell-quote": "^1.8.1" - } - }, - "node_modules/layout-base": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz", - "integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==" - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "engines": { - "node": ">=6" - } - }, - "node_modules/lilconfig": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", - "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" - }, - "node_modules/loader-runner": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", - "engines": { - "node": ">=6.11.5" - } - }, - "node_modules/loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - }, - "engines": { - "node": ">=8.9.0" - } - }, - "node_modules/locate-path": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", - "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", - "dependencies": { - "p-locate": "^6.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/lodash-es": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" - }, - "node_modules/lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" - }, - "node_modules/lodash.escape": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-4.0.1.tgz", - "integrity": "sha512-nXEOnb/jK9g0DYMr1/Xvq6l5xMD7GDG55+GSYIYmS0G4tBk/hURD4JR9WCavs04t33WmJx9kCyp9vJ+mr4BOUw==" - }, - "node_modules/lodash.flatten": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==" - }, - "node_modules/lodash.invokemap": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.invokemap/-/lodash.invokemap-4.6.0.tgz", - "integrity": "sha512-CfkycNtMqgUlfjfdh2BhKO/ZXrP8ePOX5lEU/g0R3ItJcnuxWDwokMGKx1hWcfOikmyOVx6X9IwWnDGlgKl61w==" - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==" - }, - "node_modules/lodash.pullall": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.pullall/-/lodash.pullall-4.2.0.tgz", - "integrity": "sha512-VhqxBKH0ZxPpLhiu68YD1KnHmbhQJQctcipvmFnqIBDYzcIHzf3Zpu0tpeOKtR4x76p9yohc506eGdOjTmyIBg==" - }, - "node_modules/lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==" - }, - "node_modules/lodash.uniqby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz", - "integrity": "sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==" - }, - "node_modules/longest-streak": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", - "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/lower-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", - "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/lowercase-keys": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", - "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/lz-string": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", - "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", - "bin": { - "lz-string": "bin/bin.js" - } - }, - "node_modules/markdown-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-2.0.0.tgz", - "integrity": "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/markdown-table": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.3.tgz", - "integrity": "sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/mdast-util-directive": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-directive/-/mdast-util-directive-3.0.0.tgz", - "integrity": "sha512-JUpYOqKI4mM3sZcNxmF/ox04XYFFkNwr0CFlrQIkCwbvH0xzMCqkMqAde9wRd80VAhaUrwFwKm2nxretdT1h7Q==", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "parse-entities": "^4.0.0", - "stringify-entities": "^4.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-find-and-replace": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.1.tgz", - "integrity": "sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA==", - "dependencies": { - "@types/mdast": "^4.0.0", - "escape-string-regexp": "^5.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mdast-util-from-markdown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.0.tgz", - "integrity": "sha512-n7MTOr/z+8NAX/wmhhDji8O3bRvPTV/U0oTCaZJkjhPSKTPhS3xufVhKGF8s1pJ7Ox4QgoIU7KHseh09S+9rTA==", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark": "^4.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-decode-string": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-from-markdown/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/mdast-util-frontmatter": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-frontmatter/-/mdast-util-frontmatter-2.0.1.tgz", - "integrity": "sha512-LRqI9+wdgC25P0URIJY9vwocIzCcksduHQ9OF2joxQoyTNVduwLAFUzjoopuRJbJAReaKrNQKAZKL3uCMugWJA==", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "escape-string-regexp": "^5.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "micromark-extension-frontmatter": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-frontmatter/node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mdast-util-gfm": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.0.0.tgz", - "integrity": "sha512-dgQEX5Amaq+DuUqf26jJqSK9qgixgd6rYDHAv4aTBuA92cTknZlKpPfa86Z/s8Dj8xsAQpFfBmPUHWJBWqS4Bw==", - "dependencies": { - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-gfm-autolink-literal": "^2.0.0", - "mdast-util-gfm-footnote": "^2.0.0", - "mdast-util-gfm-strikethrough": "^2.0.0", - "mdast-util-gfm-table": "^2.0.0", - "mdast-util-gfm-task-list-item": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-autolink-literal": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.0.tgz", - "integrity": "sha512-FyzMsduZZHSc3i0Px3PQcBT4WJY/X/RCtEJKuybiC6sjPqLv7h1yqAkmILZtuxMSsUyaLUWNp71+vQH2zqp5cg==", - "dependencies": { - "@types/mdast": "^4.0.0", - "ccount": "^2.0.0", - "devlop": "^1.0.0", - "mdast-util-find-and-replace": "^3.0.0", - "micromark-util-character": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-autolink-literal/node_modules/micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/mdast-util-gfm-autolink-literal/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/mdast-util-gfm-footnote": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.0.0.tgz", - "integrity": "sha512-5jOT2boTSVkMnQ7LTrd6n/18kqwjmuYqo7JUPe+tRCY6O7dAuTFMtTPauYYrMPpox9hlN0uOx/FL8XvEfG9/mQ==", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.1.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-strikethrough": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", - "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-table": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", - "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "markdown-table": "^3.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-task-list-item": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", - "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-3.0.0.tgz", - "integrity": "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==", - "dependencies": { - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-mdx-expression": "^2.0.0", - "mdast-util-mdx-jsx": "^3.0.0", - "mdast-util-mdxjs-esm": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx-expression": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.0.tgz", - "integrity": "sha512-fGCu8eWdKUKNu5mohVGkhBXCXGnOTLuFqOvGMvdikr+J1w7lDJgxThOKpwRWzzbyXAU2hhSwsmssOY4yTokluw==", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx-jsx": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.1.1.tgz", - "integrity": "sha512-Di63TQEHbiApe6CFp/qQXCORHMHnmW2JFdr5PYH57LuEIPjijRHicAmL5wQu+B0/Q4p0qJaEOE1EkhiwxiNmAQ==", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "ccount": "^2.0.0", - "devlop": "^1.1.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "parse-entities": "^4.0.0", - "stringify-entities": "^4.0.0", - "unist-util-remove-position": "^5.0.0", - "unist-util-stringify-position": "^4.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdxjs-esm": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", - "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-phrasing": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", - "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", - "dependencies": { - "@types/mdast": "^4.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-hast": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.1.0.tgz", - "integrity": "sha512-/e2l/6+OdGp/FB+ctrJ9Avz71AN/GRH3oi/3KAx/kMnoUsD6q0woXlDT8lLEeViVKE7oZxE7RXzvO3T8kF2/sA==", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@ungap/structured-clone": "^1.0.0", - "devlop": "^1.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "trim-lines": "^3.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-markdown": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.0.tgz", - "integrity": "sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ==", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "longest-streak": "^3.0.0", - "mdast-util-phrasing": "^4.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark-util-decode-string": "^2.0.0", - "unist-util-visit": "^5.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", - "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", - "dependencies": { - "@types/mdast": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdn-data": { - "version": "2.0.30", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", - "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==" - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/memfs": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", - "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", - "dependencies": { - "fs-monkey": "^1.0.4" - }, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/mermaid": { - "version": "10.6.1", - "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-10.6.1.tgz", - "integrity": "sha512-Hky0/RpOw/1il9X8AvzOEChfJtVvmXm+y7JML5C//ePYMy0/9jCEmW1E1g86x9oDfW9+iVEdTV/i+M6KWRNs4A==", - "dependencies": { - "@braintree/sanitize-url": "^6.0.1", - "@types/d3-scale": "^4.0.3", - "@types/d3-scale-chromatic": "^3.0.0", - "cytoscape": "^3.23.0", - "cytoscape-cose-bilkent": "^4.1.0", - "cytoscape-fcose": "^2.1.0", - "d3": "^7.4.0", - "d3-sankey": "^0.12.3", - "dagre-d3-es": "7.0.10", - "dayjs": "^1.11.7", - "dompurify": "^3.0.5", - "elkjs": "^0.8.2", - "khroma": "^2.0.0", - "lodash-es": "^4.17.21", - "mdast-util-from-markdown": "^1.3.0", - "non-layered-tidy-tree-layout": "^2.0.2", - "stylis": "^4.1.3", - "ts-dedent": "^2.2.0", - "uuid": "^9.0.0", - "web-worker": "^1.2.0" - } - }, - "node_modules/mermaid/node_modules/@types/mdast": { - "version": "3.0.15", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz", - "integrity": "sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==", - "dependencies": { - "@types/unist": "^2" - } - }, - "node_modules/mermaid/node_modules/@types/unist": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz", - "integrity": "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==" - }, - "node_modules/mermaid/node_modules/mdast-util-from-markdown": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-1.3.1.tgz", - "integrity": "sha512-4xTO/M8c82qBcnQc1tgpNtubGUW/Y1tBQ1B0i5CtSoelOLKFYlElIr3bvgREYYO5iRqbMY1YuqZng0GVOI8Qww==", - "dependencies": { - "@types/mdast": "^3.0.0", - "@types/unist": "^2.0.0", - "decode-named-character-reference": "^1.0.0", - "mdast-util-to-string": "^3.1.0", - "micromark": "^3.0.0", - "micromark-util-decode-numeric-character-reference": "^1.0.0", - "micromark-util-decode-string": "^1.0.0", - "micromark-util-normalize-identifier": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0", - "unist-util-stringify-position": "^3.0.0", - "uvu": "^0.5.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mermaid/node_modules/mdast-util-to-string": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.2.0.tgz", - "integrity": "sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==", - "dependencies": { - "@types/mdast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mermaid/node_modules/micromark": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.2.0.tgz", - "integrity": "sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "@types/debug": "^4.0.0", - "debug": "^4.0.0", - "decode-named-character-reference": "^1.0.0", - "micromark-core-commonmark": "^1.0.1", - "micromark-factory-space": "^1.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-chunked": "^1.0.0", - "micromark-util-combine-extensions": "^1.0.0", - "micromark-util-decode-numeric-character-reference": "^1.0.0", - "micromark-util-encode": "^1.0.0", - "micromark-util-normalize-identifier": "^1.0.0", - "micromark-util-resolve-all": "^1.0.0", - "micromark-util-sanitize-uri": "^1.0.0", - "micromark-util-subtokenize": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.1", - "uvu": "^0.5.0" - } - }, - "node_modules/mermaid/node_modules/micromark-core-commonmark": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-1.1.0.tgz", - "integrity": "sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "micromark-factory-destination": "^1.0.0", - "micromark-factory-label": "^1.0.0", - "micromark-factory-space": "^1.0.0", - "micromark-factory-title": "^1.0.0", - "micromark-factory-whitespace": "^1.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-chunked": "^1.0.0", - "micromark-util-classify-character": "^1.0.0", - "micromark-util-html-tag-name": "^1.0.0", - "micromark-util-normalize-identifier": "^1.0.0", - "micromark-util-resolve-all": "^1.0.0", - "micromark-util-subtokenize": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.1", - "uvu": "^0.5.0" - } - }, - "node_modules/mermaid/node_modules/micromark-factory-destination": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-1.1.0.tgz", - "integrity": "sha512-XaNDROBgx9SgSChd69pjiGKbV+nfHGDPVYFs5dOoDd7ZnMAE+Cuu91BCpsY8RT2NP9vo/B8pds2VQNCLiu0zhg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0" - } - }, - "node_modules/mermaid/node_modules/micromark-factory-label": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-1.1.0.tgz", - "integrity": "sha512-OLtyez4vZo/1NjxGhcpDSbHQ+m0IIGnT8BoPamh+7jVlzLJBH98zzuCoUeMxvM6WsNeh8wx8cKvqLiPHEACn0w==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0", - "uvu": "^0.5.0" - } - }, - "node_modules/mermaid/node_modules/micromark-factory-title": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-1.1.0.tgz", - "integrity": "sha512-J7n9R3vMmgjDOCY8NPw55jiyaQnH5kBdV2/UXCtZIpnHH3P6nHUKaH7XXEYuWwx/xUJcawa8plLBEjMPU24HzQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-factory-space": "^1.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0" - } - }, - "node_modules/mermaid/node_modules/micromark-factory-whitespace": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-1.1.0.tgz", - "integrity": "sha512-v2WlmiymVSp5oMg+1Q0N1Lxmt6pMhIHD457whWM7/GUlEks1hI9xj5w3zbc4uuMKXGisksZk8DzP2UyGbGqNsQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-factory-space": "^1.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0" - } - }, - "node_modules/mermaid/node_modules/micromark-util-chunked": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-1.1.0.tgz", - "integrity": "sha512-Ye01HXpkZPNcV6FiyoW2fGZDUw4Yc7vT0E9Sad83+bEDiCJ1uXu0S3mr8WLpsz3HaG3x2q0HM6CTuPdcZcluFQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^1.0.0" - } - }, - "node_modules/mermaid/node_modules/micromark-util-classify-character": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-1.1.0.tgz", - "integrity": "sha512-SL0wLxtKSnklKSUplok1WQFoGhUdWYKggKUiqhX+Swala+BtptGCu5iPRc+xvzJ4PXE/hwM3FNXsfEVgoZsWbw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0" - } - }, - "node_modules/mermaid/node_modules/micromark-util-combine-extensions": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.1.0.tgz", - "integrity": "sha512-Q20sp4mfNf9yEqDL50WwuWZHUrCO4fEyeDCnMGmG5Pr0Cz15Uo7KBs6jq+dq0EgX4DPwwrh9m0X+zPV1ypFvUA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-chunked": "^1.0.0", - "micromark-util-types": "^1.0.0" - } - }, - "node_modules/mermaid/node_modules/micromark-util-decode-numeric-character-reference": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.1.0.tgz", - "integrity": "sha512-m9V0ExGv0jB1OT21mrWcuf4QhP46pH1KkfWy9ZEezqHKAxkj4mPCy3nIH1rkbdMlChLHX531eOrymlwyZIf2iw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^1.0.0" - } - }, - "node_modules/mermaid/node_modules/micromark-util-decode-string": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-1.1.0.tgz", - "integrity": "sha512-YphLGCK8gM1tG1bd54azwyrQRjCFcmgj2S2GoJDNnh4vYtnL38JS8M4gpxzOPNyHdNEpheyWXCTnnTDY3N+NVQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-decode-numeric-character-reference": "^1.0.0", - "micromark-util-symbol": "^1.0.0" - } - }, - "node_modules/mermaid/node_modules/micromark-util-encode": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-1.1.0.tgz", - "integrity": "sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/mermaid/node_modules/micromark-util-html-tag-name": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.2.0.tgz", - "integrity": "sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/mermaid/node_modules/micromark-util-normalize-identifier": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.1.0.tgz", - "integrity": "sha512-N+w5vhqrBihhjdpM8+5Xsxy71QWqGn7HYNUvch71iV2PM7+E3uWGox1Qp90loa1ephtCxG2ftRV/Conitc6P2Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^1.0.0" - } - }, - "node_modules/mermaid/node_modules/micromark-util-resolve-all": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-1.1.0.tgz", - "integrity": "sha512-b/G6BTMSg+bX+xVCshPTPyAu2tmA0E4X98NSR7eIbeC6ycCqCeE7wjfDIgzEbkzdEVJXRtOG4FbEm/uGbCRouA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-types": "^1.0.0" - } - }, - "node_modules/mermaid/node_modules/micromark-util-sanitize-uri": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.2.0.tgz", - "integrity": "sha512-QO4GXv0XZfWey4pYFndLUKEAktKkG5kZTdUNaTAkzbuJxn2tNBOr+QtxR2XpWaMhbImT2dPzyLrPXLlPhph34A==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^1.0.0", - "micromark-util-encode": "^1.0.0", - "micromark-util-symbol": "^1.0.0" - } - }, - "node_modules/mermaid/node_modules/micromark-util-subtokenize": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-1.1.0.tgz", - "integrity": "sha512-kUQHyzRoxvZO2PuLzMt2P/dwVsTiivCK8icYTeR+3WgbuPqfHgPPy7nFKbeqRivBvn/3N3GBiNC+JRTMSxEC7A==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-chunked": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0", - "uvu": "^0.5.0" - } - }, - "node_modules/mermaid/node_modules/micromark-util-types": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz", - "integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/mermaid/node_modules/unist-util-stringify-position": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.3.tgz", - "integrity": "sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==", - "dependencies": { - "@types/unist": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mermaid/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/micromark": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.0.tgz", - "integrity": "sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "@types/debug": "^4.0.0", - "debug": "^4.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-core-commonmark": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.0.tgz", - "integrity": "sha512-jThOz/pVmAYUtkroV3D5c1osFXAMv9e0ypGDOIZuCeAe91/sD6BoE2Sjzt30yuXtwOYUmySOhMas/PVyh02itA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-factory-destination": "^2.0.0", - "micromark-factory-label": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-factory-title": "^2.0.0", - "micromark-factory-whitespace": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-html-tag-name": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-core-commonmark/node_modules/micromark-factory-space": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", - "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-core-commonmark/node_modules/micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-core-commonmark/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-extension-directive": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-3.0.0.tgz", - "integrity": "sha512-61OI07qpQrERc+0wEysLHMvoiO3s2R56x5u7glHq2Yqq6EHbH4dW25G9GfDdGCDYqA21KE6DWgNSzxSwHc2hSg==", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-factory-whitespace": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "parse-entities": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-directive/node_modules/micromark-factory-space": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", - "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-directive/node_modules/micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-directive/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-extension-frontmatter": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-frontmatter/-/micromark-extension-frontmatter-2.0.0.tgz", - "integrity": "sha512-C4AkuM3dA58cgZha7zVnuVxBhDsbttIMiytjgsM2XbHAB2faRVaHRle40558FBN+DJcrLNCoqG5mlrpdU4cRtg==", - "dependencies": { - "fault": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-frontmatter/node_modules/micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-frontmatter/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-extension-gfm": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", - "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", - "dependencies": { - "micromark-extension-gfm-autolink-literal": "^2.0.0", - "micromark-extension-gfm-footnote": "^2.0.0", - "micromark-extension-gfm-strikethrough": "^2.0.0", - "micromark-extension-gfm-table": "^2.0.0", - "micromark-extension-gfm-tagfilter": "^2.0.0", - "micromark-extension-gfm-task-list-item": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-autolink-literal": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.0.0.tgz", - "integrity": "sha512-rTHfnpt/Q7dEAK1Y5ii0W8bhfJlVJFnJMHIPisfPK3gpVNuOP0VnRl96+YJ3RYWV/P4gFeQoGKNlT3RhuvpqAg==", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-autolink-literal/node_modules/micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-gfm-autolink-literal/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-extension-gfm-footnote": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.0.0.tgz", - "integrity": "sha512-6Rzu0CYRKDv3BfLAUnZsSlzx3ak6HAoI85KTiijuKIz5UxZxbUI+pD6oHgw+6UtQuiRwnGRhzMmPRv4smcz0fg==", - "dependencies": { - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-factory-space": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", - "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-extension-gfm-strikethrough": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.0.0.tgz", - "integrity": "sha512-c3BR1ClMp5fxxmwP6AoOY2fXO9U8uFMKs4ADD66ahLTNcwzSCyRVU4k7LPV5Nxo/VJiR4TdzxRQY2v3qIUceCw==", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-strikethrough/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-extension-gfm-table": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.0.0.tgz", - "integrity": "sha512-PoHlhypg1ItIucOaHmKE8fbin3vTLpDOUg8KAr8gRCF1MOZI9Nquq2i/44wFvviM4WuxJzc3demT8Y3dkfvYrw==", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-table/node_modules/micromark-factory-space": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", - "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-gfm-table/node_modules/micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-gfm-table/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-extension-gfm-tagfilter": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", - "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", - "dependencies": { - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-task-list-item": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.0.1.tgz", - "integrity": "sha512-cY5PzGcnULaN5O7T+cOzfMoHjBW7j+T9D2sucA5d/KbsBTPcYdebm9zUd9zzdgJGCwahV+/W78Z3nbulBYVbTw==", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-task-list-item/node_modules/micromark-factory-space": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", - "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-gfm-task-list-item/node_modules/micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-gfm-task-list-item/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-extension-mdx-expression": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-3.0.0.tgz", - "integrity": "sha512-sI0nwhUDz97xyzqJAbHQhp5TfaxEvZZZ2JDqUo+7NvyIYG6BZ5CPPqj2ogUoPJlmXHBnyZUzISg9+oUmU6tUjQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0", - "micromark-factory-mdx-expression": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-events-to-acorn": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-mdx-expression/node_modules/micromark-factory-space": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", - "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-mdx-expression/node_modules/micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-mdx-expression/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-extension-mdx-jsx": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-3.0.0.tgz", - "integrity": "sha512-uvhhss8OGuzR4/N17L1JwvmJIpPhAd8oByMawEKx6NVdBCbesjH4t+vjEp3ZXft9DwvlKSD07fCeI44/N0Vf2w==", - "dependencies": { - "@types/acorn": "^4.0.0", - "@types/estree": "^1.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "micromark-factory-mdx-expression": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdx-jsx/node_modules/micromark-factory-space": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", - "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-mdx-jsx/node_modules/micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-mdx-jsx/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-extension-mdx-md": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-mdx-md/-/micromark-extension-mdx-md-2.0.0.tgz", - "integrity": "sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==", - "dependencies": { - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdxjs": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs/-/micromark-extension-mdxjs-3.0.0.tgz", - "integrity": "sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==", - "dependencies": { - "acorn": "^8.0.0", - "acorn-jsx": "^5.0.0", - "micromark-extension-mdx-expression": "^3.0.0", - "micromark-extension-mdx-jsx": "^3.0.0", - "micromark-extension-mdx-md": "^2.0.0", - "micromark-extension-mdxjs-esm": "^3.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdxjs-esm": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-3.0.0.tgz", - "integrity": "sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==", - "dependencies": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-events-to-acorn": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unist-util-position-from-estree": "^2.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdxjs-esm/node_modules/micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-mdxjs-esm/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-factory-destination": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.0.tgz", - "integrity": "sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-destination/node_modules/micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-destination/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-factory-label": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.0.tgz", - "integrity": "sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-label/node_modules/micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-label/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-factory-mdx-expression": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-2.0.1.tgz", - "integrity": "sha512-F0ccWIUHRLRrYp5TC9ZYXmZo+p2AM13ggbsW4T0b5CRKP8KHVRB8t4pwtBgTxtjRmwrK0Irwm7vs2JOZabHZfg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-events-to-acorn": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unist-util-position-from-estree": "^2.0.0", - "vfile-message": "^4.0.0" - } - }, - "node_modules/micromark-factory-mdx-expression/node_modules/micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-mdx-expression/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-factory-space": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-1.1.0.tgz", - "integrity": "sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^1.0.0", - "micromark-util-types": "^1.0.0" - } - }, - "node_modules/micromark-factory-space/node_modules/micromark-util-types": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz", - "integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-factory-title": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.0.tgz", - "integrity": "sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-title/node_modules/micromark-factory-space": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", - "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-title/node_modules/micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-title/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-factory-whitespace": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.0.tgz", - "integrity": "sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-whitespace/node_modules/micromark-factory-space": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", - "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-whitespace/node_modules/micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-whitespace/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-util-character": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-1.2.0.tgz", - "integrity": "sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0" - } - }, - "node_modules/micromark-util-character/node_modules/micromark-util-types": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz", - "integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-util-chunked": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.0.tgz", - "integrity": "sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-chunked/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-util-classify-character": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.0.tgz", - "integrity": "sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-classify-character/node_modules/micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-classify-character/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-util-combine-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.0.tgz", - "integrity": "sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-chunked": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-numeric-character-reference": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.1.tgz", - "integrity": "sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-numeric-character-reference/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-util-decode-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.0.tgz", - "integrity": "sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-string/node_modules/micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-string/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-util-encode": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz", - "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-util-events-to-acorn": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-2.0.2.tgz", - "integrity": "sha512-Fk+xmBrOv9QZnEDguL9OI9/NQQp6Hz4FuQ4YmCb/5V7+9eAh1s6AYSvL20kHkD67YIg7EpE54TiSlcsf3vyZgA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "@types/acorn": "^4.0.0", - "@types/estree": "^1.0.0", - "@types/unist": "^3.0.0", - "devlop": "^1.0.0", - "estree-util-visit": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "vfile-message": "^4.0.0" - } - }, - "node_modules/micromark-util-events-to-acorn/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-util-html-tag-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.0.tgz", - "integrity": "sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-util-normalize-identifier": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.0.tgz", - "integrity": "sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-normalize-identifier/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-util-resolve-all": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.0.tgz", - "integrity": "sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-sanitize-uri": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz", - "integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-sanitize-uri/node_modules/micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-sanitize-uri/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-util-subtokenize": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.0.tgz", - "integrity": "sha512-vc93L1t+gpR3p8jxeVdaYlbV2jTYteDje19rNSS/H5dlhxUYll5Fy6vJ2cDwP8RnsXi818yGty1ayP55y3W6fg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-subtokenize/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-util-symbol": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.1.0.tgz", - "integrity": "sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-util-types": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.0.tgz", - "integrity": "sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark/node_modules/micromark-factory-space": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", - "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark/node_modules/micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", - "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.18", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", - "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", - "dependencies": { - "mime-db": "~1.33.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/mimic-response": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", - "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mini-css-extract-plugin": { - "version": "2.7.6", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.6.tgz", - "integrity": "sha512-Qk7HcgaPkGG6eD77mLvZS1nmxlao3j+9PkrT9Uc7HAE1id3F41+DdBRYRYkbyfNRGzm8/YWtzhw7nVPmwhqTQw==", - "dependencies": { - "schema-utils": "^4.0.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - } - }, - "node_modules/minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/mri": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", - "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/mrmime": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz", - "integrity": "sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==", - "engines": { - "node": ">=10" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/multicast-dns": { - "version": "7.2.5", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", - "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", - "dependencies": { - "dns-packet": "^5.2.2", - "thunky": "^1.0.2" - }, - "bin": { - "multicast-dns": "cli.js" - } - }, - "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" - }, - "node_modules/next-tick": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", - "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" - }, - "node_modules/no-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", - "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", - "dependencies": { - "lower-case": "^2.0.2", - "tslib": "^2.0.3" - } - }, - "node_modules/node-emoji": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.1.3.tgz", - "integrity": "sha512-E2WEOVsgs7O16zsURJ/eH8BqhF029wGpEOnv7Urwdo2wmQanOACwJQh0devF9D9RhoZru0+9JXIS0dBXIAz+lA==", - "dependencies": { - "@sindresorhus/is": "^4.6.0", - "char-regex": "^1.0.2", - "emojilib": "^2.4.0", - "skin-tone": "^2.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/node-forge": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", - "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", - "engines": { - "node": ">= 6.13.0" - } - }, - "node_modules/node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==" - }, - "node_modules/non-layered-tidy-tree-layout": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/non-layered-tidy-tree-layout/-/non-layered-tidy-tree-layout-2.0.2.tgz", - "integrity": "sha512-gkXMxRzUH+PB0ax9dUN0yYF0S25BqeAYqhgMaLUFmpXLEk7Fcu8f4emJuOAY0V8kjDICxROIKsTAKsV/v355xw==" - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nprogress": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz", - "integrity": "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==" - }, - "node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/obuf": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", - "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/open": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", - "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", - "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/opener": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", - "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", - "bin": { - "opener": "bin/opener-bin.js" - } - }, - "node_modules/outvariant": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.0.tgz", - "integrity": "sha512-AlWY719RF02ujitly7Kk/0QlV+pXGFDHrHf9O2OKqyqgBieaPOIeuSkL8sRK6j2WK+/ZAURq2kZsY0d8JapUiw==" - }, - "node_modules/p-cancelable": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", - "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==", - "engines": { - "node": ">=12.20" - } - }, - "node_modules/p-limit": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", - "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", - "dependencies": { - "yocto-queue": "^1.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", - "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", - "dependencies": { - "p-limit": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-retry": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", - "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", - "dependencies": { - "@types/retry": "0.12.0", - "retry": "^0.13.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/package-json": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-8.1.1.tgz", - "integrity": "sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==", - "dependencies": { - "got": "^12.1.0", - "registry-auth-token": "^5.0.1", - "registry-url": "^6.0.0", - "semver": "^7.3.7" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/param-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", - "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", - "dependencies": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-entities": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.1.tgz", - "integrity": "sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==", - "dependencies": { - "@types/unist": "^2.0.0", - "character-entities": "^2.0.0", - "character-entities-legacy": "^3.0.0", - "character-reference-invalid": "^2.0.0", - "decode-named-character-reference": "^1.0.0", - "is-alphanumerical": "^2.0.0", - "is-decimal": "^2.0.0", - "is-hexadecimal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/parse-entities/node_modules/@types/unist": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz", - "integrity": "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==" - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parse-numeric-range": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/parse-numeric-range/-/parse-numeric-range-1.3.0.tgz", - "integrity": "sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ==" - }, - "node_modules/parse5": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", - "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", - "dependencies": { - "entities": "^4.4.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/parse5-htmlparser2-tree-adapter": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", - "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", - "dependencies": { - "domhandler": "^5.0.2", - "parse5": "^7.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/pascal-case": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", - "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/path-exists": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", - "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==" - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" - }, - "node_modules/path-to-regexp": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", - "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", - "dependencies": { - "isarray": "0.0.1" - } - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/periscopic": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", - "integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^3.0.0", - "is-reference": "^3.0.0" - } - }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pkg-dir": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-7.0.0.tgz", - "integrity": "sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==", - "dependencies": { - "find-up": "^6.3.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-up": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", - "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", - "dependencies": { - "find-up": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-up/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-up/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-up/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-up/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-up/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss": { - "version": "8.4.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", - "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-calc": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-9.0.1.tgz", - "integrity": "sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==", - "dependencies": { - "postcss-selector-parser": "^6.0.11", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.2.2" - } - }, - "node_modules/postcss-colormin": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-6.1.0.tgz", - "integrity": "sha512-x9yX7DOxeMAR+BgGVnNSAxmAj98NX/YxEMNFP+SDCEeNLb2r3i6Hh1ksMsnW8Ub5SLCpbescQqn9YEbE9554Sw==", - "dependencies": { - "browserslist": "^4.23.0", - "caniuse-api": "^3.0.0", - "colord": "^2.9.3", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-convert-values": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-6.1.0.tgz", - "integrity": "sha512-zx8IwP/ts9WvUM6NkVSkiU902QZL1bwPhaVaLynPtCsOTqp+ZKbNi+s6XJg3rfqpKGA/oc7Oxk5t8pOQJcwl/w==", - "dependencies": { - "browserslist": "^4.23.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-discard-comments": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-6.0.2.tgz", - "integrity": "sha512-65w/uIqhSBBfQmYnG92FO1mWZjJ4GL5b8atm5Yw2UgrwD7HiNiSSNwJor1eCFGzUgYnN/iIknhNRVqjrrpuglw==", - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-discard-duplicates": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-6.0.3.tgz", - "integrity": "sha512-+JA0DCvc5XvFAxwx6f/e68gQu/7Z9ud584VLmcgto28eB8FqSFZwtrLwB5Kcp70eIoWP/HXqz4wpo8rD8gpsTw==", - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-discard-empty": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-6.0.3.tgz", - "integrity": "sha512-znyno9cHKQsK6PtxL5D19Fj9uwSzC2mB74cpT66fhgOadEUPyXFkbgwm5tvc3bt3NAy8ltE5MrghxovZRVnOjQ==", - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-discard-overridden": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-6.0.2.tgz", - "integrity": "sha512-j87xzI4LUggC5zND7KdjsI25APtyMuynXZSujByMaav2roV6OZX+8AaCUcZSWqckZpjAjRyFDdpqybgjFO0HJQ==", - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-discard-unused": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-6.0.5.tgz", - "integrity": "sha512-wHalBlRHkaNnNwfC8z+ppX57VhvS+HWgjW508esjdaEYr3Mx7Gnn2xA4R/CKf5+Z9S5qsqC+Uzh4ueENWwCVUA==", - "dependencies": { - "postcss-selector-parser": "^6.0.16" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-loader": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.3.3.tgz", - "integrity": "sha512-YgO/yhtevGO/vJePCQmTxiaEwER94LABZN0ZMT4A0vsak9TpO+RvKRs7EmJ8peIlB9xfXCsS7M8LjqncsUZ5HA==", - "dependencies": { - "cosmiconfig": "^8.2.0", - "jiti": "^1.18.2", - "semver": "^7.3.8" - }, - "engines": { - "node": ">= 14.15.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "postcss": "^7.0.0 || ^8.0.1", - "webpack": "^5.0.0" - } - }, - "node_modules/postcss-merge-idents": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-6.0.3.tgz", - "integrity": "sha512-1oIoAsODUs6IHQZkLQGO15uGEbK3EAl5wi9SS8hs45VgsxQfMnxvt+L+zIr7ifZFIH14cfAeVe2uCTa+SPRa3g==", - "dependencies": { - "cssnano-utils": "^4.0.2", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-merge-longhand": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-6.0.5.tgz", - "integrity": "sha512-5LOiordeTfi64QhICp07nzzuTDjNSO8g5Ksdibt44d+uvIIAE1oZdRn8y/W5ZtYgRH/lnLDlvi9F8btZcVzu3w==", - "dependencies": { - "postcss-value-parser": "^4.2.0", - "stylehacks": "^6.1.1" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-merge-rules": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-6.1.1.tgz", - "integrity": "sha512-KOdWF0gju31AQPZiD+2Ar9Qjowz1LTChSjFFbS+e2sFgc4uHOp3ZvVX4sNeTlk0w2O31ecFGgrFzhO0RSWbWwQ==", - "dependencies": { - "browserslist": "^4.23.0", - "caniuse-api": "^3.0.0", - "cssnano-utils": "^4.0.2", - "postcss-selector-parser": "^6.0.16" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-minify-font-values": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-6.1.0.tgz", - "integrity": "sha512-gklfI/n+9rTh8nYaSJXlCo3nOKqMNkxuGpTn/Qm0gstL3ywTr9/WRKznE+oy6fvfolH6dF+QM4nCo8yPLdvGJg==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-minify-gradients": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-6.0.3.tgz", - "integrity": "sha512-4KXAHrYlzF0Rr7uc4VrfwDJ2ajrtNEpNEuLxFgwkhFZ56/7gaE4Nr49nLsQDZyUe+ds+kEhf+YAUolJiYXF8+Q==", - "dependencies": { - "colord": "^2.9.3", - "cssnano-utils": "^4.0.2", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-minify-params": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-6.1.0.tgz", - "integrity": "sha512-bmSKnDtyyE8ujHQK0RQJDIKhQ20Jq1LYiez54WiaOoBtcSuflfK3Nm596LvbtlFcpipMjgClQGyGr7GAs+H1uA==", - "dependencies": { - "browserslist": "^4.23.0", - "cssnano-utils": "^4.0.2", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-minify-selectors": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-6.0.4.tgz", - "integrity": "sha512-L8dZSwNLgK7pjTto9PzWRoMbnLq5vsZSTu8+j1P/2GB8qdtGQfn+K1uSvFgYvgh83cbyxT5m43ZZhUMTJDSClQ==", - "dependencies": { - "postcss-selector-parser": "^6.0.16" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-modules-extract-imports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", - "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-local-by-default": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.3.tgz", - "integrity": "sha512-2/u2zraspoACtrbFRnTijMiQtb4GW4BvatjaG/bCjYQo8kLTdevCUlwuBHx2sCnSyrI3x3qj4ZK1j5LQBgzmwA==", - "dependencies": { - "icss-utils": "^5.0.0", - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-scope": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", - "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", - "dependencies": { - "postcss-selector-parser": "^6.0.4" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-values": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", - "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", - "dependencies": { - "icss-utils": "^5.0.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-normalize-charset": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-6.0.2.tgz", - "integrity": "sha512-a8N9czmdnrjPHa3DeFlwqst5eaL5W8jYu3EBbTTkI5FHkfMhFZh1EGbku6jhHhIzTA6tquI2P42NtZ59M/H/kQ==", - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-normalize-display-values": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-6.0.2.tgz", - "integrity": "sha512-8H04Mxsb82ON/aAkPeq8kcBbAtI5Q2a64X/mnRRfPXBq7XeogoQvReqxEfc0B4WPq1KimjezNC8flUtC3Qz6jg==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-normalize-positions": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-6.0.2.tgz", - "integrity": "sha512-/JFzI441OAB9O7VnLA+RtSNZvQ0NCFZDOtp6QPFo1iIyawyXg0YI3CYM9HBy1WvwCRHnPep/BvI1+dGPKoXx/Q==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-normalize-repeat-style": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-6.0.2.tgz", - "integrity": "sha512-YdCgsfHkJ2jEXwR4RR3Tm/iOxSfdRt7jplS6XRh9Js9PyCR/aka/FCb6TuHT2U8gQubbm/mPmF6L7FY9d79VwQ==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-normalize-string": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-6.0.2.tgz", - "integrity": "sha512-vQZIivlxlfqqMp4L9PZsFE4YUkWniziKjQWUtsxUiVsSSPelQydwS8Wwcuw0+83ZjPWNTl02oxlIvXsmmG+CiQ==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-normalize-timing-functions": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-6.0.2.tgz", - "integrity": "sha512-a+YrtMox4TBtId/AEwbA03VcJgtyW4dGBizPl7e88cTFULYsprgHWTbfyjSLyHeBcK/Q9JhXkt2ZXiwaVHoMzA==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-normalize-unicode": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-6.1.0.tgz", - "integrity": "sha512-QVC5TQHsVj33otj8/JD869Ndr5Xcc/+fwRh4HAsFsAeygQQXm+0PySrKbr/8tkDKzW+EVT3QkqZMfFrGiossDg==", - "dependencies": { - "browserslist": "^4.23.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-normalize-url": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-6.0.2.tgz", - "integrity": "sha512-kVNcWhCeKAzZ8B4pv/DnrU1wNh458zBNp8dh4y5hhxih5RZQ12QWMuQrDgPRw3LRl8mN9vOVfHl7uhvHYMoXsQ==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-normalize-whitespace": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-6.0.2.tgz", - "integrity": "sha512-sXZ2Nj1icbJOKmdjXVT9pnyHQKiSAyuNQHSgRCUgThn2388Y9cGVDR+E9J9iAYbSbLHI+UUwLVl1Wzco/zgv0Q==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-ordered-values": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-6.0.2.tgz", - "integrity": "sha512-VRZSOB+JU32RsEAQrO94QPkClGPKJEL/Z9PCBImXMhIeK5KAYo6slP/hBYlLgrCjFxyqvn5VC81tycFEDBLG1Q==", - "dependencies": { - "cssnano-utils": "^4.0.2", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-reduce-idents": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-6.0.3.tgz", - "integrity": "sha512-G3yCqZDpsNPoQgbDUy3T0E6hqOQ5xigUtBQyrmq3tn2GxlyiL0yyl7H+T8ulQR6kOcHJ9t7/9H4/R2tv8tJbMA==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-reduce-initial": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-6.1.0.tgz", - "integrity": "sha512-RarLgBK/CrL1qZags04oKbVbrrVK2wcxhvta3GCxrZO4zveibqbRPmm2VI8sSgCXwoUHEliRSbOfpR0b/VIoiw==", - "dependencies": { - "browserslist": "^4.23.0", - "caniuse-api": "^3.0.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-reduce-transforms": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-6.0.2.tgz", - "integrity": "sha512-sB+Ya++3Xj1WaT9+5LOOdirAxP7dJZms3GRcYheSPi1PiTMigsxHAdkrbItHxwYHr4kt1zL7mmcHstgMYT+aiA==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-selector-parser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.0.tgz", - "integrity": "sha512-UMz42UD0UY0EApS0ZL9o1XnLhSTtvvvLe5Dc2H2O56fvRZi+KulDyf5ctDhhtYJBGKStV2FL1fy6253cmLgqVQ==", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-sort-media-queries": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/postcss-sort-media-queries/-/postcss-sort-media-queries-5.2.0.tgz", - "integrity": "sha512-AZ5fDMLD8SldlAYlvi8NIqo0+Z8xnXU2ia0jxmuhxAU+Lqt9K+AlmLNJ/zWEnE9x+Zx3qL3+1K20ATgNOr3fAA==", - "dependencies": { - "sort-css-media-queries": "2.2.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "postcss": "^8.4.23" - } - }, - "node_modules/postcss-svgo": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-6.0.3.tgz", - "integrity": "sha512-dlrahRmxP22bX6iKEjOM+c8/1p+81asjKT+V5lrgOH944ryx/OHpclnIbGsKVd3uWOXFLYJwCVf0eEkJGvO96g==", - "dependencies": { - "postcss-value-parser": "^4.2.0", - "svgo": "^3.2.0" - }, - "engines": { - "node": "^14 || ^16 || >= 18" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-unique-selectors": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-6.0.4.tgz", - "integrity": "sha512-K38OCaIrO8+PzpArzkLKB42dSARtC2tmG6PvD4b1o1Q2E9Os8jzfWFfSy/rixsHwohtsDdFtAWGjFVFUdwYaMg==", - "dependencies": { - "postcss-selector-parser": "^6.0.16" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" - }, - "node_modules/postcss-zindex": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-6.0.2.tgz", - "integrity": "sha512-5BxW9l1evPB/4ZIc+2GobEBoKC+h8gPGCMi+jxsYvd2x0mjq7wazk6DrP71pStqxE9Foxh5TVnonbWpFZzXaYg==", - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", - "dev": true, - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/pretty-error": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", - "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", - "dependencies": { - "lodash": "^4.17.20", - "renderkid": "^3.0.0" - } - }, - "node_modules/pretty-time": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pretty-time/-/pretty-time-1.1.0.tgz", - "integrity": "sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/prism-react-renderer": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-2.3.0.tgz", - "integrity": "sha512-UYRg2TkVIaI6tRVHC5OJ4/BxqPUxJkJvq/odLT/ykpt1zGYXooNperUxQcCvi87LyRnR4nCh81ceOA+e7nrydg==", - "dependencies": { - "@types/prismjs": "^1.26.0", - "clsx": "^2.0.0" - }, - "peerDependencies": { - "react": ">=16.0.0" - } - }, - "node_modules/prismjs": { - "version": "1.29.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", - "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==", - "engines": { - "node": ">=6" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "node_modules/prop-types/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, - "node_modules/property-information": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.4.1.tgz", - "integrity": "sha512-OHYtXfu5aI2sS2LWFSN5rgJjrQ4pCy8i1jubJLe2QvMF8JJ++HXTUIVWFLfXJoaOfvYYjk2SN8J2wFUWIGXT4w==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/proto-list": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", - "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==" - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/proxy-addr/node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==" - }, - "node_modules/pupa": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/pupa/-/pupa-3.1.0.tgz", - "integrity": "sha512-FLpr4flz5xZTSJxSeaheeMKN/EDzMdK7b8PTOC6a5PYFKTucWbdqjgqaEyH0shFiSJrVB1+Qqi4Tk19ccU6Aug==", - "dependencies": { - "escape-goat": "^4.0.0" - }, - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/queue": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", - "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", - "dependencies": { - "inherits": "~2.0.3" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/range-parser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", - "integrity": "sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/raw-body/node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/raw-loader": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-4.0.2.tgz", - "integrity": "sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA==", - "dependencies": { - "loader-utils": "^2.0.0", - "schema-utils": "^3.0.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" - } - }, - "node_modules/raw-loader/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/raw-loader/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/raw-loader/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "node_modules/raw-loader/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/rc/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", - "dependencies": { - "loose-envify": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dev-utils": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", - "integrity": "sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==", - "dependencies": { - "@babel/code-frame": "^7.16.0", - "address": "^1.1.2", - "browserslist": "^4.18.1", - "chalk": "^4.1.2", - "cross-spawn": "^7.0.3", - "detect-port-alt": "^1.1.6", - "escape-string-regexp": "^4.0.0", - "filesize": "^8.0.6", - "find-up": "^5.0.0", - "fork-ts-checker-webpack-plugin": "^6.5.0", - "global-modules": "^2.0.0", - "globby": "^11.0.4", - "gzip-size": "^6.0.0", - "immer": "^9.0.7", - "is-root": "^2.1.0", - "loader-utils": "^3.2.0", - "open": "^8.4.0", - "pkg-up": "^3.1.0", - "prompts": "^2.4.2", - "react-error-overlay": "^6.0.11", - "recursive-readdir": "^2.2.2", - "shell-quote": "^1.7.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/react-dev-utils/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-dev-utils/node_modules/loader-utils": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz", - "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==", - "engines": { - "node": ">= 12.13.0" - } - }, - "node_modules/react-dev-utils/node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-dev-utils/node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-dev-utils/node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-dev-utils/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/react-dev-utils/node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-devtools-inline": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/react-devtools-inline/-/react-devtools-inline-4.4.0.tgz", - "integrity": "sha512-ES0GolSrKO8wsKbsEkVeiR/ZAaHQTY4zDh1UW8DImVmm8oaGLl3ijJDvSGe+qDRKPZdPRnDtWWnSvvrgxXdThQ==", - "dependencies": { - "es6-symbol": "^3" - } - }, - "node_modules/react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", - "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" - }, - "peerDependencies": { - "react": "^18.2.0" - } - }, - "node_modules/react-error-overlay": { - "version": "6.0.11", - "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", - "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" - }, - "node_modules/react-fast-compare": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", - "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==" - }, - "node_modules/react-helmet-async": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-1.3.0.tgz", - "integrity": "sha512-9jZ57/dAn9t3q6hneQS0wukqC2ENOBgMNVEhb/ZG9ZSxUetzVIw4iAmEU38IaVg3QGYauQPhSeUTuIUtFglWpg==", - "dependencies": { - "@babel/runtime": "^7.12.5", - "invariant": "^2.2.4", - "prop-types": "^15.7.2", - "react-fast-compare": "^3.2.0", - "shallowequal": "^1.1.0" - }, - "peerDependencies": { - "react": "^16.6.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.6.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" - }, - "node_modules/react-json-view-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/react-json-view-lite/-/react-json-view-lite-1.4.0.tgz", - "integrity": "sha512-wh6F6uJyYAmQ4fK0e8dSQMEWuvTs2Wr3el3sLD9bambX1+pSWUVXIz1RFaoy3TI1mZ0FqdpKq9YgbgTTgyrmXA==", - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "react": "^16.13.1 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/react-loadable": { - "name": "@docusaurus/react-loadable", - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-6.0.0.tgz", - "integrity": "sha512-YMMxTUQV/QFSnbgrP3tjDzLHRg7vsbMn8e9HAa8o/1iXoiomo48b7sk/kkmWEuWNDPJVlKSJRB6Y2fHqdJk+SQ==", - "dependencies": { - "@types/react": "*" - }, - "peerDependencies": { - "react": "*" - } - }, - "node_modules/react-loadable-ssr-addon-v5-slorber": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/react-loadable-ssr-addon-v5-slorber/-/react-loadable-ssr-addon-v5-slorber-1.0.1.tgz", - "integrity": "sha512-lq3Lyw1lGku8zUEJPDxsNm1AfYHBrO9Y1+olAYwpUJ2IGFBskM0DMKok97A6LWUpHm+o7IvQBOWu9MLenp9Z+A==", - "dependencies": { - "@babel/runtime": "^7.10.3" - }, - "engines": { - "node": ">=10.13.0" - }, - "peerDependencies": { - "react-loadable": "*", - "webpack": ">=4.41.1 || 5.x" - } - }, - "node_modules/react-router": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.3.4.tgz", - "integrity": "sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA==", - "dependencies": { - "@babel/runtime": "^7.12.13", - "history": "^4.9.0", - "hoist-non-react-statics": "^3.1.0", - "loose-envify": "^1.3.1", - "path-to-regexp": "^1.7.0", - "prop-types": "^15.6.2", - "react-is": "^16.6.0", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0" - }, - "peerDependencies": { - "react": ">=15" - } - }, - "node_modules/react-router-config": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/react-router-config/-/react-router-config-5.1.1.tgz", - "integrity": "sha512-DuanZjaD8mQp1ppHjgnnUnyOlqYXZVjnov/JzFhjLEwd3Z4dYjMSnqrEzzGThH47vpCOqPPwJM2FtthLeJ8Pbg==", - "dependencies": { - "@babel/runtime": "^7.1.2" - }, - "peerDependencies": { - "react": ">=15", - "react-router": ">=5" - } - }, - "node_modules/react-router-dom": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.3.4.tgz", - "integrity": "sha512-m4EqFMHv/Ih4kpcBCONHbkT68KoAeHN4p3lAGoNryfHi0dMy0kCzEZakiKRsvg5wHZ/JLrLW8o8KomWiz/qbYQ==", - "dependencies": { - "@babel/runtime": "^7.12.13", - "history": "^4.9.0", - "loose-envify": "^1.3.1", - "prop-types": "^15.6.2", - "react-router": "5.3.4", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0" - }, - "peerDependencies": { - "react": ">=15" - } - }, - "node_modules/react-router/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/reading-time": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/reading-time/-/reading-time-1.5.0.tgz", - "integrity": "sha512-onYyVhBNr4CmAxFsKS7bz+uTLRakypIe4R+5A824vBSkQy/hB3fZepoVEf8OVAxzLvK+H/jm9TzpI3ETSm64Kg==" - }, - "node_modules/rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", - "dependencies": { - "resolve": "^1.1.6" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/recursive-readdir": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", - "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", - "dependencies": { - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/regenerate": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" - }, - "node_modules/regenerate-unicode-properties": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz", - "integrity": "sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==", - "dependencies": { - "regenerate": "^1.4.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", - "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" - }, - "node_modules/regenerator-transform": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", - "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", - "dependencies": { - "@babel/runtime": "^7.8.4" - } - }, - "node_modules/regexpu-core": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", - "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", - "dependencies": { - "@babel/regjsgen": "^0.8.0", - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.1.0", - "regjsparser": "^0.9.1", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/registry-auth-token": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.0.2.tgz", - "integrity": "sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ==", - "dependencies": { - "@pnpm/npm-conf": "^2.1.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/registry-url": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-6.0.1.tgz", - "integrity": "sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==", - "dependencies": { - "rc": "1.2.8" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/regjsparser": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", - "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", - "dependencies": { - "jsesc": "~0.5.0" - }, - "bin": { - "regjsparser": "bin/parser" - } - }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", - "bin": { - "jsesc": "bin/jsesc" - } - }, - "node_modules/rehype-raw": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz", - "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-raw": "^9.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/relateurl": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", - "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/remark-directive": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/remark-directive/-/remark-directive-3.0.0.tgz", - "integrity": "sha512-l1UyWJ6Eg1VPU7Hm/9tt0zKtReJQNOA4+iDMAxTyZNWnJnFlbS/7zhiel/rogTLQ2vMYwDzSJa4BiVNqGlqIMA==", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-directive": "^3.0.0", - "micromark-extension-directive": "^3.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-emoji": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/remark-emoji/-/remark-emoji-4.0.1.tgz", - "integrity": "sha512-fHdvsTR1dHkWKev9eNyhTo4EFwbUvJ8ka9SgeWkMPYFX4WoI7ViVBms3PjlQYgw5TLvNQso3GUB/b/8t3yo+dg==", - "dependencies": { - "@types/mdast": "^4.0.2", - "emoticon": "^4.0.1", - "mdast-util-find-and-replace": "^3.0.1", - "node-emoji": "^2.1.0", - "unified": "^11.0.4" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/remark-frontmatter": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/remark-frontmatter/-/remark-frontmatter-5.0.0.tgz", - "integrity": "sha512-XTFYvNASMe5iPN0719nPrdItC9aU0ssC4v14mH1BCi1u0n1gAocqcujWUrByftZTbLhRtiKRyjYTSIOcr69UVQ==", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-frontmatter": "^2.0.0", - "micromark-extension-frontmatter": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-gfm": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.0.tgz", - "integrity": "sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA==", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-gfm": "^3.0.0", - "micromark-extension-gfm": "^3.0.0", - "remark-parse": "^11.0.0", - "remark-stringify": "^11.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-mdx": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-3.0.1.tgz", - "integrity": "sha512-3Pz3yPQ5Rht2pM5R+0J2MrGoBSrzf+tJG94N+t/ilfdh8YLyyKYtidAYwTveB20BoHAcwIopOUqhcmh2F7hGYA==", - "dependencies": { - "mdast-util-mdx": "^3.0.0", - "micromark-extension-mdxjs": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-parse": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", - "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-rehype": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.0.tgz", - "integrity": "sha512-z3tJrAs2kIs1AqIIy6pzHmAHlF1hWQ+OdY4/hv+Wxe35EhyLKcajL33iUEn3ScxtFox9nUvRufR/Zre8Q08H/g==", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "mdast-util-to-hast": "^13.0.0", - "unified": "^11.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-stringify": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", - "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-to-markdown": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/renderkid": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", - "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", - "dependencies": { - "css-select": "^4.1.3", - "dom-converter": "^0.2.0", - "htmlparser2": "^6.1.0", - "lodash": "^4.17.21", - "strip-ansi": "^6.0.1" - } - }, - "node_modules/renderkid/node_modules/css-select": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", - "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.0.1", - "domhandler": "^4.3.1", - "domutils": "^2.8.0", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/renderkid/node_modules/dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/renderkid/node_modules/domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/renderkid/node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/renderkid/node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/renderkid/node_modules/htmlparser2": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", - "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.0.0", - "domutils": "^2.5.2", - "entities": "^2.0.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-like": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz", - "integrity": "sha512-oyrU88skkMtDdauHDuKVrgR+zuItqr6/c//FXzvmxRGMexSDc6hNvJInGW3LL46n+8b50RykrvwSUIIQH2LQ5A==", - "engines": { - "node": "*" - } - }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" - }, - "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-alpn": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", - "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==" - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve-pathname": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz", - "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==" - }, - "node_modules/responselike": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", - "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", - "dependencies": { - "lowercase-keys": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/retry": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", - "engines": { - "node": ">= 4" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/robust-predicates": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", - "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==" - }, - "node_modules/rtl-detect": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/rtl-detect/-/rtl-detect-1.1.2.tgz", - "integrity": "sha512-PGMBq03+TTG/p/cRB7HCLKJ1MgDIi07+QU1faSjiYRfmY5UsAttV9Hs08jDAHVwcOwmVLcSJkpwyfXszVjWfIQ==" - }, - "node_modules/rtlcss": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-4.1.1.tgz", - "integrity": "sha512-/oVHgBtnPNcggP2aVXQjSy6N1mMAfHg4GSag0QtZBlD5bdDgAHwr4pydqJGd+SUCu9260+Pjqbjwtvu7EMH1KQ==", - "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0", - "postcss": "^8.4.21", - "strip-json-comments": "^3.1.1" - }, - "bin": { - "rtlcss": "bin/rtlcss.js" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/rw": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", - "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" - }, - "node_modules/sade": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", - "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", - "dependencies": { - "mri": "^1.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "node_modules/sax": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", - "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==" - }, - "node_modules/scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", - "dependencies": { - "loose-envify": "^1.1.0" - } - }, - "node_modules/schema-utils": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", - "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/search-insights": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.14.0.tgz", - "integrity": "sha512-OLN6MsPMCghDOqlCtsIsYgtsC0pnwVTyT9Mu6A3ewOj1DxvzZF6COrn2g86E/c05xbktB0XN04m/t1Z+n+fTGw==", - "peer": true - }, - "node_modules/section-matter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", - "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", - "dependencies": { - "extend-shallow": "^2.0.1", - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/select-hose": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", - "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==" - }, - "node_modules/selfsigned": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", - "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", - "dependencies": { - "@types/node-forge": "^1.3.0", - "node-forge": "^1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-4.0.0.tgz", - "integrity": "sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==", - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/send/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/send/node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serialize-javascript": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", - "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/serve-handler": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.5.tgz", - "integrity": "sha512-ijPFle6Hwe8zfmBxJdE+5fta53fdIY0lHISJvuikXB3VYFafRjMRpOffSPvCYsbKyBA7pvy9oYr/BT1O3EArlg==", - "dependencies": { - "bytes": "3.0.0", - "content-disposition": "0.5.2", - "fast-url-parser": "1.1.3", - "mime-types": "2.1.18", - "minimatch": "3.1.2", - "path-is-inside": "1.0.2", - "path-to-regexp": "2.2.1", - "range-parser": "1.2.0" - } - }, - "node_modules/serve-handler/node_modules/path-to-regexp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.2.1.tgz", - "integrity": "sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ==" - }, - "node_modules/serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", - "dependencies": { - "accepts": "~1.3.4", - "batch": "0.6.1", - "debug": "2.6.9", - "escape-html": "~1.0.3", - "http-errors": "~1.6.2", - "mime-types": "~2.1.17", - "parseurl": "~1.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/serve-index/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/serve-index/node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serve-index/node_modules/http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serve-index/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" - }, - "node_modules/serve-index/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/serve-index/node_modules/setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" - }, - "node_modules/serve-index/node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", - "dependencies": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.18.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/set-function-length": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", - "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", - "dependencies": { - "define-data-property": "^1.1.1", - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, - "node_modules/shallow-clone": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", - "dependencies": { - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shallowequal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", - "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "engines": { - "node": ">=8" - } - }, - "node_modules/shell-quote": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", - "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/shelljs": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", - "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", - "dependencies": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - }, - "bin": { - "shjs": "bin/shjs" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" - }, - "node_modules/sirv": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.3.tgz", - "integrity": "sha512-O9jm9BsID1P+0HOi81VpXPoDxYP374pkOLzACAoyUQ/3OUVndNpsz6wMnY2z+yOxzbllCKZrM+9QrWsv4THnyA==", - "dependencies": { - "@polka/url": "^1.0.0-next.20", - "mrmime": "^1.0.0", - "totalist": "^3.0.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" - }, - "node_modules/sitemap": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-7.1.2.tgz", - "integrity": "sha512-ARCqzHJ0p4gWt+j7NlU5eDlIO9+Rkr/JhPFZKKQ1l5GCus7rJH4UdrlVAh0xC/gDS/Qir2UMxqYNHtsKr2rpCw==", - "dependencies": { - "@types/node": "^17.0.5", - "@types/sax": "^1.2.1", - "arg": "^5.0.0", - "sax": "^1.2.4" - }, - "bin": { - "sitemap": "dist/cli.js" - }, - "engines": { - "node": ">=12.0.0", - "npm": ">=5.6.0" - } - }, - "node_modules/sitemap/node_modules/@types/node": { - "version": "17.0.45", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", - "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==" - }, - "node_modules/skin-tone": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/skin-tone/-/skin-tone-2.0.0.tgz", - "integrity": "sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==", - "dependencies": { - "unicode-emoji-modifier-base": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/snake-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", - "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", - "dependencies": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/sockjs": { - "version": "0.3.24", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", - "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", - "dependencies": { - "faye-websocket": "^0.11.3", - "uuid": "^8.3.2", - "websocket-driver": "^0.7.4" - } - }, - "node_modules/sort-css-media-queries": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/sort-css-media-queries/-/sort-css-media-queries-2.2.0.tgz", - "integrity": "sha512-0xtkGhWCC9MGt/EzgnvbbbKhqWjl1+/rncmhTh5qCpbYguXh6S/qwePfv/JQ8jePXXmqingylxoC49pCkSPIbA==", - "engines": { - "node": ">= 6.3.0" - } - }, - "node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/source-map-support/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/space-separated-tokens": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", - "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/spdy": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", - "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", - "dependencies": { - "debug": "^4.1.0", - "handle-thing": "^2.0.0", - "http-deceiver": "^1.2.7", - "select-hose": "^2.0.0", - "spdy-transport": "^3.0.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/spdy-transport": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", - "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", - "dependencies": { - "debug": "^4.1.0", - "detect-node": "^2.0.4", - "hpack.js": "^2.1.6", - "obuf": "^1.1.2", - "readable-stream": "^3.0.6", - "wbuf": "^1.7.3" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" - }, - "node_modules/srcset": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/srcset/-/srcset-4.0.0.tgz", - "integrity": "sha512-wvLeHgcVHKO8Sc/H/5lkGreJQVeYMm9rlmt8PuR1xE31rIuXhuzznUUqAt8MqLhB3MqJdFzlNAfpcWnxiFUcPw==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/static-browser-server": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/static-browser-server/-/static-browser-server-1.0.3.tgz", - "integrity": "sha512-ZUyfgGDdFRbZGGJQ1YhiM930Yczz5VlbJObrQLlk24+qNHVQx4OlLcYswEUo3bIyNAbQUIUR9Yr5/Hqjzqb4zA==", - "dependencies": { - "@open-draft/deferred-promise": "^2.1.0", - "dotenv": "^16.0.3", - "mime-db": "^1.52.0", - "outvariant": "^1.3.0" - } - }, - "node_modules/static-browser-server/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/std-env": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.4.3.tgz", - "integrity": "sha512-f9aPhy8fYBuMN+sNfakZV18U39PbalgjXG3lLB9WkaYTxijru61wb57V9wxxNthXM5Sd88ETBWi29qLAsHO52Q==" - }, - "node_modules/strict-event-emitter": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.4.6.tgz", - "integrity": "sha512-12KWeb+wixJohmnwNFerbyiBrAlq5qJLwIt38etRtKtmmHyDSoGlIqFE9wx+4IwG0aDjI7GV8tc8ZccjWZZtTg==" - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/string-width/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/string-width/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/stringify-entities": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.3.tgz", - "integrity": "sha512-BP9nNHMhhfcMbiuQKCqMjhDP5yBCAxsPu4pHFFzJ6Alo9dZgY4VLDPutXqIjpRiMoKdp7Av85Gr73Q5uH9k7+g==", - "dependencies": { - "character-entities-html4": "^2.0.0", - "character-entities-legacy": "^3.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/stringify-object": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", - "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", - "dependencies": { - "get-own-enumerable-property-symbols": "^3.0.0", - "is-obj": "^1.0.1", - "is-regexp": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", - "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/style-mod": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.0.tgz", - "integrity": "sha512-Ca5ib8HrFn+f+0n4N4ScTIA9iTOQ7MaGS1ylHcoVqW9J7w2w8PzN6g9gKmTYgGEBH8e120+RCmhpje6jC5uGWA==" - }, - "node_modules/style-to-object": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.4.4.tgz", - "integrity": "sha512-HYNoHZa2GorYNyqiCaBgsxvcJIn7OHq6inEga+E6Ke3m5JkoqpQbnFssk4jwe+K7AhGa2fcha4wSOf1Kn01dMg==", - "dependencies": { - "inline-style-parser": "0.1.1" - } - }, - "node_modules/stylehacks": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-6.1.1.tgz", - "integrity": "sha512-gSTTEQ670cJNoaeIp9KX6lZmm8LJ3jPB5yJmX8Zq/wQxOsAFXV3qjWzHas3YYk1qesuVIyYWWUpZ0vSE/dTSGg==", - "dependencies": { - "browserslist": "^4.23.0", - "postcss-selector-parser": "^6.0.16" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/stylis": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.0.tgz", - "integrity": "sha512-E87pIogpwUsUwXw7dNyU4QDjdgVMy52m+XEOPEKUn161cCzWjjhPSQhByfd1CcNvrOLnXQ6OnnZDwnJrz/Z4YQ==" - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/svg-parser": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", - "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==" - }, - "node_modules/svgo": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz", - "integrity": "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==", - "dependencies": { - "@trysound/sax": "0.2.0", - "commander": "^7.2.0", - "css-select": "^5.1.0", - "css-tree": "^2.3.1", - "css-what": "^6.1.0", - "csso": "^5.0.5", - "picocolors": "^1.0.0" - }, - "bin": { - "svgo": "bin/svgo" - }, - "engines": { - "node": ">=14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/svgo" - } - }, - "node_modules/svgo/node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "engines": { - "node": ">= 10" - } - }, - "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/terser": { - "version": "5.24.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.24.0.tgz", - "integrity": "sha512-ZpGR4Hy3+wBEzVEnHvstMvqpD/nABNelQn/z2r0fjVWGQsN3bpOLzQlqDxmb4CDZnXq5lpjnQ+mHQLAOpfM5iw==", - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.8.2", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser-webpack-plugin": { - "version": "5.3.9", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz", - "integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.17", - "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.1", - "terser": "^5.16.8" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "uglify-js": { - "optional": true - } - } - }, - "node_modules/terser-webpack-plugin/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/terser-webpack-plugin/node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "node_modules/terser-webpack-plugin/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/terser-webpack-plugin/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/terser/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" - }, - "node_modules/thunky": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", - "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" - }, - "node_modules/tiny-invariant": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", - "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==" - }, - "node_modules/tiny-warning": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", - "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "engines": { - "node": ">=4" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/totalist": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", - "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/trim-lines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", - "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/trough": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", - "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/ts-dedent": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", - "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==", - "engines": { - "node": ">=6.10" - } - }, - "node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" - }, - "node_modules/type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" - }, - "node_modules/type-fest": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", - "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/type-is/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/type-is/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dependencies": { - "is-typedarray": "^1.0.0" - } - }, - "node_modules/typescript": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", - "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" - }, - "node_modules/unicode-canonical-property-names-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", - "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-emoji-modifier-base": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unicode-emoji-modifier-base/-/unicode-emoji-modifier-base-1.0.0.tgz", - "integrity": "sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", - "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", - "dependencies": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", - "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-property-aliases-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", - "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", - "engines": { - "node": ">=4" - } - }, - "node_modules/unified": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.4.tgz", - "integrity": "sha512-apMPnyLjAX+ty4OrNap7yumyVAMlKx5IWU2wlzzUdYJO9A8f1p9m/gywF/GM2ZDFcjQPrx59Mc90KwmxsoklxQ==", - "dependencies": { - "@types/unist": "^3.0.0", - "bail": "^2.0.0", - "devlop": "^1.0.0", - "extend": "^3.0.0", - "is-plain-obj": "^4.0.0", - "trough": "^2.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unique-string": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz", - "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==", - "dependencies": { - "crypto-random-string": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/unist-util-is": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", - "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-position": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", - "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-position-from-estree": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unist-util-position-from-estree/-/unist-util-position-from-estree-2.0.0.tgz", - "integrity": "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-remove-position": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz", - "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-visit": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-stringify-position": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", - "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", - "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit-parents": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", - "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/update-notifier": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-6.0.2.tgz", - "integrity": "sha512-EDxhTEVPZZRLWYcJ4ZXjGFN0oP7qYvbXWzEgRm/Yql4dHX5wDbvh89YHP6PK1lzZJYrMtXUuZZz8XGK+U6U1og==", - "dependencies": { - "boxen": "^7.0.0", - "chalk": "^5.0.1", - "configstore": "^6.0.0", - "has-yarn": "^3.0.0", - "import-lazy": "^4.0.0", - "is-ci": "^3.0.1", - "is-installed-globally": "^0.4.0", - "is-npm": "^6.0.0", - "is-yarn-global": "^0.4.0", - "latest-version": "^7.0.0", - "pupa": "^3.1.0", - "semver": "^7.3.7", - "semver-diff": "^4.0.0", - "xdg-basedir": "^5.1.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/yeoman/update-notifier?sponsor=1" - } - }, - "node_modules/update-notifier/node_modules/boxen": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.1.1.tgz", - "integrity": "sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==", - "dependencies": { - "ansi-align": "^3.0.1", - "camelcase": "^7.0.1", - "chalk": "^5.2.0", - "cli-boxes": "^3.0.0", - "string-width": "^5.1.2", - "type-fest": "^2.13.0", - "widest-line": "^4.0.1", - "wrap-ansi": "^8.1.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/update-notifier/node_modules/camelcase": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz", - "integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==", - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/update-notifier/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/uri-js/node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/url-loader": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz", - "integrity": "sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==", - "dependencies": { - "loader-utils": "^2.0.0", - "mime-types": "^2.1.27", - "schema-utils": "^3.0.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "file-loader": "*", - "webpack": "^4.0.0 || ^5.0.0" - }, - "peerDependenciesMeta": { - "file-loader": { - "optional": true - } - } - }, - "node_modules/url-loader/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/url-loader/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/url-loader/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "node_modules/url-loader/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/url-loader/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/url-loader/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "node_modules/utila": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", - "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==" - }, - "node_modules/utility-types": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz", - "integrity": "sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==", - "engines": { - "node": ">= 4" - } - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/uvu": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.6.tgz", - "integrity": "sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==", - "dependencies": { - "dequal": "^2.0.0", - "diff": "^5.0.0", - "kleur": "^4.0.3", - "sade": "^1.7.3" - }, - "bin": { - "uvu": "bin.js" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/uvu/node_modules/kleur": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", - "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/value-equal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", - "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==" - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/vfile": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.1.tgz", - "integrity": "sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-stringify-position": "^4.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-location": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.2.tgz", - "integrity": "sha512-NXPYyxyBSH7zB5U6+3uDdd6Nybz6o6/od9rk8bp9H8GR3L+cm/fC0uUTbqBmUTnMCUDslAGBOIKNfvvb+gGlDg==", - "dependencies": { - "@types/unist": "^3.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-message": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", - "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/w3c-keyname": { - "version": "2.2.8", - "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", - "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==" - }, - "node_modules/watchpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/wbuf": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", - "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", - "dependencies": { - "minimalistic-assert": "^1.0.0" - } - }, - "node_modules/web-namespaces": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", - "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/web-worker": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.2.0.tgz", - "integrity": "sha512-PgF341avzqyx60neE9DD+XS26MMNMoUQRz9NOZwW32nPQrF6p77f1htcnjBSEV8BGMKZ16choqUG4hyI0Hx7mA==" - }, - "node_modules/webpack": { - "version": "5.89.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.89.0.tgz", - "integrity": "sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==", - "dependencies": { - "@types/eslint-scope": "^3.7.3", - "@types/estree": "^1.0.0", - "@webassemblyjs/ast": "^1.11.5", - "@webassemblyjs/wasm-edit": "^1.11.5", - "@webassemblyjs/wasm-parser": "^1.11.5", - "acorn": "^8.7.1", - "acorn-import-assertions": "^1.9.0", - "browserslist": "^4.14.5", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.15.0", - "es-module-lexer": "^1.2.1", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.2.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.7", - "watchpack": "^2.4.0", - "webpack-sources": "^3.2.3" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-bundle-analyzer": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.9.1.tgz", - "integrity": "sha512-jnd6EoYrf9yMxCyYDPj8eutJvtjQNp8PHmni/e/ulydHBWhT5J3menXt3HEkScsu9YqMAcG4CfFjs3rj5pVU1w==", - "dependencies": { - "@discoveryjs/json-ext": "0.5.7", - "acorn": "^8.0.4", - "acorn-walk": "^8.0.0", - "commander": "^7.2.0", - "escape-string-regexp": "^4.0.0", - "gzip-size": "^6.0.0", - "is-plain-object": "^5.0.0", - "lodash.debounce": "^4.0.8", - "lodash.escape": "^4.0.1", - "lodash.flatten": "^4.4.0", - "lodash.invokemap": "^4.6.0", - "lodash.pullall": "^4.2.0", - "lodash.uniqby": "^4.7.0", - "opener": "^1.5.2", - "picocolors": "^1.0.0", - "sirv": "^2.0.3", - "ws": "^7.3.1" - }, - "bin": { - "webpack-bundle-analyzer": "lib/bin/analyzer.js" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/webpack-bundle-analyzer/node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "engines": { - "node": ">= 10" - } - }, - "node_modules/webpack-dev-middleware": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", - "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==", - "dependencies": { - "colorette": "^2.0.10", - "memfs": "^3.4.3", - "mime-types": "^2.1.31", - "range-parser": "^1.2.1", - "schema-utils": "^4.0.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" - } - }, - "node_modules/webpack-dev-middleware/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/webpack-dev-middleware/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/webpack-dev-middleware/node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/webpack-dev-server": { - "version": "4.15.1", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.1.tgz", - "integrity": "sha512-5hbAst3h3C3L8w6W4P96L5vaV0PxSmJhxZvWKYIdgxOQm8pNZ5dEOmmSLBVpP85ReeyRt6AS1QJNyo/oFFPeVA==", - "dependencies": { - "@types/bonjour": "^3.5.9", - "@types/connect-history-api-fallback": "^1.3.5", - "@types/express": "^4.17.13", - "@types/serve-index": "^1.9.1", - "@types/serve-static": "^1.13.10", - "@types/sockjs": "^0.3.33", - "@types/ws": "^8.5.5", - "ansi-html-community": "^0.0.8", - "bonjour-service": "^1.0.11", - "chokidar": "^3.5.3", - "colorette": "^2.0.10", - "compression": "^1.7.4", - "connect-history-api-fallback": "^2.0.0", - "default-gateway": "^6.0.3", - "express": "^4.17.3", - "graceful-fs": "^4.2.6", - "html-entities": "^2.3.2", - "http-proxy-middleware": "^2.0.3", - "ipaddr.js": "^2.0.1", - "launch-editor": "^2.6.0", - "open": "^8.0.9", - "p-retry": "^4.5.0", - "rimraf": "^3.0.2", - "schema-utils": "^4.0.0", - "selfsigned": "^2.1.1", - "serve-index": "^1.9.1", - "sockjs": "^0.3.24", - "spdy": "^4.0.2", - "webpack-dev-middleware": "^5.3.1", - "ws": "^8.13.0" - }, - "bin": { - "webpack-dev-server": "bin/webpack-dev-server.js" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.37.0 || ^5.0.0" - }, - "peerDependenciesMeta": { - "webpack": { - "optional": true - }, - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-dev-server/node_modules/ws": { - "version": "8.14.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz", - "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/webpack-merge": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", - "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", - "dependencies": { - "clone-deep": "^4.0.1", - "flat": "^5.0.2", - "wildcard": "^2.0.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/webpack/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/webpack/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/webpack/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "node_modules/webpack/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/webpack/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/webpack/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/webpackbar": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/webpackbar/-/webpackbar-5.0.2.tgz", - "integrity": "sha512-BmFJo7veBDgQzfWXl/wwYXr/VFus0614qZ8i9znqcl9fnEdiVkdbi0TedLQ6xAK92HZHDJ0QmyQ0fmuZPAgCYQ==", - "dependencies": { - "chalk": "^4.1.0", - "consola": "^2.15.3", - "pretty-time": "^1.1.0", - "std-env": "^3.0.1" - }, - "engines": { - "node": ">=12" - }, - "peerDependencies": { - "webpack": "3 || 4 || 5" - } - }, - "node_modules/websocket-driver": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", - "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", - "dependencies": { - "http-parser-js": ">=0.5.1", - "safe-buffer": ">=5.1.0", - "websocket-extensions": ">=0.1.1" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/websocket-extensions": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", - "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/widest-line": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", - "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==", - "dependencies": { - "string-width": "^5.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/wildcard": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", - "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==" - }, - "node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, - "node_modules/write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dependencies": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "node_modules/ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/xdg-basedir": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-5.1.0.tgz", - "integrity": "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/xml-js": { - "version": "1.6.11", - "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz", - "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==", - "dependencies": { - "sax": "^1.2.4" - }, - "bin": { - "xml-js": "bin/cli.js" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - }, - "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/yocto-queue": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", - "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/zwitch": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", - "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - } - }, - "dependencies": { - "@algolia/autocomplete-core": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.9.3.tgz", - "integrity": "sha512-009HdfugtGCdC4JdXUbVJClA0q0zh24yyePn+KUGk3rP7j8FEe/m5Yo/z65gn6nP/cM39PxpzqKrL7A6fP6PPw==", - "requires": { - "@algolia/autocomplete-plugin-algolia-insights": "1.9.3", - "@algolia/autocomplete-shared": "1.9.3" - } - }, - "@algolia/autocomplete-plugin-algolia-insights": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.9.3.tgz", - "integrity": "sha512-a/yTUkcO/Vyy+JffmAnTWbr4/90cLzw+CC3bRbhnULr/EM0fGNvM13oQQ14f2moLMcVDyAx/leczLlAOovhSZg==", - "requires": { - "@algolia/autocomplete-shared": "1.9.3" - } - }, - "@algolia/autocomplete-preset-algolia": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.9.3.tgz", - "integrity": "sha512-d4qlt6YmrLMYy95n5TB52wtNDr6EgAIPH81dvvvW8UmuWRgxEtY0NJiPwl/h95JtG2vmRM804M0DSwMCNZlzRA==", - "requires": { - "@algolia/autocomplete-shared": "1.9.3" - } - }, - "@algolia/autocomplete-shared": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.9.3.tgz", - "integrity": "sha512-Wnm9E4Ye6Rl6sTTqjoymD+l8DjSTHsHboVRYrKgEt8Q7UHm9nYbqhN/i0fhUYA3OAEH7WA8x3jfpnmJm3rKvaQ==", - "requires": {} - }, - "@algolia/cache-browser-local-storage": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.23.3.tgz", - "integrity": "sha512-vRHXYCpPlTDE7i6UOy2xE03zHF2C8MEFjPN2v7fRbqVpcOvAUQK81x3Kc21xyb5aSIpYCjWCZbYZuz8Glyzyyg==", - "requires": { - "@algolia/cache-common": "4.23.3" - } - }, - "@algolia/cache-common": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.23.3.tgz", - "integrity": "sha512-h9XcNI6lxYStaw32pHpB1TMm0RuxphF+Ik4o7tcQiodEdpKK+wKufY6QXtba7t3k8eseirEMVB83uFFF3Nu54A==" - }, - "@algolia/cache-in-memory": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.23.3.tgz", - "integrity": "sha512-yvpbuUXg/+0rbcagxNT7un0eo3czx2Uf0y4eiR4z4SD7SiptwYTpbuS0IHxcLHG3lq22ukx1T6Kjtk/rT+mqNg==", - "requires": { - "@algolia/cache-common": "4.23.3" - } - }, - "@algolia/client-account": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.23.3.tgz", - "integrity": "sha512-hpa6S5d7iQmretHHF40QGq6hz0anWEHGlULcTIT9tbUssWUriN9AUXIFQ8Ei4w9azD0hc1rUok9/DeQQobhQMA==", - "requires": { - "@algolia/client-common": "4.23.3", - "@algolia/client-search": "4.23.3", - "@algolia/transporter": "4.23.3" - } - }, - "@algolia/client-analytics": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.23.3.tgz", - "integrity": "sha512-LBsEARGS9cj8VkTAVEZphjxTjMVCci+zIIiRhpFun9jGDUlS1XmhCW7CTrnaWeIuCQS/2iPyRqSy1nXPjcBLRA==", - "requires": { - "@algolia/client-common": "4.23.3", - "@algolia/client-search": "4.23.3", - "@algolia/requester-common": "4.23.3", - "@algolia/transporter": "4.23.3" - } - }, - "@algolia/client-common": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.23.3.tgz", - "integrity": "sha512-l6EiPxdAlg8CYhroqS5ybfIczsGUIAC47slLPOMDeKSVXYG1n0qGiz4RjAHLw2aD0xzh2EXZ7aRguPfz7UKDKw==", - "requires": { - "@algolia/requester-common": "4.23.3", - "@algolia/transporter": "4.23.3" - } - }, - "@algolia/client-personalization": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.23.3.tgz", - "integrity": "sha512-3E3yF3Ocr1tB/xOZiuC3doHQBQ2zu2MPTYZ0d4lpfWads2WTKG7ZzmGnsHmm63RflvDeLK/UVx7j2b3QuwKQ2g==", - "requires": { - "@algolia/client-common": "4.23.3", - "@algolia/requester-common": "4.23.3", - "@algolia/transporter": "4.23.3" - } - }, - "@algolia/client-search": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.23.3.tgz", - "integrity": "sha512-P4VAKFHqU0wx9O+q29Q8YVuaowaZ5EM77rxfmGnkHUJggh28useXQdopokgwMeYw2XUht49WX5RcTQ40rZIabw==", - "requires": { - "@algolia/client-common": "4.23.3", - "@algolia/requester-common": "4.23.3", - "@algolia/transporter": "4.23.3" - } - }, - "@algolia/events": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@algolia/events/-/events-4.0.1.tgz", - "integrity": "sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ==" - }, - "@algolia/logger-common": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.23.3.tgz", - "integrity": "sha512-y9kBtmJwiZ9ZZ+1Ek66P0M68mHQzKRxkW5kAAXYN/rdzgDN0d2COsViEFufxJ0pb45K4FRcfC7+33YB4BLrZ+g==" - }, - "@algolia/logger-console": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.23.3.tgz", - "integrity": "sha512-8xoiseoWDKuCVnWP8jHthgaeobDLolh00KJAdMe9XPrWPuf1by732jSpgy2BlsLTaT9m32pHI8CRfrOqQzHv3A==", - "requires": { - "@algolia/logger-common": "4.23.3" - } - }, - "@algolia/recommend": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-4.23.3.tgz", - "integrity": "sha512-9fK4nXZF0bFkdcLBRDexsnGzVmu4TSYZqxdpgBW2tEyfuSSY54D4qSRkLmNkrrz4YFvdh2GM1gA8vSsnZPR73w==", - "requires": { - "@algolia/cache-browser-local-storage": "4.23.3", - "@algolia/cache-common": "4.23.3", - "@algolia/cache-in-memory": "4.23.3", - "@algolia/client-common": "4.23.3", - "@algolia/client-search": "4.23.3", - "@algolia/logger-common": "4.23.3", - "@algolia/logger-console": "4.23.3", - "@algolia/requester-browser-xhr": "4.23.3", - "@algolia/requester-common": "4.23.3", - "@algolia/requester-node-http": "4.23.3", - "@algolia/transporter": "4.23.3" - } - }, - "@algolia/requester-browser-xhr": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.23.3.tgz", - "integrity": "sha512-jDWGIQ96BhXbmONAQsasIpTYWslyjkiGu0Quydjlowe+ciqySpiDUrJHERIRfELE5+wFc7hc1Q5hqjGoV7yghw==", - "requires": { - "@algolia/requester-common": "4.23.3" - } - }, - "@algolia/requester-common": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.23.3.tgz", - "integrity": "sha512-xloIdr/bedtYEGcXCiF2muajyvRhwop4cMZo+K2qzNht0CMzlRkm8YsDdj5IaBhshqfgmBb3rTg4sL4/PpvLYw==" - }, - "@algolia/requester-node-http": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.23.3.tgz", - "integrity": "sha512-zgu++8Uj03IWDEJM3fuNl34s746JnZOWn1Uz5taV1dFyJhVM/kTNw9Ik7YJWiUNHJQXcaD8IXD1eCb0nq/aByA==", - "requires": { - "@algolia/requester-common": "4.23.3" - } - }, - "@algolia/transporter": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.23.3.tgz", - "integrity": "sha512-Wjl5gttqnf/gQKJA+dafnD0Y6Yw97yvfY8R9h0dQltX1GXTgNs1zWgvtWW0tHl1EgMdhAyw189uWiZMnL3QebQ==", - "requires": { - "@algolia/cache-common": "4.23.3", - "@algolia/logger-common": "4.23.3", - "@algolia/requester-common": "4.23.3" - } - }, - "@ampproject/remapping": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", - "requires": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@babel/code-frame": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", - "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", - "requires": { - "@babel/highlight": "^7.23.4", - "chalk": "^2.4.2" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@babel/compat-data": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.2.tgz", - "integrity": "sha512-0S9TQMmDHlqAZ2ITT95irXKfxN9bncq8ZCoJhun3nHL/lLUxd2NKBJYoNGWH7S0hz6fRQwWlAWn/ILM0C70KZQ==" - }, - "@babel/core": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.5.tgz", - "integrity": "sha512-Cwc2XjUrG4ilcfOw4wBAK+enbdgwAcAJCfGUItPBKR7Mjw4aEfAFYrLxeRp4jWgtNIKn3n2AlBOfwwafl+42/g==", - "requires": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.5", - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.23.5", - "@babel/parser": "^7.23.5", - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.5", - "@babel/types": "^7.23.5", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" - } - } - }, - "@babel/generator": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.5.tgz", - "integrity": "sha512-BPssCHrBD+0YrxviOa3QzpqwhNIXKEtOa2jQrm4FlmkC2apYgRnQcmPWiGZDlGxiNtltnUFolMe8497Esry+jA==", - "requires": { - "@babel/types": "^7.23.5", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" - } - }, - "@babel/helper-annotate-as-pure": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", - "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz", - "integrity": "sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==", - "requires": { - "@babel/types": "^7.22.15" - } - }, - "@babel/helper-compilation-targets": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", - "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", - "requires": { - "@babel/compat-data": "^7.22.9", - "@babel/helper-validator-option": "^7.22.15", - "browserslist": "^4.21.9", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" - } - } - }, - "@babel/helper-create-class-features-plugin": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.15.tgz", - "integrity": "sha512-jKkwA59IXcvSaiK2UN45kKwSC9o+KuoXsBDvHvU/7BecYIp8GQ2UwrVvFgJASUT+hBnwJx6MhvMCuMzwZZ7jlg==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", - "@babel/helper-member-expression-to-functions": "^7.22.15", - "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" - } - } - }, - "@babel/helper-create-regexp-features-plugin": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz", - "integrity": "sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "regexpu-core": "^5.3.1", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" - } - } - }, - "@babel/helper-define-polyfill-provider": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.3.tgz", - "integrity": "sha512-WBrLmuPP47n7PNwsZ57pqam6G/RGo1vw/87b0Blc53tZNGZ4x7YvZ6HgQe2vo1W/FR20OgjeZuGXzudPiXHFug==", - "requires": { - "@babel/helper-compilation-targets": "^7.22.6", - "@babel/helper-plugin-utils": "^7.22.5", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2" - } - }, - "@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==" - }, - "@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", - "requires": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz", - "integrity": "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==", - "requires": { - "@babel/types": "^7.23.0" - } - }, - "@babel/helper-module-imports": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", - "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", - "requires": { - "@babel/types": "^7.22.15" - } - }, - "@babel/helper-module-transforms": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", - "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", - "requires": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.20" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", - "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.7.tgz", - "integrity": "sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==" - }, - "@babel/helper-remap-async-to-generator": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz", - "integrity": "sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-wrap-function": "^7.22.20" - } - }, - "@babel/helper-replace-supers": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz", - "integrity": "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==", - "requires": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-member-expression-to-functions": "^7.22.15", - "@babel/helper-optimise-call-expression": "^7.22.5" - } - }, - "@babel/helper-simple-access": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", - "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-string-parser": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", - "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==" - }, - "@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==" - }, - "@babel/helper-validator-option": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", - "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==" - }, - "@babel/helper-wrap-function": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz", - "integrity": "sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==", - "requires": { - "@babel/helper-function-name": "^7.22.5", - "@babel/template": "^7.22.15", - "@babel/types": "^7.22.19" - } - }, - "@babel/helpers": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.5.tgz", - "integrity": "sha512-oO7us8FzTEsG3U6ag9MfdF1iA/7Z6dz+MtFhifZk8C8o453rGJFFWUP1t+ULM9TUIAzC9uxXEiXjOiVMyd7QPg==", - "requires": { - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.5", - "@babel/types": "^7.23.5" - } - }, - "@babel/highlight": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", - "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", - "requires": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@babel/parser": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.5.tgz", - "integrity": "sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ==" - }, - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.15.tgz", - "integrity": "sha512-FB9iYlz7rURmRJyXRKEnalYPPdn87H5no108cyuQQyMwlpJ2SJtpIUBI27kdTin956pz+LPypkPVPUTlxOmrsg==", - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.15.tgz", - "integrity": "sha512-Hyph9LseGvAeeXzikV88bczhsrLrIZqDPxO+sSmAunMPaGrBGhfMWzCPYTtiW9t+HzSE2wtV8e5cc5P6r1xMDQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/plugin-transform-optional-chaining": "^7.22.15" - } - }, - "@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.0-placeholder-for-preset-env.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", - "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", - "requires": {} - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-syntax-import-assertions": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.22.5.tgz", - "integrity": "sha512-rdV97N7KqsRzeNGoWUOK6yUsWarLjE5Su/Snk9IYPU9CwkWHs4t+rTGOvffTR8XGkJMTAdLfO0xVnXm8wugIJg==", - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-syntax-import-attributes": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.22.5.tgz", - "integrity": "sha512-KwvoWDeNKPETmozyFE0P2rOLqh39EoQHNjqizrI5B8Vt0ZNS7M56s7dAiAqbYfiAYOuIzIh96z3iR2ktgu3tEg==", - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-jsx": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", - "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-typescript": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", - "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-syntax-unicode-sets-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", - "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-transform-arrow-functions": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.22.5.tgz", - "integrity": "sha512-26lTNXoVRdAnsaDXPpvCNUq+OVWEVC6bx7Vvz9rC53F2bagUWW4u4ii2+h8Fejfh7RYqPxn+libeFBBck9muEw==", - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-transform-async-generator-functions": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.2.tgz", - "integrity": "sha512-BBYVGxbDVHfoeXbOwcagAkOQAm9NxoTdMGfTqghu1GrvadSaw6iW3Je6IcL5PNOw8VwjxqBECXy50/iCQSY/lQ==", - "requires": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-remap-async-to-generator": "^7.22.20", - "@babel/plugin-syntax-async-generators": "^7.8.4" - } - }, - "@babel/plugin-transform-async-to-generator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.22.5.tgz", - "integrity": "sha512-b1A8D8ZzE/VhNDoV1MSJTnpKkCG5bJo+19R4o4oy03zM7ws8yEMK755j61Dc3EyvdysbqH5BOOTquJ7ZX9C6vQ==", - "requires": { - "@babel/helper-module-imports": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-remap-async-to-generator": "^7.22.5" - } - }, - "@babel/plugin-transform-block-scoped-functions": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.22.5.tgz", - "integrity": "sha512-tdXZ2UdknEKQWKJP1KMNmuF5Lx3MymtMN/pvA+p/VEkhK8jVcQ1fzSy8KM9qRYhAf2/lV33hoMPKI/xaI9sADA==", - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-transform-block-scoping": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.0.tgz", - "integrity": "sha512-cOsrbmIOXmf+5YbL99/S49Y3j46k/T16b9ml8bm9lP6N9US5iQ2yBK7gpui1pg0V/WMcXdkfKbTb7HXq9u+v4g==", - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-transform-class-properties": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.22.5.tgz", - "integrity": "sha512-nDkQ0NfkOhPTq8YCLiWNxp1+f9fCobEjCb0n8WdbNUBc4IB5V7P1QnX9IjpSoquKrXF5SKojHleVNs2vGeHCHQ==", - "requires": { - "@babel/helper-create-class-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-transform-class-static-block": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.11.tgz", - "integrity": "sha512-GMM8gGmqI7guS/llMFk1bJDkKfn3v3C4KHK9Yg1ey5qcHcOlKb0QvcMrgzvxo+T03/4szNh5lghY+fEC98Kq9g==", - "requires": { - "@babel/helper-create-class-features-plugin": "^7.22.11", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-class-static-block": "^7.14.5" - } - }, - "@babel/plugin-transform-classes": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.15.tgz", - "integrity": "sha512-VbbC3PGjBdE0wAWDdHM9G8Gm977pnYI0XpqMd6LrKISj8/DJXEsWqgRuTYaNE9Bv0JGhTZUzHDlMk18IpOuoqw==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", - "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.9", - "@babel/helper-split-export-declaration": "^7.22.6", - "globals": "^11.1.0" - } - }, - "@babel/plugin-transform-computed-properties": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.22.5.tgz", - "integrity": "sha512-4GHWBgRf0krxPX+AaPtgBAlTgTeZmqDynokHOX7aqqAB4tHs3U2Y02zH6ETFdLZGcg9UQSD1WCmkVrE9ErHeOg==", - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/template": "^7.22.5" - } - }, - "@babel/plugin-transform-destructuring": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.0.tgz", - "integrity": "sha512-vaMdgNXFkYrB+8lbgniSYWHsgqK5gjaMNcc84bMIOMRLH0L9AqYq3hwMdvnyqj1OPqea8UtjPEuS/DCenah1wg==", - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-transform-dotall-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.22.5.tgz", - "integrity": "sha512-5/Yk9QxCQCl+sOIB1WelKnVRxTJDSAIxtJLL2/pqL14ZVlbH0fUQUZa/T5/UnQtBNgghR7mfB8ERBKyKPCi7Vw==", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-transform-duplicate-keys": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.22.5.tgz", - "integrity": "sha512-dEnYD+9BBgld5VBXHnF/DbYGp3fqGMsyxKbtD1mDyIA7AkTSpKXFhCVuj/oQVOoALfBs77DudA0BE4d5mcpmqw==", - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-transform-dynamic-import": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.11.tgz", - "integrity": "sha512-g/21plo58sfteWjaO0ZNVb+uEOkJNjAaHhbejrnBmu011l/eNDScmkbjCC3l4FKb10ViaGU4aOkFznSu2zRHgA==", - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" - } - }, - "@babel/plugin-transform-exponentiation-operator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.22.5.tgz", - "integrity": "sha512-vIpJFNM/FjZ4rh1myqIya9jXwrwwgFRHPjT3DkUA9ZLHuzox8jiXkOLvwm1H+PQIP3CqfC++WPKeuDi0Sjdj1g==", - "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-transform-export-namespace-from": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.11.tgz", - "integrity": "sha512-xa7aad7q7OiT8oNZ1mU7NrISjlSkVdMbNxn9IuLZyL9AJEhs1Apba3I+u5riX1dIkdptP5EKDG5XDPByWxtehw==", - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" - } - }, - "@babel/plugin-transform-for-of": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.15.tgz", - "integrity": "sha512-me6VGeHsx30+xh9fbDLLPi0J1HzmeIIyenoOQHuw2D4m2SAU3NrspX5XxJLBpqn5yrLzrlw2Iy3RA//Bx27iOA==", - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-transform-function-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.22.5.tgz", - "integrity": "sha512-UIzQNMS0p0HHiQm3oelztj+ECwFnj+ZRV4KnguvlsD2of1whUeM6o7wGNj6oLwcDoAXQ8gEqfgC24D+VdIcevg==", - "requires": { - "@babel/helper-compilation-targets": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-transform-json-strings": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.11.tgz", - "integrity": "sha512-CxT5tCqpA9/jXFlme9xIBCc5RPtdDq3JpkkhgHQqtDdiTnTI0jtZ0QzXhr5DILeYifDPp2wvY2ad+7+hLMW5Pw==", - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-json-strings": "^7.8.3" - } - }, - "@babel/plugin-transform-literals": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.22.5.tgz", - "integrity": "sha512-fTLj4D79M+mepcw3dgFBTIDYpbcB9Sm0bpm4ppXPaO+U+PKFFyV9MGRvS0gvGw62sd10kT5lRMKXAADb9pWy8g==", - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-transform-logical-assignment-operators": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.11.tgz", - "integrity": "sha512-qQwRTP4+6xFCDV5k7gZBF3C31K34ut0tbEcTKxlX/0KXxm9GLcO14p570aWxFvVzx6QAfPgq7gaeIHXJC8LswQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" - } - }, - "@babel/plugin-transform-member-expression-literals": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.22.5.tgz", - "integrity": "sha512-RZEdkNtzzYCFl9SE9ATaUMTj2hqMb4StarOJLrZRbqqU4HSBE7UlBw9WBWQiDzrJZJdUWiMTVDI6Gv/8DPvfew==", - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-transform-modules-amd": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.23.0.tgz", - "integrity": "sha512-xWT5gefv2HGSm4QHtgc1sYPbseOyf+FFDo2JbpE25GWl5BqTGO9IMwTYJRoIdjsF85GE+VegHxSCUt5EvoYTAw==", - "requires": { - "@babel/helper-module-transforms": "^7.23.0", - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-transform-modules-commonjs": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.0.tgz", - "integrity": "sha512-32Xzss14/UVc7k9g775yMIvkVK8xwKE0DPdP5JTapr3+Z9w4tzeOuLNY6BXDQR6BdnzIlXnCGAzsk/ICHBLVWQ==", - "requires": { - "@babel/helper-module-transforms": "^7.23.0", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-simple-access": "^7.22.5" - } - }, - "@babel/plugin-transform-modules-systemjs": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.0.tgz", - "integrity": "sha512-qBej6ctXZD2f+DhlOC9yO47yEYgUh5CZNz/aBoH4j/3NOlRfJXJbY7xDQCqQVf9KbrqGzIWER1f23doHGrIHFg==", - "requires": { - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-module-transforms": "^7.23.0", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.20" - } - }, - "@babel/plugin-transform-modules-umd": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.22.5.tgz", - "integrity": "sha512-+S6kzefN/E1vkSsKx8kmQuqeQsvCKCd1fraCM7zXm4SFoggI099Tr4G8U81+5gtMdUeMQ4ipdQffbKLX0/7dBQ==", - "requires": { - "@babel/helper-module-transforms": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz", - "integrity": "sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-transform-new-target": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.22.5.tgz", - "integrity": "sha512-AsF7K0Fx/cNKVyk3a+DW0JLo+Ua598/NxMRvxDnkpCIGFh43+h/v2xyhRUYf6oD8gE4QtL83C7zZVghMjHd+iw==", - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.11.tgz", - "integrity": "sha512-YZWOw4HxXrotb5xsjMJUDlLgcDXSfO9eCmdl1bgW4+/lAGdkjaEvOnQ4p5WKKdUgSzO39dgPl0pTnfxm0OAXcg==", - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" - } - }, - "@babel/plugin-transform-numeric-separator": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.11.tgz", - "integrity": "sha512-3dzU4QGPsILdJbASKhF/V2TVP+gJya1PsueQCxIPCEcerqF21oEcrob4mzjsp2Py/1nLfF5m+xYNMDpmA8vffg==", - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - } - }, - "@babel/plugin-transform-object-rest-spread": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.15.tgz", - "integrity": "sha512-fEB+I1+gAmfAyxZcX1+ZUwLeAuuf8VIg67CTznZE0MqVFumWkh8xWtn58I4dxdVf080wn7gzWoF8vndOViJe9Q==", - "requires": { - "@babel/compat-data": "^7.22.9", - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.22.15" - } - }, - "@babel/plugin-transform-object-super": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.22.5.tgz", - "integrity": "sha512-klXqyaT9trSjIUrcsYIfETAzmOEZL3cBYqOYLJxBHfMFFggmXOv+NYSX/Jbs9mzMVESw/WycLFPRx8ba/b2Ipw==", - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.5" - } - }, - "@babel/plugin-transform-optional-catch-binding": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.11.tgz", - "integrity": "sha512-rli0WxesXUeCJnMYhzAglEjLWVDF6ahb45HuprcmQuLidBJFWjNnOzssk2kuc6e33FlLaiZhG/kUIzUMWdBKaQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" - } - }, - "@babel/plugin-transform-optional-chaining": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.0.tgz", - "integrity": "sha512-sBBGXbLJjxTzLBF5rFWaikMnOGOk/BmK6vVByIdEggZ7Vn6CvWXZyRkkLFK6WE0IF8jSliyOkUN6SScFgzCM0g==", - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - } - }, - "@babel/plugin-transform-parameters": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.15.tgz", - "integrity": "sha512-hjk7qKIqhyzhhUvRT683TYQOFa/4cQKwQy7ALvTpODswN40MljzNDa0YldevS6tGbxwaEKVn502JmY0dP7qEtQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-transform-private-methods": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.22.5.tgz", - "integrity": "sha512-PPjh4gyrQnGe97JTalgRGMuU4icsZFnWkzicB/fUtzlKUqvsWBKEpPPfr5a2JiyirZkHxnAqkQMO5Z5B2kK3fA==", - "requires": { - "@babel/helper-create-class-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-transform-private-property-in-object": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.11.tgz", - "integrity": "sha512-sSCbqZDBKHetvjSwpyWzhuHkmW5RummxJBVbYLkGkaiTOWGxml7SXt0iWa03bzxFIx7wOj3g/ILRd0RcJKBeSQ==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.22.11", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" - } - }, - "@babel/plugin-transform-property-literals": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.22.5.tgz", - "integrity": "sha512-TiOArgddK3mK/x1Qwf5hay2pxI6wCZnvQqrFSqbtg1GLl2JcNMitVH/YnqjP+M31pLUeTfzY1HAXFDnUBV30rQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-transform-react-constant-elements": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.24.7.tgz", - "integrity": "sha512-7LidzZfUXyfZ8/buRW6qIIHBY8wAZ1OrY9c/wTr8YhZ6vMPo+Uc/CVFLYY1spZrEQlD4w5u8wjqk5NQ3OVqQKA==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-react-display-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.22.5.tgz", - "integrity": "sha512-PVk3WPYudRF5z4GKMEYUrLjPl38fJSKNaEOkFuoprioowGuWN6w2RKznuFNSlJx7pzzXXStPUnNSOEO0jL5EVw==", - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-transform-react-jsx": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.22.15.tgz", - "integrity": "sha512-oKckg2eZFa8771O/5vi7XeTvmM6+O9cxZu+kanTU7tD4sin5nO/G8jGJhq8Hvt2Z0kUoEDRayuZLaUlYl8QuGA==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-jsx": "^7.22.5", - "@babel/types": "^7.22.15" - } - }, - "@babel/plugin-transform-react-jsx-development": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.22.5.tgz", - "integrity": "sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A==", - "requires": { - "@babel/plugin-transform-react-jsx": "^7.22.5" - } - }, - "@babel/plugin-transform-react-pure-annotations": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.22.5.tgz", - "integrity": "sha512-gP4k85wx09q+brArVinTXhWiyzLl9UpmGva0+mWyKxk6JZequ05x3eUcIUE+FyttPKJFRRVtAvQaJ6YF9h1ZpA==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-transform-regenerator": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.10.tgz", - "integrity": "sha512-F28b1mDt8KcT5bUyJc/U9nwzw6cV+UmTeRlXYIl2TNqMMJif0Jeey9/RQ3C4NOd2zp0/TRsDns9ttj2L523rsw==", - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "regenerator-transform": "^0.15.2" - } - }, - "@babel/plugin-transform-reserved-words": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.22.5.tgz", - "integrity": "sha512-DTtGKFRQUDm8svigJzZHzb/2xatPc6TzNvAIJ5GqOKDsGFYgAskjRulbR/vGsPKq3OPqtexnz327qYpP57RFyA==", - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-transform-runtime": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.23.2.tgz", - "integrity": "sha512-XOntj6icgzMS58jPVtQpiuF6ZFWxQiJavISGx5KGjRj+3gqZr8+N6Kx+N9BApWzgS+DOjIZfXXj0ZesenOWDyA==", - "requires": { - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "babel-plugin-polyfill-corejs2": "^0.4.6", - "babel-plugin-polyfill-corejs3": "^0.8.5", - "babel-plugin-polyfill-regenerator": "^0.5.3", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" - } - } - }, - "@babel/plugin-transform-shorthand-properties": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.22.5.tgz", - "integrity": "sha512-vM4fq9IXHscXVKzDv5itkO1X52SmdFBFcMIBZ2FRn2nqVYqw6dBexUgMvAjHW+KXpPPViD/Yo3GrDEBaRC0QYA==", - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-transform-spread": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.22.5.tgz", - "integrity": "sha512-5ZzDQIGyvN4w8+dMmpohL6MBo+l2G7tfC/O2Dg7/hjpgeWvUx8FzfeOKxGog9IimPa4YekaQ9PlDqTLOljkcxg==", - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" - } - }, - "@babel/plugin-transform-sticky-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.22.5.tgz", - "integrity": "sha512-zf7LuNpHG0iEeiyCNwX4j3gDg1jgt1k3ZdXBKbZSoA3BbGQGvMiSvfbZRR3Dr3aeJe3ooWFZxOOG3IRStYp2Bw==", - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-transform-template-literals": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.22.5.tgz", - "integrity": "sha512-5ciOehRNf+EyUeewo8NkbQiUs4d6ZxiHo6BcBcnFlgiJfu16q0bQUw9Jvo0b0gBKFG1SMhDSjeKXSYuJLeFSMA==", - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-transform-typeof-symbol": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.22.5.tgz", - "integrity": "sha512-bYkI5lMzL4kPii4HHEEChkD0rkc+nvnlR6+o/qdqR6zrm0Sv/nodmyLhlq2DO0YKLUNd2VePmPRjJXSBh9OIdA==", - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-transform-typescript": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.15.tgz", - "integrity": "sha512-1uirS0TnijxvQLnlv5wQBwOX3E1wCFX7ITv+9pBV2wKEk4K+M5tqDaoNXnTH8tjEIYHLO98MwiTWO04Ggz4XuA==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-typescript": "^7.22.5" - } - }, - "@babel/plugin-transform-unicode-escapes": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.10.tgz", - "integrity": "sha512-lRfaRKGZCBqDlRU3UIFovdp9c9mEvlylmpod0/OatICsSfuQ9YFthRo1tpTkGsklEefZdqlEFdY4A2dwTb6ohg==", - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-transform-unicode-property-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.22.5.tgz", - "integrity": "sha512-HCCIb+CbJIAE6sXn5CjFQXMwkCClcOfPCzTlilJ8cUatfzwHlWQkbtV0zD338u9dZskwvuOYTuuaMaA8J5EI5A==", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-transform-unicode-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.22.5.tgz", - "integrity": "sha512-028laaOKptN5vHJf9/Arr/HiJekMd41hOEZYvNsrsXqJ7YPYuX2bQxh31fkZzGmq3YqHRJzYFFAVYvKfMPKqyg==", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-transform-unicode-sets-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.22.5.tgz", - "integrity": "sha512-lhMfi4FC15j13eKrh3DnYHjpGj6UKQHtNKTbtc1igvAhRy4+kLhV07OpLcsN0VgDEw/MjAvJO4BdMJsHwMhzCg==", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/preset-env": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.23.2.tgz", - "integrity": "sha512-BW3gsuDD+rvHL2VO2SjAUNTBe5YrjsTiDyqamPDWY723na3/yPQ65X5oQkFVJZ0o50/2d+svm1rkPoJeR1KxVQ==", - "requires": { - "@babel/compat-data": "^7.23.2", - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-option": "^7.22.15", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.22.15", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.22.15", - "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.22.5", - "@babel/plugin-syntax-import-attributes": "^7.22.5", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", - "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.22.5", - "@babel/plugin-transform-async-generator-functions": "^7.23.2", - "@babel/plugin-transform-async-to-generator": "^7.22.5", - "@babel/plugin-transform-block-scoped-functions": "^7.22.5", - "@babel/plugin-transform-block-scoping": "^7.23.0", - "@babel/plugin-transform-class-properties": "^7.22.5", - "@babel/plugin-transform-class-static-block": "^7.22.11", - "@babel/plugin-transform-classes": "^7.22.15", - "@babel/plugin-transform-computed-properties": "^7.22.5", - "@babel/plugin-transform-destructuring": "^7.23.0", - "@babel/plugin-transform-dotall-regex": "^7.22.5", - "@babel/plugin-transform-duplicate-keys": "^7.22.5", - "@babel/plugin-transform-dynamic-import": "^7.22.11", - "@babel/plugin-transform-exponentiation-operator": "^7.22.5", - "@babel/plugin-transform-export-namespace-from": "^7.22.11", - "@babel/plugin-transform-for-of": "^7.22.15", - "@babel/plugin-transform-function-name": "^7.22.5", - "@babel/plugin-transform-json-strings": "^7.22.11", - "@babel/plugin-transform-literals": "^7.22.5", - "@babel/plugin-transform-logical-assignment-operators": "^7.22.11", - "@babel/plugin-transform-member-expression-literals": "^7.22.5", - "@babel/plugin-transform-modules-amd": "^7.23.0", - "@babel/plugin-transform-modules-commonjs": "^7.23.0", - "@babel/plugin-transform-modules-systemjs": "^7.23.0", - "@babel/plugin-transform-modules-umd": "^7.22.5", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", - "@babel/plugin-transform-new-target": "^7.22.5", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.11", - "@babel/plugin-transform-numeric-separator": "^7.22.11", - "@babel/plugin-transform-object-rest-spread": "^7.22.15", - "@babel/plugin-transform-object-super": "^7.22.5", - "@babel/plugin-transform-optional-catch-binding": "^7.22.11", - "@babel/plugin-transform-optional-chaining": "^7.23.0", - "@babel/plugin-transform-parameters": "^7.22.15", - "@babel/plugin-transform-private-methods": "^7.22.5", - "@babel/plugin-transform-private-property-in-object": "^7.22.11", - "@babel/plugin-transform-property-literals": "^7.22.5", - "@babel/plugin-transform-regenerator": "^7.22.10", - "@babel/plugin-transform-reserved-words": "^7.22.5", - "@babel/plugin-transform-shorthand-properties": "^7.22.5", - "@babel/plugin-transform-spread": "^7.22.5", - "@babel/plugin-transform-sticky-regex": "^7.22.5", - "@babel/plugin-transform-template-literals": "^7.22.5", - "@babel/plugin-transform-typeof-symbol": "^7.22.5", - "@babel/plugin-transform-unicode-escapes": "^7.22.10", - "@babel/plugin-transform-unicode-property-regex": "^7.22.5", - "@babel/plugin-transform-unicode-regex": "^7.22.5", - "@babel/plugin-transform-unicode-sets-regex": "^7.22.5", - "@babel/preset-modules": "0.1.6-no-external-plugins", - "@babel/types": "^7.23.0", - "babel-plugin-polyfill-corejs2": "^0.4.6", - "babel-plugin-polyfill-corejs3": "^0.8.5", - "babel-plugin-polyfill-regenerator": "^0.5.3", - "core-js-compat": "^3.31.0", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" - } - } - }, - "@babel/preset-modules": { - "version": "0.1.6-no-external-plugins", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", - "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - } - }, - "@babel/preset-react": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.22.15.tgz", - "integrity": "sha512-Csy1IJ2uEh/PecCBXXoZGAZBeCATTuePzCSB7dLYWS0vOEj6CNpjxIhW4duWwZodBNueH7QO14WbGn8YyeuN9w==", - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-option": "^7.22.15", - "@babel/plugin-transform-react-display-name": "^7.22.5", - "@babel/plugin-transform-react-jsx": "^7.22.15", - "@babel/plugin-transform-react-jsx-development": "^7.22.5", - "@babel/plugin-transform-react-pure-annotations": "^7.22.5" - } - }, - "@babel/preset-typescript": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.23.2.tgz", - "integrity": "sha512-u4UJc1XsS1GhIGteM8rnGiIvf9rJpiVgMEeCnwlLA7WJPC+jcXWJAGxYmeqs5hOZD8BbAfnV5ezBOxQbb4OUxA==", - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-option": "^7.22.15", - "@babel/plugin-syntax-jsx": "^7.22.5", - "@babel/plugin-transform-modules-commonjs": "^7.23.0", - "@babel/plugin-transform-typescript": "^7.22.15" - } - }, - "@babel/regjsgen": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" - }, - "@babel/runtime": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz", - "integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==", - "requires": { - "regenerator-runtime": "^0.14.0" - } - }, - "@babel/runtime-corejs3": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.23.2.tgz", - "integrity": "sha512-54cIh74Z1rp4oIjsHjqN+WM4fMyCBYe+LpZ9jWm51CZ1fbH3SkAzQD/3XLoNkjbJ7YEmjobLXyvQrFypRHOrXw==", - "requires": { - "core-js-pure": "^3.30.2", - "regenerator-runtime": "^0.14.0" - } - }, - "@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", - "requires": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" - } - }, - "@babel/traverse": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.5.tgz", - "integrity": "sha512-czx7Xy5a6sapWWRx61m1Ke1Ra4vczu1mCTtJam5zRTBOonfdJ+S/B6HYmGYu3fJtr8GGET3si6IhgWVBhJ/m8w==", - "requires": { - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.5", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.5", - "@babel/types": "^7.23.5", - "debug": "^4.1.0", - "globals": "^11.1.0" - } - }, - "@babel/types": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.5.tgz", - "integrity": "sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w==", - "requires": { - "@babel/helper-string-parser": "^7.23.4", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" - } - }, - "@braintree/sanitize-url": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-6.0.4.tgz", - "integrity": "sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A==" - }, - "@codemirror/autocomplete": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.10.2.tgz", - "integrity": "sha512-3dCL7b0j2GdtZzWN5j7HDpRAJ26ip07R4NGYz7QYthIYMiX8I4E4TNrYcdTayPJGeVQtd/xe7lWU4XL7THFb/w==", - "requires": { - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.17.0", - "@lezer/common": "^1.0.0" - } - }, - "@codemirror/commands": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.3.0.tgz", - "integrity": "sha512-tFfcxRIlOWiQDFhjBSWJ10MxcvbCIsRr6V64SgrcaY0MwNk32cUOcCuNlWo8VjV4qRQCgNgUAnIeo0svkk4R5Q==", - "requires": { - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.2.0", - "@codemirror/view": "^6.0.0", - "@lezer/common": "^1.1.0" - } - }, - "@codemirror/lang-css": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.2.1.tgz", - "integrity": "sha512-/UNWDNV5Viwi/1lpr/dIXJNWiwDxpw13I4pTUAsNxZdg6E0mI2kTQb0P2iHczg1Tu+H4EBgJR+hYhKiHKko7qg==", - "requires": { - "@codemirror/autocomplete": "^6.0.0", - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@lezer/common": "^1.0.2", - "@lezer/css": "^1.0.0" - } - }, - "@codemirror/lang-html": { - "version": "6.4.6", - "resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.6.tgz", - "integrity": "sha512-E4C8CVupBksXvgLSme/zv31x91g06eZHSph7NczVxZW+/K+3XgJGWNT//2WLzaKSBoxpAjaOi5ZnPU1SHhjh3A==", - "requires": { - "@codemirror/autocomplete": "^6.0.0", - "@codemirror/lang-css": "^6.0.0", - "@codemirror/lang-javascript": "^6.0.0", - "@codemirror/language": "^6.4.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.17.0", - "@lezer/common": "^1.0.0", - "@lezer/css": "^1.1.0", - "@lezer/html": "^1.3.0" - } - }, - "@codemirror/lang-javascript": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.1.tgz", - "integrity": "sha512-jlFOXTejVyiQCW3EQwvKH0m99bUYIw40oPmFjSX2VS78yzfe0HELZ+NEo9Yfo1MkGRpGlj3Gnu4rdxV1EnAs5A==", - "requires": { - "@codemirror/autocomplete": "^6.0.0", - "@codemirror/language": "^6.6.0", - "@codemirror/lint": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.17.0", - "@lezer/common": "^1.0.0", - "@lezer/javascript": "^1.0.0" - } - }, - "@codemirror/language": { - "version": "6.9.2", - "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.9.2.tgz", - "integrity": "sha512-QGTQXSpAKDIzaSE96zNK1UfIUhPgkT1CLjh1N5qVzZuxgsEOhz5RqaN8QCIdyOQklGLx3MgHd9YrE3X3+Pl1ow==", - "requires": { - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "@lezer/common": "^1.1.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0", - "style-mod": "^4.0.0" - } - }, - "@codemirror/lint": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.4.2.tgz", - "integrity": "sha512-wzRkluWb1ptPKdzlsrbwwjYCPLgzU6N88YBAmlZi8WFyuiEduSd05MnJYNogzyc8rPK7pj6m95ptUApc8sHKVA==", - "requires": { - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "crelt": "^1.0.5" - } - }, - "@codemirror/state": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.3.1.tgz", - "integrity": "sha512-88e4HhMtKJyw6fKprGaN/yZfiaoGYOi2nM45YCUC6R/kex9sxFWBDGatS1vk4lMgnWmdIIB9tk8Gj1LmL8YfvA==" - }, - "@codemirror/view": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.22.0.tgz", - "integrity": "sha512-6zLj4YIoIpfTGKrDMTbeZRpa8ih4EymMCKmddEDcJWrCdp/N1D46B38YEz4creTb4T177AVS9EyXkLeC/HL2jA==", - "requires": { - "@codemirror/state": "^6.1.4", - "style-mod": "^4.1.0", - "w3c-keyname": "^2.2.4" - } - }, - "@codesandbox/nodebox": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/@codesandbox/nodebox/-/nodebox-0.1.8.tgz", - "integrity": "sha512-2VRS6JDSk+M+pg56GA6CryyUSGPjBEe8Pnae0QL3jJF1mJZJVMDKr93gJRtBbLkfZN6LD/DwMtf+2L0bpWrjqg==", - "requires": { - "outvariant": "^1.4.0", - "strict-event-emitter": "^0.4.3" - } - }, - "@codesandbox/sandpack-client": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/@codesandbox/sandpack-client/-/sandpack-client-2.9.0.tgz", - "integrity": "sha512-KkG/YusBsL0RnI3P079cckHrRleLaGQVM5Plzn6xWOPM04Dce52MMkr6XIU77ZMjZm/zZ5pmgXLW7M6HoyIy/A==", - "requires": { - "@codesandbox/nodebox": "0.1.8", - "buffer": "^6.0.3", - "dequal": "^2.0.2", - "outvariant": "1.4.0", - "static-browser-server": "1.0.3" - } - }, - "@codesandbox/sandpack-react": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/@codesandbox/sandpack-react/-/sandpack-react-2.9.0.tgz", - "integrity": "sha512-A0quGugVyWbRktpdq2Nc6W1+XV0QnHq1lbqDCHAs2ijfWxvhhNaqMr6lWDaG/NTUGRNLUNETbipcNaBeC0dxhw==", - "requires": { - "@codemirror/autocomplete": "^6.4.0", - "@codemirror/commands": "^6.1.3", - "@codemirror/lang-css": "^6.0.1", - "@codemirror/lang-html": "^6.4.0", - "@codemirror/lang-javascript": "^6.1.2", - "@codemirror/language": "^6.3.2", - "@codemirror/state": "^6.2.0", - "@codemirror/view": "^6.7.1", - "@codesandbox/sandpack-client": "^2.9.0", - "@lezer/highlight": "^1.1.3", - "@react-hook/intersection-observer": "^3.1.1", - "@stitches/core": "^1.2.6", - "anser": "^2.1.1", - "clean-set": "^1.1.2", - "codesandbox-import-util-types": "^2.2.3", - "dequal": "^2.0.2", - "escape-carriage": "^1.3.1", - "lz-string": "^1.4.4", - "react-devtools-inline": "4.4.0", - "react-is": "^17.0.2" - } - }, - "@codesandbox/sandpack-themes": { - "version": "2.0.21", - "resolved": "https://registry.npmjs.org/@codesandbox/sandpack-themes/-/sandpack-themes-2.0.21.tgz", - "integrity": "sha512-CMH/MO/dh6foPYb/3eSn2Cu/J3+1+/81Fsaj7VggICkCrmRk0qG5dmgjGAearPTnRkOGORIPHuRqwNXgw0E6YQ==" - }, - "@colors/colors": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", - "optional": true - }, - "@discoveryjs/json-ext": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", - "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==" - }, - "@docsearch/css": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.6.0.tgz", - "integrity": "sha512-+sbxb71sWre+PwDK7X2T8+bhS6clcVMLwBPznX45Qu6opJcgRjAp7gYSDzVFp187J+feSj5dNBN1mJoi6ckkUQ==" - }, - "@docsearch/react": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.6.0.tgz", - "integrity": "sha512-HUFut4ztcVNmqy9gp/wxNbC7pTOHhgVVkHVGCACTuLhUKUhKAF9KYHJtMiLUJxEqiFLQiuri1fWF8zqwM/cu1w==", - "requires": { - "@algolia/autocomplete-core": "1.9.3", - "@algolia/autocomplete-preset-algolia": "1.9.3", - "@docsearch/css": "3.6.0", - "algoliasearch": "^4.19.1" - } - }, - "@docusaurus/core": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.4.0.tgz", - "integrity": "sha512-g+0wwmN2UJsBqy2fQRQ6fhXruoEa62JDeEa5d8IdTJlMoaDaEDfHh7WjwGRn4opuTQWpjAwP/fbcgyHKlE+64w==", - "requires": { - "@babel/core": "^7.23.3", - "@babel/generator": "^7.23.3", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-transform-runtime": "^7.22.9", - "@babel/preset-env": "^7.22.9", - "@babel/preset-react": "^7.22.5", - "@babel/preset-typescript": "^7.22.5", - "@babel/runtime": "^7.22.6", - "@babel/runtime-corejs3": "^7.22.6", - "@babel/traverse": "^7.22.8", - "@docusaurus/cssnano-preset": "3.4.0", - "@docusaurus/logger": "3.4.0", - "@docusaurus/mdx-loader": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-common": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", - "autoprefixer": "^10.4.14", - "babel-loader": "^9.1.3", - "babel-plugin-dynamic-import-node": "^2.3.3", - "boxen": "^6.2.1", - "chalk": "^4.1.2", - "chokidar": "^3.5.3", - "clean-css": "^5.3.2", - "cli-table3": "^0.6.3", - "combine-promises": "^1.1.0", - "commander": "^5.1.0", - "copy-webpack-plugin": "^11.0.0", - "core-js": "^3.31.1", - "css-loader": "^6.8.1", - "css-minimizer-webpack-plugin": "^5.0.1", - "cssnano": "^6.1.2", - "del": "^6.1.1", - "detect-port": "^1.5.1", - "escape-html": "^1.0.3", - "eta": "^2.2.0", - "eval": "^0.1.8", - "file-loader": "^6.2.0", - "fs-extra": "^11.1.1", - "html-minifier-terser": "^7.2.0", - "html-tags": "^3.3.1", - "html-webpack-plugin": "^5.5.3", - "leven": "^3.1.0", - "lodash": "^4.17.21", - "mini-css-extract-plugin": "^2.7.6", - "p-map": "^4.0.0", - "postcss": "^8.4.26", - "postcss-loader": "^7.3.3", - "prompts": "^2.4.2", - "react-dev-utils": "^12.0.1", - "react-helmet-async": "^1.3.0", - "react-loadable": "npm:@docusaurus/react-loadable@6.0.0", - "react-loadable-ssr-addon-v5-slorber": "^1.0.1", - "react-router": "^5.3.4", - "react-router-config": "^5.1.1", - "react-router-dom": "^5.3.4", - "rtl-detect": "^1.0.4", - "semver": "^7.5.4", - "serve-handler": "^6.1.5", - "shelljs": "^0.8.5", - "terser-webpack-plugin": "^5.3.9", - "tslib": "^2.6.0", - "update-notifier": "^6.0.2", - "url-loader": "^4.1.1", - "webpack": "^5.88.1", - "webpack-bundle-analyzer": "^4.9.0", - "webpack-dev-server": "^4.15.1", - "webpack-merge": "^5.9.0", - "webpackbar": "^5.0.2" - } - }, - "@docusaurus/cssnano-preset": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-3.4.0.tgz", - "integrity": "sha512-qwLFSz6v/pZHy/UP32IrprmH5ORce86BGtN0eBtG75PpzQJAzp9gefspox+s8IEOr0oZKuQ/nhzZ3xwyc3jYJQ==", - "requires": { - "cssnano-preset-advanced": "^6.1.2", - "postcss": "^8.4.38", - "postcss-sort-media-queries": "^5.2.0", - "tslib": "^2.6.0" - } - }, - "@docusaurus/logger": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-3.4.0.tgz", - "integrity": "sha512-bZwkX+9SJ8lB9kVRkXw+xvHYSMGG4bpYHKGXeXFvyVc79NMeeBSGgzd4TQLHH+DYeOJoCdl8flrFJVxlZ0wo/Q==", - "requires": { - "chalk": "^4.1.2", - "tslib": "^2.6.0" - } - }, - "@docusaurus/mdx-loader": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.4.0.tgz", - "integrity": "sha512-kSSbrrk4nTjf4d+wtBA9H+FGauf2gCax89kV8SUSJu3qaTdSIKdWERlngsiHaCFgZ7laTJ8a67UFf+xlFPtuTw==", - "requires": { - "@docusaurus/logger": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", - "@mdx-js/mdx": "^3.0.0", - "@slorber/remark-comment": "^1.0.0", - "escape-html": "^1.0.3", - "estree-util-value-to-estree": "^3.0.1", - "file-loader": "^6.2.0", - "fs-extra": "^11.1.1", - "image-size": "^1.0.2", - "mdast-util-mdx": "^3.0.0", - "mdast-util-to-string": "^4.0.0", - "rehype-raw": "^7.0.0", - "remark-directive": "^3.0.0", - "remark-emoji": "^4.0.0", - "remark-frontmatter": "^5.0.0", - "remark-gfm": "^4.0.0", - "stringify-object": "^3.3.0", - "tslib": "^2.6.0", - "unified": "^11.0.3", - "unist-util-visit": "^5.0.0", - "url-loader": "^4.1.1", - "vfile": "^6.0.1", - "webpack": "^5.88.1" - } - }, - "@docusaurus/module-type-aliases": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.4.0.tgz", - "integrity": "sha512-A1AyS8WF5Bkjnb8s+guTDuYmUiwJzNrtchebBHpc0gz0PyHJNMaybUlSrmJjHVcGrya0LKI4YcR3lBDQfXRYLw==", - "requires": { - "@docusaurus/types": "3.4.0", - "@types/history": "^4.7.11", - "@types/react": "*", - "@types/react-router-config": "*", - "@types/react-router-dom": "*", - "react-helmet-async": "*", - "react-loadable": "npm:@docusaurus/react-loadable@6.0.0" - } - }, - "@docusaurus/plugin-content-blog": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.4.0.tgz", - "integrity": "sha512-vv6ZAj78ibR5Jh7XBUT4ndIjmlAxkijM3Sx5MAAzC1gyv0vupDQNhzuFg1USQmQVj3P5I6bquk12etPV3LJ+Xw==", - "requires": { - "@docusaurus/core": "3.4.0", - "@docusaurus/logger": "3.4.0", - "@docusaurus/mdx-loader": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-common": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", - "cheerio": "^1.0.0-rc.12", - "feed": "^4.2.2", - "fs-extra": "^11.1.1", - "lodash": "^4.17.21", - "reading-time": "^1.5.0", - "srcset": "^4.0.0", - "tslib": "^2.6.0", - "unist-util-visit": "^5.0.0", - "utility-types": "^3.10.0", - "webpack": "^5.88.1" - } - }, - "@docusaurus/plugin-content-docs": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.4.0.tgz", - "integrity": "sha512-HkUCZffhBo7ocYheD9oZvMcDloRnGhBMOZRyVcAQRFmZPmNqSyISlXA1tQCIxW+r478fty97XXAGjNYzBjpCsg==", - "requires": { - "@docusaurus/core": "3.4.0", - "@docusaurus/logger": "3.4.0", - "@docusaurus/mdx-loader": "3.4.0", - "@docusaurus/module-type-aliases": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-common": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", - "@types/react-router-config": "^5.0.7", - "combine-promises": "^1.1.0", - "fs-extra": "^11.1.1", - "js-yaml": "^4.1.0", - "lodash": "^4.17.21", - "tslib": "^2.6.0", - "utility-types": "^3.10.0", - "webpack": "^5.88.1" - } - }, - "@docusaurus/plugin-content-pages": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.4.0.tgz", - "integrity": "sha512-h2+VN/0JjpR8fIkDEAoadNjfR3oLzB+v1qSXbIAKjQ46JAHx3X22n9nqS+BWSQnTnp1AjkjSvZyJMekmcwxzxg==", - "requires": { - "@docusaurus/core": "3.4.0", - "@docusaurus/mdx-loader": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", - "fs-extra": "^11.1.1", - "tslib": "^2.6.0", - "webpack": "^5.88.1" - } - }, - "@docusaurus/plugin-debug": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-3.4.0.tgz", - "integrity": "sha512-uV7FDUNXGyDSD3PwUaf5YijX91T5/H9SX4ErEcshzwgzWwBtK37nUWPU3ZLJfeTavX3fycTOqk9TglpOLaWkCg==", - "requires": { - "@docusaurus/core": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils": "3.4.0", - "fs-extra": "^11.1.1", - "react-json-view-lite": "^1.2.0", - "tslib": "^2.6.0" - } - }, - "@docusaurus/plugin-google-analytics": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.4.0.tgz", - "integrity": "sha512-mCArluxEGi3cmYHqsgpGGt3IyLCrFBxPsxNZ56Mpur0xSlInnIHoeLDH7FvVVcPJRPSQ9/MfRqLsainRw+BojA==", - "requires": { - "@docusaurus/core": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", - "tslib": "^2.6.0" - } - }, - "@docusaurus/plugin-google-gtag": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.4.0.tgz", - "integrity": "sha512-Dsgg6PLAqzZw5wZ4QjUYc8Z2KqJqXxHxq3vIoyoBWiLEEfigIs7wHR+oiWUQy3Zk9MIk6JTYj7tMoQU0Jm3nqA==", - "requires": { - "@docusaurus/core": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", - "@types/gtag.js": "^0.0.12", - "tslib": "^2.6.0" - } - }, - "@docusaurus/plugin-google-tag-manager": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.4.0.tgz", - "integrity": "sha512-O9tX1BTwxIhgXpOLpFDueYA9DWk69WCbDRrjYoMQtFHSkTyE7RhNgyjSPREUWJb9i+YUg3OrsvrBYRl64FCPCQ==", - "requires": { - "@docusaurus/core": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", - "tslib": "^2.6.0" - } - }, - "@docusaurus/plugin-sitemap": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.4.0.tgz", - "integrity": "sha512-+0VDvx9SmNrFNgwPoeoCha+tRoAjopwT0+pYO1xAbyLcewXSemq+eLxEa46Q1/aoOaJQ0qqHELuQM7iS2gp33Q==", - "requires": { - "@docusaurus/core": "3.4.0", - "@docusaurus/logger": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-common": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", - "fs-extra": "^11.1.1", - "sitemap": "^7.1.1", - "tslib": "^2.6.0" - } - }, - "@docusaurus/preset-classic": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-3.4.0.tgz", - "integrity": "sha512-Ohj6KB7siKqZaQhNJVMBBUzT3Nnp6eTKqO+FXO3qu/n1hJl3YLwVKTWBg28LF7MWrKu46UuYavwMRxud0VyqHg==", - "requires": { - "@docusaurus/core": "3.4.0", - "@docusaurus/plugin-content-blog": "3.4.0", - "@docusaurus/plugin-content-docs": "3.4.0", - "@docusaurus/plugin-content-pages": "3.4.0", - "@docusaurus/plugin-debug": "3.4.0", - "@docusaurus/plugin-google-analytics": "3.4.0", - "@docusaurus/plugin-google-gtag": "3.4.0", - "@docusaurus/plugin-google-tag-manager": "3.4.0", - "@docusaurus/plugin-sitemap": "3.4.0", - "@docusaurus/theme-classic": "3.4.0", - "@docusaurus/theme-common": "3.4.0", - "@docusaurus/theme-search-algolia": "3.4.0", - "@docusaurus/types": "3.4.0" - } - }, - "@docusaurus/theme-classic": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-3.4.0.tgz", - "integrity": "sha512-0IPtmxsBYv2adr1GnZRdMkEQt1YW6tpzrUPj02YxNpvJ5+ju4E13J5tB4nfdaen/tfR1hmpSPlTFPvTf4kwy8Q==", - "requires": { - "@docusaurus/core": "3.4.0", - "@docusaurus/mdx-loader": "3.4.0", - "@docusaurus/module-type-aliases": "3.4.0", - "@docusaurus/plugin-content-blog": "3.4.0", - "@docusaurus/plugin-content-docs": "3.4.0", - "@docusaurus/plugin-content-pages": "3.4.0", - "@docusaurus/theme-common": "3.4.0", - "@docusaurus/theme-translations": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-common": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", - "@mdx-js/react": "^3.0.0", - "clsx": "^2.0.0", - "copy-text-to-clipboard": "^3.2.0", - "infima": "0.2.0-alpha.43", - "lodash": "^4.17.21", - "nprogress": "^0.2.0", - "postcss": "^8.4.26", - "prism-react-renderer": "^2.3.0", - "prismjs": "^1.29.0", - "react-router-dom": "^5.3.4", - "rtlcss": "^4.1.0", - "tslib": "^2.6.0", - "utility-types": "^3.10.0" - } - }, - "@docusaurus/theme-common": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.4.0.tgz", - "integrity": "sha512-0A27alXuv7ZdCg28oPE8nH/Iz73/IUejVaCazqu9elS4ypjiLhK3KfzdSQBnL/g7YfHSlymZKdiOHEo8fJ0qMA==", - "requires": { - "@docusaurus/mdx-loader": "3.4.0", - "@docusaurus/module-type-aliases": "3.4.0", - "@docusaurus/plugin-content-blog": "3.4.0", - "@docusaurus/plugin-content-docs": "3.4.0", - "@docusaurus/plugin-content-pages": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-common": "3.4.0", - "@types/history": "^4.7.11", - "@types/react": "*", - "@types/react-router-config": "*", - "clsx": "^2.0.0", - "parse-numeric-range": "^1.3.0", - "prism-react-renderer": "^2.3.0", - "tslib": "^2.6.0", - "utility-types": "^3.10.0" - } - }, - "@docusaurus/theme-mermaid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-mermaid/-/theme-mermaid-3.4.0.tgz", - "integrity": "sha512-3w5QW0HEZ2O6x2w6lU3ZvOe1gNXP2HIoKDMJBil1VmLBc9PmpAG17VmfhI/p3L2etNmOiVs5GgniUqvn8AFEGQ==", - "requires": { - "@docusaurus/core": "3.4.0", - "@docusaurus/module-type-aliases": "3.4.0", - "@docusaurus/theme-common": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", - "mermaid": "^10.4.0", - "tslib": "^2.6.0" - } - }, - "@docusaurus/theme-search-algolia": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.4.0.tgz", - "integrity": "sha512-aiHFx7OCw4Wck1z6IoShVdUWIjntC8FHCw9c5dR8r3q4Ynh+zkS8y2eFFunN/DL6RXPzpnvKCg3vhLQYJDmT9Q==", - "requires": { - "@docsearch/react": "^3.5.2", - "@docusaurus/core": "3.4.0", - "@docusaurus/logger": "3.4.0", - "@docusaurus/plugin-content-docs": "3.4.0", - "@docusaurus/theme-common": "3.4.0", - "@docusaurus/theme-translations": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", - "algoliasearch": "^4.18.0", - "algoliasearch-helper": "^3.13.3", - "clsx": "^2.0.0", - "eta": "^2.2.0", - "fs-extra": "^11.1.1", - "lodash": "^4.17.21", - "tslib": "^2.6.0", - "utility-types": "^3.10.0" - } - }, - "@docusaurus/theme-translations": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.4.0.tgz", - "integrity": "sha512-zSxCSpmQCCdQU5Q4CnX/ID8CSUUI3fvmq4hU/GNP/XoAWtXo9SAVnM3TzpU8Gb//H3WCsT8mJcTfyOk3d9ftNg==", - "requires": { - "fs-extra": "^11.1.1", - "tslib": "^2.6.0" - } - }, - "@docusaurus/tsconfig": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/tsconfig/-/tsconfig-3.4.0.tgz", - "integrity": "sha512-0qENiJ+TRaeTzcg4olrnh0BQ7eCxTgbYWBnWUeQDc84UYkt/T3pDNnm3SiQkqPb+YQ1qtYFlC0RriAElclo8Dg==" - }, - "@docusaurus/types": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.4.0.tgz", - "integrity": "sha512-4jcDO8kXi5Cf9TcyikB/yKmz14f2RZ2qTRerbHAsS+5InE9ZgSLBNLsewtFTcTOXSVcbU3FoGOzcNWAmU1TR0A==", - "requires": { - "@mdx-js/mdx": "^3.0.0", - "@types/history": "^4.7.11", - "@types/react": "*", - "commander": "^5.1.0", - "joi": "^17.9.2", - "react-helmet-async": "^1.3.0", - "utility-types": "^3.10.0", - "webpack": "^5.88.1", - "webpack-merge": "^5.9.0" - } - }, - "@docusaurus/utils": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.4.0.tgz", - "integrity": "sha512-fRwnu3L3nnWaXOgs88BVBmG1yGjcQqZNHG+vInhEa2Sz2oQB+ZjbEMO5Rh9ePFpZ0YDiDUhpaVjwmS+AU2F14g==", - "requires": { - "@docusaurus/logger": "3.4.0", - "@docusaurus/utils-common": "3.4.0", - "@svgr/webpack": "^8.1.0", - "escape-string-regexp": "^4.0.0", - "file-loader": "^6.2.0", - "fs-extra": "^11.1.1", - "github-slugger": "^1.5.0", - "globby": "^11.1.0", - "gray-matter": "^4.0.3", - "jiti": "^1.20.0", - "js-yaml": "^4.1.0", - "lodash": "^4.17.21", - "micromatch": "^4.0.5", - "prompts": "^2.4.2", - "resolve-pathname": "^3.0.0", - "shelljs": "^0.8.5", - "tslib": "^2.6.0", - "url-loader": "^4.1.1", - "utility-types": "^3.10.0", - "webpack": "^5.88.1" - } - }, - "@docusaurus/utils-common": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.4.0.tgz", - "integrity": "sha512-NVx54Wr4rCEKsjOH5QEVvxIqVvm+9kh7q8aYTU5WzUU9/Hctd6aTrcZ3G0Id4zYJ+AeaG5K5qHA4CY5Kcm2iyQ==", - "requires": { - "tslib": "^2.6.0" - } - }, - "@docusaurus/utils-validation": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.4.0.tgz", - "integrity": "sha512-hYQ9fM+AXYVTWxJOT1EuNaRnrR2WGpRdLDQG07O8UOpsvCPWUVOeo26Rbm0JWY2sGLfzAb+tvJ62yF+8F+TV0g==", - "requires": { - "@docusaurus/logger": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-common": "3.4.0", - "fs-extra": "^11.2.0", - "joi": "^17.9.2", - "js-yaml": "^4.1.0", - "lodash": "^4.17.21", - "tslib": "^2.6.0" - } - }, - "@hapi/hoek": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", - "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" - }, - "@hapi/topo": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", - "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", - "requires": { - "@hapi/hoek": "^9.0.0" - } - }, - "@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "requires": { - "@sinclair/typebox": "^0.27.8" - } - }, - "@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "requires": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, - "@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==" - }, - "@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==" - }, - "@jridgewell/source-map": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", - "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", - "requires": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" - }, - "@jridgewell/trace-mapping": { - "version": "0.3.20", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", - "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", - "requires": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "@leichtgewicht/ip-codec": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", - "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" - }, - "@lezer/common": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.1.0.tgz", - "integrity": "sha512-XPIN3cYDXsoJI/oDWoR2tD++juVrhgIago9xyKhZ7IhGlzdDM9QgC8D8saKNCz5pindGcznFr2HBSsEQSWnSjw==" - }, - "@lezer/css": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.1.3.tgz", - "integrity": "sha512-SjSM4pkQnQdJDVc80LYzEaMiNy9txsFbI7HsMgeVF28NdLaAdHNtQ+kB/QqDUzRBV/75NTXjJ/R5IdC8QQGxMg==", - "requires": { - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0" - } - }, - "@lezer/highlight": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.1.6.tgz", - "integrity": "sha512-cmSJYa2us+r3SePpRCjN5ymCqCPv+zyXmDl0ciWtVaNiORT/MxM7ZgOMQZADD0o51qOaOg24qc/zBViOIwAjJg==", - "requires": { - "@lezer/common": "^1.0.0" - } - }, - "@lezer/html": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.6.tgz", - "integrity": "sha512-Kk9HJARZTc0bAnMQUqbtuhFVsB4AnteR2BFUWfZV7L/x1H0aAKz6YabrfJ2gk/BEgjh9L3hg5O4y2IDZRBdzuQ==", - "requires": { - "@lezer/common": "^1.0.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0" - } - }, - "@lezer/javascript": { - "version": "1.4.9", - "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.4.9.tgz", - "integrity": "sha512-7Uv8mBBE6l44spgWEZvEMdDqGV+FIuY7kJ1o5TFm+jxIuxydO3PcKJYiINij09igd1D/9P7l2KDqpkN8c3bM6A==", - "requires": { - "@lezer/highlight": "^1.1.3", - "@lezer/lr": "^1.3.0" - } - }, - "@lezer/lr": { - "version": "1.3.14", - "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.3.14.tgz", - "integrity": "sha512-z5mY4LStlA3yL7aHT/rqgG614cfcvklS+8oFRFBYrs4YaWLJyKKM4+nN6KopToX0o9Hj6zmH6M5kinOYuy06ug==", - "requires": { - "@lezer/common": "^1.0.0" - } - }, - "@mdx-js/mdx": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-3.0.1.tgz", - "integrity": "sha512-eIQ4QTrOWyL3LWEe/bu6Taqzq2HQvHcyTMaOrI95P2/LmJE7AsfPfgJGuFLPVqBUE1BC1rik3VIhU+s9u72arA==", - "requires": { - "@types/estree": "^1.0.0", - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdx": "^2.0.0", - "collapse-white-space": "^2.0.0", - "devlop": "^1.0.0", - "estree-util-build-jsx": "^3.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "estree-util-to-js": "^2.0.0", - "estree-walker": "^3.0.0", - "hast-util-to-estree": "^3.0.0", - "hast-util-to-jsx-runtime": "^2.0.0", - "markdown-extensions": "^2.0.0", - "periscopic": "^3.0.0", - "remark-mdx": "^3.0.0", - "remark-parse": "^11.0.0", - "remark-rehype": "^11.0.0", - "source-map": "^0.7.0", - "unified": "^11.0.0", - "unist-util-position-from-estree": "^2.0.0", - "unist-util-stringify-position": "^4.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - } - }, - "@mdx-js/react": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.0.0.tgz", - "integrity": "sha512-nDctevR9KyYFyV+m+/+S4cpzCWHqj+iHDHq3QrsWezcC+B17uZdIWgCguESUkwFhM3n/56KxWVE3V6EokrmONQ==", - "requires": { - "@types/mdx": "^2.0.0" - } - }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, - "@open-draft/deferred-promise": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz", - "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==" - }, - "@pnpm/config.env-replace": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", - "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==" - }, - "@pnpm/network.ca-file": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz", - "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==", - "requires": { - "graceful-fs": "4.2.10" - }, - "dependencies": { - "graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" - } - } - }, - "@pnpm/npm-conf": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-2.2.2.tgz", - "integrity": "sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==", - "requires": { - "@pnpm/config.env-replace": "^1.1.0", - "@pnpm/network.ca-file": "^1.0.1", - "config-chain": "^1.1.11" - } - }, - "@polka/url": { - "version": "1.0.0-next.23", - "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.23.tgz", - "integrity": "sha512-C16M+IYz0rgRhWZdCmK+h58JMv8vijAA61gmz2rspCSwKwzBebpdcsiUmwrtJRdphuY30i6BSLEOP8ppbNLyLg==" - }, - "@react-hook/intersection-observer": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@react-hook/intersection-observer/-/intersection-observer-3.1.1.tgz", - "integrity": "sha512-OTDx8/wFaRvzFtKl1dEUEXSOqK2zVJHporiTTdC2xO++0e9FEx9wIrPis5q3lqtXeZH9zYGLbk+aB75qNFbbuw==", - "requires": { - "@react-hook/passive-layout-effect": "^1.2.0", - "intersection-observer": "^0.10.0" - } - }, - "@react-hook/passive-layout-effect": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@react-hook/passive-layout-effect/-/passive-layout-effect-1.2.1.tgz", - "integrity": "sha512-IwEphTD75liO8g+6taS+4oqz+nnroocNfWVHWz7j+N+ZO2vYrc6PV1q7GQhuahL0IOR7JccFTsFKQ/mb6iZWAg==", - "requires": {} - }, - "@sideway/address": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz", - "integrity": "sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==", - "requires": { - "@hapi/hoek": "^9.0.0" - } - }, - "@sideway/formula": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", - "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==" - }, - "@sideway/pinpoint": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", - "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" - }, - "@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==" - }, - "@sindresorhus/is": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", - "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==" - }, - "@slorber/remark-comment": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@slorber/remark-comment/-/remark-comment-1.0.0.tgz", - "integrity": "sha512-RCE24n7jsOj1M0UPvIQCHTe7fI0sFL4S2nwKVWwHyVr/wI/H8GosgsJGyhnsZoGFnD/P2hLf1mSbrrgSLN93NA==", - "requires": { - "micromark-factory-space": "^1.0.0", - "micromark-util-character": "^1.1.0", - "micromark-util-symbol": "^1.0.1" - } - }, - "@stitches/core": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@stitches/core/-/core-1.2.8.tgz", - "integrity": "sha512-Gfkvwk9o9kE9r9XNBmJRfV8zONvXThnm1tcuojL04Uy5uRyqg93DC83lDebl0rocZCfKSjUv+fWYtMQmEDJldg==" - }, - "@svgr/babel-plugin-add-jsx-attribute": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", - "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==", - "requires": {} - }, - "@svgr/babel-plugin-remove-jsx-attribute": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz", - "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==", - "requires": {} - }, - "@svgr/babel-plugin-remove-jsx-empty-expression": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz", - "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==", - "requires": {} - }, - "@svgr/babel-plugin-replace-jsx-attribute-value": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz", - "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==", - "requires": {} - }, - "@svgr/babel-plugin-svg-dynamic-title": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz", - "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==", - "requires": {} - }, - "@svgr/babel-plugin-svg-em-dimensions": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz", - "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==", - "requires": {} - }, - "@svgr/babel-plugin-transform-react-native-svg": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz", - "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==", - "requires": {} - }, - "@svgr/babel-plugin-transform-svg-component": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz", - "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==", - "requires": {} - }, - "@svgr/babel-preset": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz", - "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==", - "requires": { - "@svgr/babel-plugin-add-jsx-attribute": "8.0.0", - "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0", - "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0", - "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0", - "@svgr/babel-plugin-svg-dynamic-title": "8.0.0", - "@svgr/babel-plugin-svg-em-dimensions": "8.0.0", - "@svgr/babel-plugin-transform-react-native-svg": "8.1.0", - "@svgr/babel-plugin-transform-svg-component": "8.0.0" - } - }, - "@svgr/core": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", - "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", - "requires": { - "@babel/core": "^7.21.3", - "@svgr/babel-preset": "8.1.0", - "camelcase": "^6.2.0", - "cosmiconfig": "^8.1.3", - "snake-case": "^3.0.4" - } - }, - "@svgr/hast-util-to-babel-ast": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz", - "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==", - "requires": { - "@babel/types": "^7.21.3", - "entities": "^4.4.0" - } - }, - "@svgr/plugin-jsx": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz", - "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==", - "requires": { - "@babel/core": "^7.21.3", - "@svgr/babel-preset": "8.1.0", - "@svgr/hast-util-to-babel-ast": "8.0.0", - "svg-parser": "^2.0.4" - } - }, - "@svgr/plugin-svgo": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-8.1.0.tgz", - "integrity": "sha512-Ywtl837OGO9pTLIN/onoWLmDQ4zFUycI1g76vuKGEz6evR/ZTJlJuz3G/fIkb6OVBJ2g0o6CGJzaEjfmEo3AHA==", - "requires": { - "cosmiconfig": "^8.1.3", - "deepmerge": "^4.3.1", - "svgo": "^3.0.2" - } - }, - "@svgr/webpack": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-8.1.0.tgz", - "integrity": "sha512-LnhVjMWyMQV9ZmeEy26maJk+8HTIbd59cH4F2MJ439k9DqejRisfFNGAPvRYlKETuh9LrImlS8aKsBgKjMA8WA==", - "requires": { - "@babel/core": "^7.21.3", - "@babel/plugin-transform-react-constant-elements": "^7.21.3", - "@babel/preset-env": "^7.20.2", - "@babel/preset-react": "^7.18.6", - "@babel/preset-typescript": "^7.21.0", - "@svgr/core": "8.1.0", - "@svgr/plugin-jsx": "8.1.0", - "@svgr/plugin-svgo": "8.1.0" - } - }, - "@szmarczak/http-timer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", - "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", - "requires": { - "defer-to-connect": "^2.0.1" - } - }, - "@trysound/sax": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", - "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==" - }, - "@types/acorn": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@types/acorn/-/acorn-4.0.6.tgz", - "integrity": "sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ==", - "requires": { - "@types/estree": "*" - } - }, - "@types/body-parser": { - "version": "1.19.5", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", - "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", - "requires": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "@types/bonjour": { - "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz", - "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", - "requires": { - "@types/node": "*" - } - }, - "@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "requires": { - "@types/node": "*" - } - }, - "@types/connect-history-api-fallback": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.3.tgz", - "integrity": "sha512-6mfQ6iNvhSKCZJoY6sIG3m0pKkdUcweVNOLuBBKvoWGzl2yRxOJcYOTRyLKt3nxXvBLJWa6QkW//tgbIwJehmA==", - "requires": { - "@types/express-serve-static-core": "*", - "@types/node": "*" - } - }, - "@types/d3-scale": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz", - "integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==", - "requires": { - "@types/d3-time": "*" - } - }, - "@types/d3-scale-chromatic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.0.3.tgz", - "integrity": "sha512-laXM4+1o5ImZv3RpFAsTRn3TEkzqkytiOY0Dz0sq5cnd1dtNlk6sHLon4OvqaiJb28T0S/TdsBI3Sjsy+keJrw==" - }, - "@types/d3-time": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.3.tgz", - "integrity": "sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==" - }, - "@types/debug": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.11.tgz", - "integrity": "sha512-R2qflTjHDs4CL6D/6TkqBeIHr54WzZfIxN729xvCNlYIVp2LknlnCro5Yo3frNaX2E5gO9pZ3/QAPVdGmu+q9w==", - "requires": { - "@types/ms": "*" - } - }, - "@types/eslint": { - "version": "8.44.7", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.7.tgz", - "integrity": "sha512-f5ORu2hcBbKei97U73mf+l9t4zTGl74IqZ0GQk4oVea/VS8tQZYkUveSYojk+frraAVYId0V2WC9O4PTNru2FQ==", - "requires": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "requires": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, - "@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" - }, - "@types/estree-jsx": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", - "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", - "requires": { - "@types/estree": "*" - } - }, - "@types/express": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", - "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", - "requires": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "@types/express-serve-static-core": { - "version": "4.17.41", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.41.tgz", - "integrity": "sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==", - "requires": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "@types/gtag.js": { - "version": "0.0.12", - "resolved": "https://registry.npmjs.org/@types/gtag.js/-/gtag.js-0.0.12.tgz", - "integrity": "sha512-YQV9bUsemkzG81Ea295/nF/5GijnD2Af7QhEofh7xu+kvCN6RdodgNwwGWXB5GMI3NoyvQo0odNctoH/qLMIpg==" - }, - "@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", - "requires": { - "@types/unist": "*" - } - }, - "@types/history": { - "version": "4.7.11", - "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.11.tgz", - "integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==" - }, - "@types/html-minifier-terser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", - "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==" - }, - "@types/http-cache-semantics": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", - "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==" - }, - "@types/http-errors": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", - "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==" - }, - "@types/http-proxy": { - "version": "1.17.14", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.14.tgz", - "integrity": "sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w==", - "requires": { - "@types/node": "*" - } - }, - "@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==" - }, - "@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "requires": { - "@types/istanbul-lib-coverage": "*" - } - }, - "@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" - }, - "@types/mdast": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.3.tgz", - "integrity": "sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==", - "requires": { - "@types/unist": "*" - } - }, - "@types/mdx": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.10.tgz", - "integrity": "sha512-Rllzc5KHk0Al5/WANwgSPl1/CwjqCy+AZrGd78zuK+jO9aDM6ffblZ+zIjgPNAaEBmlO0RYDvLNh7wD0zKVgEg==" - }, - "@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==" - }, - "@types/ms": { - "version": "0.7.34", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", - "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==" - }, - "@types/node": { - "version": "20.9.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.0.tgz", - "integrity": "sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw==", - "requires": { - "undici-types": "~5.26.4" - } - }, - "@types/node-forge": { - "version": "1.3.9", - "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.9.tgz", - "integrity": "sha512-meK88cx/sTalPSLSoCzkiUB4VPIFHmxtXm5FaaqRDqBX2i/Sy8bJ4odsan0b20RBjPh06dAQ+OTTdnyQyhJZyQ==", - "requires": { - "@types/node": "*" - } - }, - "@types/parse-json": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", - "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==" - }, - "@types/prismjs": { - "version": "1.26.3", - "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.3.tgz", - "integrity": "sha512-A0D0aTXvjlqJ5ZILMz3rNfDBOx9hHxLZYv2by47Sm/pqW35zzjusrZTryatjN/Rf8Us2gZrJD+KeHbUSTux1Cw==" - }, - "@types/prop-types": { - "version": "15.7.10", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.10.tgz", - "integrity": "sha512-mxSnDQxPqsZxmeShFH+uwQ4kO4gcJcGahjjMFeLbKE95IAZiiZyiEepGZjtXJ7hN/yfu0bu9xN2ajcU0JcxX6A==" - }, - "@types/qs": { - "version": "6.9.10", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.10.tgz", - "integrity": "sha512-3Gnx08Ns1sEoCrWssEgTSJs/rsT2vhGP+Ja9cnnk9k4ALxinORlQneLXFeFKOTJMOeZUFD1s7w+w2AphTpvzZw==" - }, - "@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==" - }, - "@types/react": { - "version": "18.2.37", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.37.tgz", - "integrity": "sha512-RGAYMi2bhRgEXT3f4B92WTohopH6bIXw05FuGlmJEnv/omEn190+QYEIYxIAuIBdKgboYYdVved2p1AxZVQnaw==", - "requires": { - "@types/prop-types": "*", - "@types/scheduler": "*", - "csstype": "^3.0.2" - } - }, - "@types/react-router": { - "version": "5.1.20", - "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.20.tgz", - "integrity": "sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==", - "requires": { - "@types/history": "^4.7.11", - "@types/react": "*" - } - }, - "@types/react-router-config": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/@types/react-router-config/-/react-router-config-5.0.10.tgz", - "integrity": "sha512-Wn6c/tXdEgi9adCMtDwx8Q2vGty6TsPTc/wCQQ9kAlye8UqFxj0vGFWWuhywNfkwqth+SOgJxQTLTZukrqDQmQ==", - "requires": { - "@types/history": "^4.7.11", - "@types/react": "*", - "@types/react-router": "^5.1.0" - } - }, - "@types/react-router-dom": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.3.3.tgz", - "integrity": "sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==", - "requires": { - "@types/history": "^4.7.11", - "@types/react": "*", - "@types/react-router": "*" - } - }, - "@types/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" - }, - "@types/sax": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz", - "integrity": "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==", - "requires": { - "@types/node": "*" - } - }, - "@types/scheduler": { - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.6.tgz", - "integrity": "sha512-Vlktnchmkylvc9SnwwwozTv04L/e1NykF5vgoQ0XTmI8DD+wxfjQuHuvHS3p0r2jz2x2ghPs2h1FVeDirIteWA==" - }, - "@types/send": { - "version": "0.17.4", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", - "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", - "requires": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "@types/serve-index": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz", - "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", - "requires": { - "@types/express": "*" - } - }, - "@types/serve-static": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz", - "integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==", - "requires": { - "@types/http-errors": "*", - "@types/mime": "*", - "@types/node": "*" - } - }, - "@types/sockjs": { - "version": "0.3.36", - "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", - "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", - "requires": { - "@types/node": "*" - } - }, - "@types/unist": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", - "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==" - }, - "@types/ws": { - "version": "8.5.9", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.9.tgz", - "integrity": "sha512-jbdrY0a8lxfdTp/+r7Z4CkycbOFN8WX+IOchLJr3juT/xzbJ8URyTVSJ/hvNdadTgM1mnedb47n+Y31GsFnQlg==", - "requires": { - "@types/node": "*" - } - }, - "@types/yargs": { - "version": "17.0.32", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", - "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", - "requires": { - "@types/yargs-parser": "*" - } - }, - "@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==" - }, - "@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==" - }, - "@webassemblyjs/ast": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", - "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", - "requires": { - "@webassemblyjs/helper-numbers": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6" - } - }, - "@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", - "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==" - }, - "@webassemblyjs/helper-api-error": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", - "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==" - }, - "@webassemblyjs/helper-buffer": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", - "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==" - }, - "@webassemblyjs/helper-numbers": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", - "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", - "requires": { - "@webassemblyjs/floating-point-hex-parser": "1.11.6", - "@webassemblyjs/helper-api-error": "1.11.6", - "@xtuc/long": "4.2.2" - } - }, - "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", - "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==" - }, - "@webassemblyjs/helper-wasm-section": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", - "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", - "requires": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6" - } - }, - "@webassemblyjs/ieee754": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", - "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", - "requires": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "@webassemblyjs/leb128": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", - "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", - "requires": { - "@xtuc/long": "4.2.2" - } - }, - "@webassemblyjs/utf8": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", - "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==" - }, - "@webassemblyjs/wasm-edit": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", - "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", - "requires": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/helper-wasm-section": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6", - "@webassemblyjs/wasm-opt": "1.11.6", - "@webassemblyjs/wasm-parser": "1.11.6", - "@webassemblyjs/wast-printer": "1.11.6" - } - }, - "@webassemblyjs/wasm-gen": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", - "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", - "requires": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" - } - }, - "@webassemblyjs/wasm-opt": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", - "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", - "requires": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6", - "@webassemblyjs/wasm-parser": "1.11.6" - } - }, - "@webassemblyjs/wasm-parser": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", - "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", - "requires": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-api-error": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" - } - }, - "@webassemblyjs/wast-printer": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", - "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", - "requires": { - "@webassemblyjs/ast": "1.11.6", - "@xtuc/long": "4.2.2" - } - }, - "@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" - }, - "@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" - }, - "accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "requires": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "dependencies": { - "mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" - }, - "mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "requires": { - "mime-db": "1.52.0" - } - } - } - }, - "acorn": { - "version": "8.11.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", - "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==" - }, - "acorn-import-assertions": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", - "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", - "requires": {} - }, - "acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "requires": {} - }, - "acorn-walk": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.0.tgz", - "integrity": "sha512-FS7hV565M5l1R08MXqo8odwMTB02C2UqzB17RVgu9EyuYFBqJZ3/ZY97sQD5FewVu1UyDFc1yztUDrAwT0EypA==" - }, - "address": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/address/-/address-1.2.2.tgz", - "integrity": "sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==" - }, - "aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "requires": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - } - }, - "ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "requires": { - "ajv": "^8.0.0" - } - }, - "ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "requires": { - "fast-deep-equal": "^3.1.3" - } - }, - "algoliasearch": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.23.3.tgz", - "integrity": "sha512-Le/3YgNvjW9zxIQMRhUHuhiUjAlKY/zsdZpfq4dlLqg6mEm0nL6yk+7f2hDOtLpxsgE4jSzDmvHL7nXdBp5feg==", - "requires": { - "@algolia/cache-browser-local-storage": "4.23.3", - "@algolia/cache-common": "4.23.3", - "@algolia/cache-in-memory": "4.23.3", - "@algolia/client-account": "4.23.3", - "@algolia/client-analytics": "4.23.3", - "@algolia/client-common": "4.23.3", - "@algolia/client-personalization": "4.23.3", - "@algolia/client-search": "4.23.3", - "@algolia/logger-common": "4.23.3", - "@algolia/logger-console": "4.23.3", - "@algolia/recommend": "4.23.3", - "@algolia/requester-browser-xhr": "4.23.3", - "@algolia/requester-common": "4.23.3", - "@algolia/requester-node-http": "4.23.3", - "@algolia/transporter": "4.23.3" - } - }, - "algoliasearch-helper": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.21.0.tgz", - "integrity": "sha512-hjVOrL15I3Y3K8xG0icwG1/tWE+MocqBrhW6uVBWpU+/kVEMK0BnM2xdssj6mZM61eJ4iRxHR0djEI3ENOpR8w==", - "requires": { - "@algolia/events": "^4.0.1" - } - }, - "anser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/anser/-/anser-2.1.1.tgz", - "integrity": "sha512-nqLm4HxOTpeLOxcmB3QWmV5TcDFhW9y/fyQ+hivtDFcK4OQ+pQ5fzPnXHM1Mfcm0VkLtvVi1TCPr++Qy0Q/3EQ==" - }, - "ansi-align": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", - "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", - "requires": { - "string-width": "^4.1.0" - }, - "dependencies": { - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - } - } - }, - "ansi-html-community": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", - "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==" - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" - }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "array-flatten": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", - "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==" - }, - "astring": { - "version": "1.8.6", - "resolved": "https://registry.npmjs.org/astring/-/astring-1.8.6.tgz", - "integrity": "sha512-ISvCdHdlTDlH5IpxQJIex7BWBywFWgjJSVdwst+/iQCoEYnyOaQ95+X1JGshuBjGp6nxKUy1jMgE3zPqN7fQdg==" - }, - "at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" - }, - "autoprefixer": { - "version": "10.4.19", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz", - "integrity": "sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==", - "requires": { - "browserslist": "^4.23.0", - "caniuse-lite": "^1.0.30001599", - "fraction.js": "^4.3.7", - "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", - "postcss-value-parser": "^4.2.0" - } - }, - "babel-loader": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.1.3.tgz", - "integrity": "sha512-xG3ST4DglodGf8qSwv0MdeWLhrDsw/32QMdTO5T1ZIp9gQur0HkCyFs7Awskr10JKXFXwpAhiCuYX5oGXnRGbw==", - "requires": { - "find-cache-dir": "^4.0.0", - "schema-utils": "^4.0.0" - } - }, - "babel-plugin-dynamic-import-node": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", - "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", - "requires": { - "object.assign": "^4.1.0" - } - }, - "babel-plugin-polyfill-corejs2": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.6.tgz", - "integrity": "sha512-jhHiWVZIlnPbEUKSSNb9YoWcQGdlTLq7z1GHL4AjFxaoOUMuuEVJ+Y4pAaQUGOGk93YsVCKPbqbfw3m0SM6H8Q==", - "requires": { - "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.4.3", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" - } - } - }, - "babel-plugin-polyfill-corejs3": { - "version": "0.8.6", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.6.tgz", - "integrity": "sha512-leDIc4l4tUgU7str5BWLS2h8q2N4Nf6lGZP6UrNDxdtfF2g69eJ5L0H7S8A5Ln/arfFAfHor5InAdZuIOwZdgQ==", - "requires": { - "@babel/helper-define-polyfill-provider": "^0.4.3", - "core-js-compat": "^3.33.1" - } - }, - "babel-plugin-polyfill-regenerator": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.3.tgz", - "integrity": "sha512-8sHeDOmXC8csczMrYEOf0UTNa4yE2SxV5JGeT/LP1n0OYVDUUFPxG9vdk2AlDlIit4t+Kf0xCtpgXPBwnn/9pw==", - "requires": { - "@babel/helper-define-polyfill-provider": "^0.4.3" - } - }, - "bail": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", - "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==" - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" - }, - "batch": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==" - }, - "big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==" - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" - }, - "body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", - "requires": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.1", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "dependencies": { - "bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - } - } - }, - "bonjour-service": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.1.1.tgz", - "integrity": "sha512-Z/5lQRMOG9k7W+FkeGTNjh7htqn/2LMnfOvBZ8pynNZCM9MwkQkI3zeI4oz09uWdcgmgHugVvBqxGg4VQJ5PCg==", - "requires": { - "array-flatten": "^2.1.2", - "dns-equal": "^1.0.0", - "fast-deep-equal": "^3.1.3", - "multicast-dns": "^7.2.5" - } - }, - "boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" - }, - "boxen": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-6.2.1.tgz", - "integrity": "sha512-H4PEsJXfFI/Pt8sjDWbHlQPx4zL/bvSQjcilJmaulGt5mLDorHOHpmdXAJcBcmru7PhYSp/cDMWRko4ZUMFkSw==", - "requires": { - "ansi-align": "^3.0.1", - "camelcase": "^6.2.0", - "chalk": "^4.1.2", - "cli-boxes": "^3.0.0", - "string-width": "^5.0.1", - "type-fest": "^2.5.0", - "widest-line": "^4.0.1", - "wrap-ansi": "^8.0.1" - } - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "requires": { - "fill-range": "^7.0.1" - } - }, - "browserslist": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", - "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", - "requires": { - "caniuse-lite": "^1.0.30001587", - "electron-to-chromium": "^1.4.668", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" - } - }, - "buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "requires": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" - }, - "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==" - }, - "cacheable-lookup": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", - "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==" - }, - "cacheable-request": { - "version": "10.2.14", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz", - "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==", - "requires": { - "@types/http-cache-semantics": "^4.0.2", - "get-stream": "^6.0.1", - "http-cache-semantics": "^4.1.1", - "keyv": "^4.5.3", - "mimic-response": "^4.0.0", - "normalize-url": "^8.0.0", - "responselike": "^3.0.0" - }, - "dependencies": { - "normalize-url": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.0.tgz", - "integrity": "sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw==" - } - } - }, - "call-bind": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", - "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", - "requires": { - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.1", - "set-function-length": "^1.1.1" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" - }, - "camel-case": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", - "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", - "requires": { - "pascal-case": "^3.1.2", - "tslib": "^2.0.3" - } - }, - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==" - }, - "caniuse-api": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", - "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", - "requires": { - "browserslist": "^4.0.0", - "caniuse-lite": "^1.0.0", - "lodash.memoize": "^4.1.2", - "lodash.uniq": "^4.5.0" - } - }, - "caniuse-lite": { - "version": "1.0.30001632", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001632.tgz", - "integrity": "sha512-udx3o7yHJfUxMLkGohMlVHCvFvWmirKh9JAH/d7WOLPetlH+LTL5cocMZ0t7oZx/mdlOWXti97xLZWc8uURRHg==" - }, - "ccount": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", - "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==" - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==" - }, - "character-entities": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", - "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==" - }, - "character-entities-html4": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", - "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==" - }, - "character-entities-legacy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", - "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==" - }, - "character-reference-invalid": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", - "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==" - }, - "cheerio": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", - "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", - "requires": { - "cheerio-select": "^2.1.0", - "dom-serializer": "^2.0.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "htmlparser2": "^8.0.1", - "parse5": "^7.0.0", - "parse5-htmlparser2-tree-adapter": "^7.0.0" - } - }, - "cheerio-select": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", - "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", - "requires": { - "boolbase": "^1.0.0", - "css-select": "^5.1.0", - "css-what": "^6.1.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1" - } - }, - "chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - } - }, - "chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==" - }, - "ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==" - }, - "clean-css": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.2.tgz", - "integrity": "sha512-JVJbM+f3d3Q704rF4bqQ5UUyTtuJ0JRKNbTKVEeujCCBoMdkEi+V+e8oktO9qGQNSvHrFTM6JZRXrUvGR1czww==", - "requires": { - "source-map": "~0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } - } - }, - "clean-set": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/clean-set/-/clean-set-1.1.2.tgz", - "integrity": "sha512-cA8uCj0qSoG9e0kevyOWXwPaELRPVg5Pxp6WskLMwerx257Zfnh8Nl0JBH59d7wQzij2CK7qEfJQK3RjuKKIug==" - }, - "clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==" - }, - "cli-boxes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", - "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==" - }, - "cli-table3": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz", - "integrity": "sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==", - "requires": { - "@colors/colors": "1.5.0", - "string-width": "^4.2.0" - }, - "dependencies": { - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - } - } - }, - "clone-deep": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", - "requires": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - }, - "dependencies": { - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "requires": { - "isobject": "^3.0.1" - } - } - } - }, - "clsx": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz", - "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==" - }, - "codesandbox-import-util-types": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/codesandbox-import-util-types/-/codesandbox-import-util-types-2.2.3.tgz", - "integrity": "sha512-Qj00p60oNExthP2oR3vvXmUGjukij+rxJGuiaKM6tyUmSyimdZsqHI/TUvFFClAffk9s7hxGnQgWQ8KCce27qQ==" - }, - "collapse-white-space": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-2.1.0.tgz", - "integrity": "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==" - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "colord": { - "version": "2.9.3", - "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", - "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==" - }, - "colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==" - }, - "combine-promises": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/combine-promises/-/combine-promises-1.2.0.tgz", - "integrity": "sha512-VcQB1ziGD0NXrhKxiwyNbCDmRzs/OShMs2GqW2DlU2A/Sd0nQxE1oWDAE5O0ygSx5mgQOn9eIFh7yKPgFRVkPQ==" - }, - "comma-separated-tokens": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", - "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==" - }, - "commander": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", - "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==" - }, - "common-path-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", - "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==" - }, - "compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "requires": { - "mime-db": ">= 1.43.0 < 2" - }, - "dependencies": { - "mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" - } - } - }, - "compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", - "requires": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", - "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", - "vary": "~1.1.2" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - } - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "config-chain": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", - "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", - "requires": { - "ini": "^1.3.4", - "proto-list": "~1.2.1" - } - }, - "configstore": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-6.0.0.tgz", - "integrity": "sha512-cD31W1v3GqUlQvbBCGcXmd2Nj9SvLDOP1oQ0YFuLETufzSPaKp11rYBsSOm7rCsW3OnIRAFM3OxRhceaXNYHkA==", - "requires": { - "dot-prop": "^6.0.1", - "graceful-fs": "^4.2.6", - "unique-string": "^3.0.0", - "write-file-atomic": "^3.0.3", - "xdg-basedir": "^5.0.1" - } - }, - "connect-history-api-fallback": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", - "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==" - }, - "consola": { - "version": "2.15.3", - "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", - "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==" - }, - "content-disposition": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", - "integrity": "sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==" - }, - "content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" - }, - "convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" - }, - "cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" - }, - "copy-text-to-clipboard": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/copy-text-to-clipboard/-/copy-text-to-clipboard-3.2.0.tgz", - "integrity": "sha512-RnJFp1XR/LOBDckxTib5Qjr/PMfkatD0MUCQgdpqS8MdKiNUzBjAQBEN6oUy+jW7LI93BBG3DtMB2KOOKpGs2Q==" - }, - "copy-webpack-plugin": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz", - "integrity": "sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==", - "requires": { - "fast-glob": "^3.2.11", - "glob-parent": "^6.0.1", - "globby": "^13.1.1", - "normalize-path": "^3.0.0", - "schema-utils": "^4.0.0", - "serialize-javascript": "^6.0.0" - }, - "dependencies": { - "glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "requires": { - "is-glob": "^4.0.3" - } - }, - "globby": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", - "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", - "requires": { - "dir-glob": "^3.0.1", - "fast-glob": "^3.3.0", - "ignore": "^5.2.4", - "merge2": "^1.4.1", - "slash": "^4.0.0" - } - }, - "slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==" - } - } - }, - "core-js": { - "version": "3.33.2", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.33.2.tgz", - "integrity": "sha512-XeBzWI6QL3nJQiHmdzbAOiMYqjrb7hwU7A39Qhvd/POSa/t9E1AeZyEZx3fNvp/vtM8zXwhoL0FsiS0hD0pruQ==" - }, - "core-js-compat": { - "version": "3.33.2", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.33.2.tgz", - "integrity": "sha512-axfo+wxFVxnqf8RvxTzoAlzW4gRoacrHeoFlc9n0x50+7BEyZL/Rt3hicaED1/CEd7I6tPCPVUYcJwCMO5XUYw==", - "requires": { - "browserslist": "^4.22.1" - } - }, - "core-js-pure": { - "version": "3.33.2", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.33.2.tgz", - "integrity": "sha512-a8zeCdyVk7uF2elKIGz67AjcXOxjRbwOLz8SbklEso1V+2DoW4OkAMZN9S9GBgvZIaqQi/OemFX4OiSoQEmg1Q==" - }, - "core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" - }, - "cose-base": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz", - "integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==", - "requires": { - "layout-base": "^1.0.0" - } - }, - "cosmiconfig": { - "version": "8.3.6", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", - "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", - "requires": { - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0", - "path-type": "^4.0.0" - } - }, - "crelt": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", - "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==" - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "crypto-random-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz", - "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==", - "requires": { - "type-fest": "^1.0.1" - }, - "dependencies": { - "type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==" - } - } - }, - "css-declaration-sorter": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.2.0.tgz", - "integrity": "sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow==", - "requires": {} - }, - "css-loader": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.8.1.tgz", - "integrity": "sha512-xDAXtEVGlD0gJ07iclwWVkLoZOpEvAWaSyf6W18S2pOC//K8+qUDIx8IIT3D+HjnmkJPQeesOPv5aiUaJsCM2g==", - "requires": { - "icss-utils": "^5.1.0", - "postcss": "^8.4.21", - "postcss-modules-extract-imports": "^3.0.0", - "postcss-modules-local-by-default": "^4.0.3", - "postcss-modules-scope": "^3.0.0", - "postcss-modules-values": "^4.0.0", - "postcss-value-parser": "^4.2.0", - "semver": "^7.3.8" - } - }, - "css-minimizer-webpack-plugin": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-5.0.1.tgz", - "integrity": "sha512-3caImjKFQkS+ws1TGcFn0V1HyDJFq1Euy589JlD6/3rV2kj+w7r5G9WDMgSHvpvXHNZ2calVypZWuEDQd9wfLg==", - "requires": { - "@jridgewell/trace-mapping": "^0.3.18", - "cssnano": "^6.0.1", - "jest-worker": "^29.4.3", - "postcss": "^8.4.24", - "schema-utils": "^4.0.1", - "serialize-javascript": "^6.0.1" - } - }, - "css-select": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", - "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", - "requires": { - "boolbase": "^1.0.0", - "css-what": "^6.1.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "nth-check": "^2.0.1" - } - }, - "css-tree": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", - "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", - "requires": { - "mdn-data": "2.0.30", - "source-map-js": "^1.0.1" - } - }, - "css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==" - }, - "cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" - }, - "cssnano": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-6.1.2.tgz", - "integrity": "sha512-rYk5UeX7VAM/u0lNqewCdasdtPK81CgX8wJFLEIXHbV2oldWRgJAsZrdhRXkV1NJzA2g850KiFm9mMU2HxNxMA==", - "requires": { - "cssnano-preset-default": "^6.1.2", - "lilconfig": "^3.1.1" - } - }, - "cssnano-preset-advanced": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/cssnano-preset-advanced/-/cssnano-preset-advanced-6.1.2.tgz", - "integrity": "sha512-Nhao7eD8ph2DoHolEzQs5CfRpiEP0xa1HBdnFZ82kvqdmbwVBUr2r1QuQ4t1pi+D1ZpqpcO4T+wy/7RxzJ/WPQ==", - "requires": { - "autoprefixer": "^10.4.19", - "browserslist": "^4.23.0", - "cssnano-preset-default": "^6.1.2", - "postcss-discard-unused": "^6.0.5", - "postcss-merge-idents": "^6.0.3", - "postcss-reduce-idents": "^6.0.3", - "postcss-zindex": "^6.0.2" - } - }, - "cssnano-preset-default": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-6.1.2.tgz", - "integrity": "sha512-1C0C+eNaeN8OcHQa193aRgYexyJtU8XwbdieEjClw+J9d94E41LwT6ivKH0WT+fYwYWB0Zp3I3IZ7tI/BbUbrg==", - "requires": { - "browserslist": "^4.23.0", - "css-declaration-sorter": "^7.2.0", - "cssnano-utils": "^4.0.2", - "postcss-calc": "^9.0.1", - "postcss-colormin": "^6.1.0", - "postcss-convert-values": "^6.1.0", - "postcss-discard-comments": "^6.0.2", - "postcss-discard-duplicates": "^6.0.3", - "postcss-discard-empty": "^6.0.3", - "postcss-discard-overridden": "^6.0.2", - "postcss-merge-longhand": "^6.0.5", - "postcss-merge-rules": "^6.1.1", - "postcss-minify-font-values": "^6.1.0", - "postcss-minify-gradients": "^6.0.3", - "postcss-minify-params": "^6.1.0", - "postcss-minify-selectors": "^6.0.4", - "postcss-normalize-charset": "^6.0.2", - "postcss-normalize-display-values": "^6.0.2", - "postcss-normalize-positions": "^6.0.2", - "postcss-normalize-repeat-style": "^6.0.2", - "postcss-normalize-string": "^6.0.2", - "postcss-normalize-timing-functions": "^6.0.2", - "postcss-normalize-unicode": "^6.1.0", - "postcss-normalize-url": "^6.0.2", - "postcss-normalize-whitespace": "^6.0.2", - "postcss-ordered-values": "^6.0.2", - "postcss-reduce-initial": "^6.1.0", - "postcss-reduce-transforms": "^6.0.2", - "postcss-svgo": "^6.0.3", - "postcss-unique-selectors": "^6.0.4" - } - }, - "cssnano-utils": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-4.0.2.tgz", - "integrity": "sha512-ZR1jHg+wZ8o4c3zqf1SIUSTIvm/9mU343FMR6Obe/unskbvpGhZOo1J6d/r8D1pzkRQYuwbcH3hToOuoA2G7oQ==", - "requires": {} - }, - "csso": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", - "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", - "requires": { - "css-tree": "~2.2.0" - }, - "dependencies": { - "css-tree": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", - "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", - "requires": { - "mdn-data": "2.0.28", - "source-map-js": "^1.0.1" - } - }, - "mdn-data": { - "version": "2.0.28", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", - "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==" - } - } - }, - "csstype": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", - "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" - }, - "cytoscape": { - "version": "3.27.0", - "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.27.0.tgz", - "integrity": "sha512-pPZJilfX9BxESwujODz5pydeGi+FBrXq1rcaB1mfhFXXFJ9GjE6CNndAk+8jPzoXGD+16LtSS4xlYEIUiW4Abg==", - "requires": { - "heap": "^0.2.6", - "lodash": "^4.17.21" - } - }, - "cytoscape-cose-bilkent": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz", - "integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==", - "requires": { - "cose-base": "^1.0.0" - } - }, - "cytoscape-fcose": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz", - "integrity": "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==", - "requires": { - "cose-base": "^2.2.0" - }, - "dependencies": { - "cose-base": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-2.2.0.tgz", - "integrity": "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==", - "requires": { - "layout-base": "^2.0.0" - } - }, - "layout-base": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-2.0.1.tgz", - "integrity": "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==" - } - } - }, - "d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "requires": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, - "d3": { - "version": "7.8.5", - "resolved": "https://registry.npmjs.org/d3/-/d3-7.8.5.tgz", - "integrity": "sha512-JgoahDG51ncUfJu6wX/1vWQEqOflgXyl4MaHqlcSruTez7yhaRKR9i8VjjcQGeS2en/jnFivXuaIMnseMMt0XA==", - "requires": { - "d3-array": "3", - "d3-axis": "3", - "d3-brush": "3", - "d3-chord": "3", - "d3-color": "3", - "d3-contour": "4", - "d3-delaunay": "6", - "d3-dispatch": "3", - "d3-drag": "3", - "d3-dsv": "3", - "d3-ease": "3", - "d3-fetch": "3", - "d3-force": "3", - "d3-format": "3", - "d3-geo": "3", - "d3-hierarchy": "3", - "d3-interpolate": "3", - "d3-path": "3", - "d3-polygon": "3", - "d3-quadtree": "3", - "d3-random": "3", - "d3-scale": "4", - "d3-scale-chromatic": "3", - "d3-selection": "3", - "d3-shape": "3", - "d3-time": "3", - "d3-time-format": "4", - "d3-timer": "3", - "d3-transition": "3", - "d3-zoom": "3" - } - }, - "d3-array": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", - "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", - "requires": { - "internmap": "1 - 2" - } - }, - "d3-axis": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", - "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==" - }, - "d3-brush": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", - "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", - "requires": { - "d3-dispatch": "1 - 3", - "d3-drag": "2 - 3", - "d3-interpolate": "1 - 3", - "d3-selection": "3", - "d3-transition": "3" - } - }, - "d3-chord": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", - "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", - "requires": { - "d3-path": "1 - 3" - } - }, - "d3-color": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", - "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==" - }, - "d3-contour": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", - "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", - "requires": { - "d3-array": "^3.2.0" - } - }, - "d3-delaunay": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", - "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", - "requires": { - "delaunator": "5" - } - }, - "d3-dispatch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", - "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==" - }, - "d3-drag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", - "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", - "requires": { - "d3-dispatch": "1 - 3", - "d3-selection": "3" - } - }, - "d3-dsv": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", - "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", - "requires": { - "commander": "7", - "iconv-lite": "0.6", - "rw": "1" - }, - "dependencies": { - "commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==" - }, - "iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - } - } - } - }, - "d3-ease": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", - "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==" - }, - "d3-fetch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", - "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", - "requires": { - "d3-dsv": "1 - 3" - } - }, - "d3-force": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", - "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", - "requires": { - "d3-dispatch": "1 - 3", - "d3-quadtree": "1 - 3", - "d3-timer": "1 - 3" - } - }, - "d3-format": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", - "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==" - }, - "d3-geo": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.0.tgz", - "integrity": "sha512-JEo5HxXDdDYXCaWdwLRt79y7giK8SbhZJbFWXqbRTolCHFI5jRqteLzCsq51NKbUoX0PjBVSohxrx+NoOUujYA==", - "requires": { - "d3-array": "2.5.0 - 3" - } - }, - "d3-hierarchy": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", - "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==" - }, - "d3-interpolate": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", - "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", - "requires": { - "d3-color": "1 - 3" - } - }, - "d3-path": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", - "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==" - }, - "d3-polygon": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", - "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==" - }, - "d3-quadtree": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", - "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==" - }, - "d3-random": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", - "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==" - }, - "d3-sankey": { - "version": "0.12.3", - "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz", - "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==", - "requires": { - "d3-array": "1 - 2", - "d3-shape": "^1.2.0" - }, - "dependencies": { - "d3-array": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", - "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", - "requires": { - "internmap": "^1.0.0" - } - }, - "d3-path": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", - "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==" - }, - "d3-shape": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", - "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", - "requires": { - "d3-path": "1" - } - }, - "internmap": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", - "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==" - } - } - }, - "d3-scale": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", - "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", - "requires": { - "d3-array": "2.10.0 - 3", - "d3-format": "1 - 3", - "d3-interpolate": "1.2.0 - 3", - "d3-time": "2.1.1 - 3", - "d3-time-format": "2 - 4" - } - }, - "d3-scale-chromatic": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz", - "integrity": "sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==", - "requires": { - "d3-color": "1 - 3", - "d3-interpolate": "1 - 3" - } - }, - "d3-selection": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", - "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==" - }, - "d3-shape": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", - "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", - "requires": { - "d3-path": "^3.1.0" - } - }, - "d3-time": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", - "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", - "requires": { - "d3-array": "2 - 3" - } - }, - "d3-time-format": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", - "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", - "requires": { - "d3-time": "1 - 3" - } - }, - "d3-timer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", - "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==" - }, - "d3-transition": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", - "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", - "requires": { - "d3-color": "1 - 3", - "d3-dispatch": "1 - 3", - "d3-ease": "1 - 3", - "d3-interpolate": "1 - 3", - "d3-timer": "1 - 3" - } - }, - "d3-zoom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", - "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", - "requires": { - "d3-dispatch": "1 - 3", - "d3-drag": "2 - 3", - "d3-interpolate": "1 - 3", - "d3-selection": "2 - 3", - "d3-transition": "2 - 3" - } - }, - "dagre-d3-es": { - "version": "7.0.10", - "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.10.tgz", - "integrity": "sha512-qTCQmEhcynucuaZgY5/+ti3X/rnszKZhEQH/ZdWdtP1tA/y3VoHJzcVrO9pjjJCNpigfscAtoUB5ONcd2wNn0A==", - "requires": { - "d3": "^7.8.2", - "lodash-es": "^4.17.21" - } - }, - "dayjs": { - "version": "1.11.10", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", - "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==" - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "requires": { - "ms": "2.1.2" - } - }, - "decode-named-character-reference": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", - "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", - "requires": { - "character-entities": "^2.0.0" - } - }, - "decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "requires": { - "mimic-response": "^3.1.0" - }, - "dependencies": { - "mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==" - } - } - }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" - }, - "deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==" - }, - "default-gateway": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", - "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", - "requires": { - "execa": "^5.0.0" - } - }, - "defer-to-connect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", - "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==" - }, - "define-data-property": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", - "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", - "requires": { - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" - } - }, - "define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==" - }, - "define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "requires": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - } - }, - "del": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz", - "integrity": "sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==", - "requires": { - "globby": "^11.0.1", - "graceful-fs": "^4.2.4", - "is-glob": "^4.0.1", - "is-path-cwd": "^2.2.0", - "is-path-inside": "^3.0.2", - "p-map": "^4.0.0", - "rimraf": "^3.0.2", - "slash": "^3.0.0" - } - }, - "delaunator": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.0.tgz", - "integrity": "sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==", - "requires": { - "robust-predicates": "^3.0.0" - } - }, - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" - }, - "dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==" - }, - "destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" - }, - "detect-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", - "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==" - }, - "detect-port": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.5.1.tgz", - "integrity": "sha512-aBzdj76lueB6uUst5iAs7+0H/oOjqI5D16XUWxlWMIMROhcM0rfsNVk93zTngq1dDNpoXRr++Sus7ETAExppAQ==", - "requires": { - "address": "^1.0.1", - "debug": "4" - } - }, - "detect-port-alt": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz", - "integrity": "sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==", - "requires": { - "address": "^1.0.1", - "debug": "^2.6.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - } - } - }, - "devlop": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", - "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", - "requires": { - "dequal": "^2.0.0" - } - }, - "diff": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", - "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==" - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "requires": { - "path-type": "^4.0.0" - } - }, - "dns-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", - "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==" - }, - "dns-packet": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", - "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", - "requires": { - "@leichtgewicht/ip-codec": "^2.0.1" - } - }, - "dom-converter": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", - "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", - "requires": { - "utila": "~0.4" - } - }, - "dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "requires": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - } - }, - "domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==" - }, - "domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "requires": { - "domelementtype": "^2.3.0" - } - }, - "dompurify": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.6.tgz", - "integrity": "sha512-ilkD8YEnnGh1zJ240uJsW7AzE+2qpbOUYjacomn3AvJ6J4JhKGSZ2nh4wUIXPZrEPppaCLx5jFe8T89Rk8tQ7w==" - }, - "domutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", - "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", - "requires": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - } - }, - "dot-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", - "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", - "requires": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "dot-prop": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", - "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", - "requires": { - "is-obj": "^2.0.0" - }, - "dependencies": { - "is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==" - } - } - }, - "dotenv": { - "version": "16.3.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", - "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==" - }, - "duplexer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" - }, - "eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" - }, - "electron-to-chromium": { - "version": "1.4.699", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.699.tgz", - "integrity": "sha512-I7q3BbQi6e4tJJN5CRcyvxhK0iJb34TV8eJQcgh+fR2fQ8miMgZcEInckCo1U9exDHbfz7DLDnFn8oqH/VcRKw==" - }, - "elkjs": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/elkjs/-/elkjs-0.8.2.tgz", - "integrity": "sha512-L6uRgvZTH+4OF5NE/MBbzQx/WYpru1xCBE9respNj6qznEewGUIfhzmm7horWWxbNO2M0WckQypGctR8lH79xQ==" - }, - "emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" - }, - "emojilib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/emojilib/-/emojilib-2.4.0.tgz", - "integrity": "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==" - }, - "emojis-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==" - }, - "emoticon": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/emoticon/-/emoticon-4.0.1.tgz", - "integrity": "sha512-dqx7eA9YaqyvYtUhJwT4rC1HIp82j5ybS1/vQ42ur+jBe17dJMwZE4+gvL1XadSFfxaPFFGt3Xsw+Y8akThDlw==" - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" - }, - "enhanced-resolve": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", - "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", - "requires": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - } - }, - "entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==" - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "es-module-lexer": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.1.tgz", - "integrity": "sha512-JUFAyicQV9mXc3YRxPnDlrfBKpqt6hUYzz9/boprUJHs4e4KVr3XwOF70doO6gwXUor6EWZJAyWAfKki84t20Q==" - }, - "es5-ext": { - "version": "0.10.62", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", - "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", - "requires": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "next-tick": "^1.1.0" - } - }, - "es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", - "requires": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", - "requires": { - "d": "^1.0.1", - "ext": "^1.1.2" - } - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" - }, - "escape-carriage": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/escape-carriage/-/escape-carriage-1.3.1.tgz", - "integrity": "sha512-GwBr6yViW3ttx1kb7/Oh+gKQ1/TrhYwxKqVmg5gS+BK+Qe2KrOa/Vh7w3HPBvgGf0LfcDGoY9I6NHKoA5Hozhw==" - }, - "escape-goat": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-4.0.0.tgz", - "integrity": "sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==" - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" - }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "requires": { - "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" - } - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" - }, - "estree-util-attach-comments": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/estree-util-attach-comments/-/estree-util-attach-comments-3.0.0.tgz", - "integrity": "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==", - "requires": { - "@types/estree": "^1.0.0" - } - }, - "estree-util-build-jsx": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/estree-util-build-jsx/-/estree-util-build-jsx-3.0.1.tgz", - "integrity": "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==", - "requires": { - "@types/estree-jsx": "^1.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "estree-walker": "^3.0.0" - } - }, - "estree-util-is-identifier-name": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", - "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==" - }, - "estree-util-to-js": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/estree-util-to-js/-/estree-util-to-js-2.0.0.tgz", - "integrity": "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==", - "requires": { - "@types/estree-jsx": "^1.0.0", - "astring": "^1.8.0", - "source-map": "^0.7.0" - } - }, - "estree-util-value-to-estree": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/estree-util-value-to-estree/-/estree-util-value-to-estree-3.1.1.tgz", - "integrity": "sha512-5mvUrF2suuv5f5cGDnDphIy4/gW86z82kl5qG6mM9z04SEQI4FB5Apmaw/TGEf3l55nLtMs5s51dmhUzvAHQCA==", - "requires": { - "@types/estree": "^1.0.0", - "is-plain-obj": "^4.0.0" - } - }, - "estree-util-visit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/estree-util-visit/-/estree-util-visit-2.0.0.tgz", - "integrity": "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==", - "requires": { - "@types/estree-jsx": "^1.0.0", - "@types/unist": "^3.0.0" - } - }, - "estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "requires": { - "@types/estree": "^1.0.0" - } - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" - }, - "eta": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/eta/-/eta-2.2.0.tgz", - "integrity": "sha512-UVQ72Rqjy/ZKQalzV5dCCJP80GrmPrMxh6NlNf+erV6ObL0ZFkhCstWRawS85z3smdr3d2wXPsZEY7rDPfGd2g==" - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" - }, - "eval": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/eval/-/eval-0.1.8.tgz", - "integrity": "sha512-EzV94NYKoO09GLXGjXj9JIlXijVck4ONSr5wiCWDvhsvj5jxSrzTmRU/9C1DyB6uToszLs8aifA6NQ7lEQdvFw==", - "requires": { - "@types/node": "*", - "require-like": ">= 0.1.1" - } - }, - "eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" - }, - "events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" - }, - "execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - } - }, - "express": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", - "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", - "requires": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.1", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.5.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.11.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "dependencies": { - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" - }, - "content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "requires": { - "safe-buffer": "5.2.1" - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" - }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" - } - } - }, - "ext": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", - "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", - "requires": { - "type": "^2.7.2" - }, - "dependencies": { - "type": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", - "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" - } - } - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "requires": { - "is-extendable": "^0.1.0" - } - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" - }, - "fast-url-parser": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz", - "integrity": "sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ==", - "requires": { - "punycode": "^1.3.2" - } - }, - "fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", - "requires": { - "reusify": "^1.0.4" - } - }, - "fault": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fault/-/fault-2.0.1.tgz", - "integrity": "sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==", - "requires": { - "format": "^0.2.0" - } - }, - "faye-websocket": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", - "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", - "requires": { - "websocket-driver": ">=0.5.1" - } - }, - "feed": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/feed/-/feed-4.2.2.tgz", - "integrity": "sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ==", - "requires": { - "xml-js": "^1.6.11" - } - }, - "file-loader": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", - "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", - "requires": { - "loader-utils": "^2.0.0", - "schema-utils": "^3.0.0" - }, - "dependencies": { - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "requires": {} - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "requires": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - } - } - } - }, - "filesize": { - "version": "8.0.7", - "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", - "integrity": "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==" - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - } - } - }, - "find-cache-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-4.0.0.tgz", - "integrity": "sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==", - "requires": { - "common-path-prefix": "^3.0.0", - "pkg-dir": "^7.0.0" - } - }, - "find-up": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", - "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", - "requires": { - "locate-path": "^7.1.0", - "path-exists": "^5.0.0" - } - }, - "flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==" - }, - "follow-redirects": { - "version": "1.15.3", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", - "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==" - }, - "fork-ts-checker-webpack-plugin": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.3.tgz", - "integrity": "sha512-SbH/l9ikmMWycd5puHJKTkZJKddF4iRLyW3DeZ08HTI7NGyLS38MXd/KGgeWumQO7YNQbW2u/NtPT2YowbPaGQ==", - "requires": { - "@babel/code-frame": "^7.8.3", - "@types/json-schema": "^7.0.5", - "chalk": "^4.1.0", - "chokidar": "^3.4.2", - "cosmiconfig": "^6.0.0", - "deepmerge": "^4.2.2", - "fs-extra": "^9.0.0", - "glob": "^7.1.6", - "memfs": "^3.1.2", - "minimatch": "^3.0.4", - "schema-utils": "2.7.0", - "semver": "^7.3.2", - "tapable": "^1.0.0" - }, - "dependencies": { - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "requires": {} - }, - "cosmiconfig": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", - "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", - "requires": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.1.0", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.7.2" - } - }, - "fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "schema-utils": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", - "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", - "requires": { - "@types/json-schema": "^7.0.4", - "ajv": "^6.12.2", - "ajv-keywords": "^3.4.1" - } - }, - "tapable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", - "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==" - } - } - }, - "form-data-encoder": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", - "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==" - }, - "format": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", - "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==" - }, - "forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" - }, - "fraction.js": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", - "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==" - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" - }, - "fs-extra": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", - "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, - "fs-monkey": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.5.tgz", - "integrity": "sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew==" - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, - "fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "optional": true - }, - "function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" - }, - "gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==" - }, - "get-intrinsic": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", - "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", - "requires": { - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - } - }, - "get-own-enumerable-property-symbols": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", - "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==" - }, - "get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==" - }, - "github-slugger": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.5.0.tgz", - "integrity": "sha512-wIh+gKBI9Nshz2o46B0B3f5k/W+WI9ZAv6y5Dn5WJ5SK1t0TnDimB4WE5rmTD05ZAIn8HALCZVmCsvj0w0v0lw==" - }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "requires": { - "is-glob": "^4.0.1" - } - }, - "glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" - }, - "global-dirs": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", - "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==", - "requires": { - "ini": "2.0.0" - }, - "dependencies": { - "ini": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", - "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==" - } - } - }, - "global-modules": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", - "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", - "requires": { - "global-prefix": "^3.0.0" - } - }, - "global-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", - "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", - "requires": { - "ini": "^1.3.5", - "kind-of": "^6.0.2", - "which": "^1.3.1" - }, - "dependencies": { - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" - }, - "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - } - }, - "gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "requires": { - "get-intrinsic": "^1.1.3" - } - }, - "got": { - "version": "12.6.1", - "resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz", - "integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==", - "requires": { - "@sindresorhus/is": "^5.2.0", - "@szmarczak/http-timer": "^5.0.1", - "cacheable-lookup": "^7.0.0", - "cacheable-request": "^10.2.8", - "decompress-response": "^6.0.0", - "form-data-encoder": "^2.1.2", - "get-stream": "^6.0.1", - "http2-wrapper": "^2.1.10", - "lowercase-keys": "^3.0.0", - "p-cancelable": "^3.0.0", - "responselike": "^3.0.0" - }, - "dependencies": { - "@sindresorhus/is": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", - "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==" - } - } - }, - "graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" - }, - "gray-matter": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", - "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==", - "requires": { - "js-yaml": "^3.13.1", - "kind-of": "^6.0.2", - "section-matter": "^1.0.0", - "strip-bom-string": "^1.0.0" - }, - "dependencies": { - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - } - } - }, - "gzip-size": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", - "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", - "requires": { - "duplexer": "^0.1.2" - } - }, - "handle-thing": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", - "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "has-property-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", - "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", - "requires": { - "get-intrinsic": "^1.2.2" - } - }, - "has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==" - }, - "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" - }, - "has-yarn": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-3.0.0.tgz", - "integrity": "sha512-IrsVwUHhEULx3R8f/aA8AHuEzAorplsab/v8HBzEiIukwq5i/EC+xmOW+HfP1OaDP+2JkgT1yILHN2O3UFIbcA==" - }, - "hasown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", - "requires": { - "function-bind": "^1.1.2" - } - }, - "hast-util-from-parse5": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.1.tgz", - "integrity": "sha512-Er/Iixbc7IEa7r/XLtuG52zoqn/b3Xng/w6aZQ0xGVxzhw5xUFxcRqdPzP6yFi/4HBYRaifaI5fQ1RH8n0ZeOQ==", - "requires": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "devlop": "^1.0.0", - "hastscript": "^8.0.0", - "property-information": "^6.0.0", - "vfile": "^6.0.0", - "vfile-location": "^5.0.0", - "web-namespaces": "^2.0.0" - } - }, - "hast-util-parse-selector": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", - "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", - "requires": { - "@types/hast": "^3.0.0" - } - }, - "hast-util-raw": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.0.3.tgz", - "integrity": "sha512-ICWvVOF2fq4+7CMmtCPD5CM4QKjPbHpPotE6+8tDooV0ZuyJVUzHsrNX+O5NaRbieTf0F7FfeBOMAwi6Td0+yQ==", - "requires": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "@ungap/structured-clone": "^1.0.0", - "hast-util-from-parse5": "^8.0.0", - "hast-util-to-parse5": "^8.0.0", - "html-void-elements": "^3.0.0", - "mdast-util-to-hast": "^13.0.0", - "parse5": "^7.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0", - "web-namespaces": "^2.0.0", - "zwitch": "^2.0.0" - } - }, - "hast-util-to-estree": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/hast-util-to-estree/-/hast-util-to-estree-3.1.0.tgz", - "integrity": "sha512-lfX5g6hqVh9kjS/B9E2gSkvHH4SZNiQFiqWS0x9fENzEl+8W12RqdRxX6d/Cwxi30tPQs3bIO+aolQJNp1bIyw==", - "requires": { - "@types/estree": "^1.0.0", - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "devlop": "^1.0.0", - "estree-util-attach-comments": "^3.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "hast-util-whitespace": "^3.0.0", - "mdast-util-mdx-expression": "^2.0.0", - "mdast-util-mdx-jsx": "^3.0.0", - "mdast-util-mdxjs-esm": "^2.0.0", - "property-information": "^6.0.0", - "space-separated-tokens": "^2.0.0", - "style-to-object": "^0.4.0", - "unist-util-position": "^5.0.0", - "zwitch": "^2.0.0" - } - }, - "hast-util-to-jsx-runtime": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.0.tgz", - "integrity": "sha512-H/y0+IWPdsLLS738P8tDnrQ8Z+dj12zQQ6WC11TIM21C8WFVoIxcqWXf2H3hiTVZjF1AWqoimGwrTWecWrnmRQ==", - "requires": { - "@types/estree": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "hast-util-whitespace": "^3.0.0", - "mdast-util-mdx-expression": "^2.0.0", - "mdast-util-mdx-jsx": "^3.0.0", - "mdast-util-mdxjs-esm": "^2.0.0", - "property-information": "^6.0.0", - "space-separated-tokens": "^2.0.0", - "style-to-object": "^1.0.0", - "unist-util-position": "^5.0.0", - "vfile-message": "^4.0.0" - }, - "dependencies": { - "inline-style-parser": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.2.tgz", - "integrity": "sha512-EcKzdTHVe8wFVOGEYXiW9WmJXPjqi1T+234YpJr98RiFYKHV3cdy1+3mkTE+KHTHxFFLH51SfaGOoUdW+v7ViQ==" - }, - "style-to-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.5.tgz", - "integrity": "sha512-rDRwHtoDD3UMMrmZ6BzOW0naTjMsVZLIjsGleSKS/0Oz+cgCfAPRspaqJuE8rDzpKha/nEvnM0IF4seEAZUTKQ==", - "requires": { - "inline-style-parser": "0.2.2" - } - } - } - }, - "hast-util-to-parse5": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.0.tgz", - "integrity": "sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==", - "requires": { - "@types/hast": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "devlop": "^1.0.0", - "property-information": "^6.0.0", - "space-separated-tokens": "^2.0.0", - "web-namespaces": "^2.0.0", - "zwitch": "^2.0.0" - } - }, - "hast-util-whitespace": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", - "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", - "requires": { - "@types/hast": "^3.0.0" - } - }, - "hastscript": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-8.0.0.tgz", - "integrity": "sha512-dMOtzCEd3ABUeSIISmrETiKuyydk1w0pa+gE/uormcTpSYuaNJPbX1NU3JLyscSLjwAQM8bWMhhIlnCqnRvDTw==", - "requires": { - "@types/hast": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "hast-util-parse-selector": "^4.0.0", - "property-information": "^6.0.0", - "space-separated-tokens": "^2.0.0" - } - }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" - }, - "heap": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.7.tgz", - "integrity": "sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==" - }, - "history": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", - "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==", - "requires": { - "@babel/runtime": "^7.1.2", - "loose-envify": "^1.2.0", - "resolve-pathname": "^3.0.0", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0", - "value-equal": "^1.0.1" - } - }, - "hoist-non-react-statics": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", - "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "requires": { - "react-is": "^16.7.0" - }, - "dependencies": { - "react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - } - } - }, - "hpack.js": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", - "requires": { - "inherits": "^2.0.1", - "obuf": "^1.0.0", - "readable-stream": "^2.0.1", - "wbuf": "^1.1.0" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "html-entities": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.4.0.tgz", - "integrity": "sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ==" - }, - "html-minifier-terser": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-7.2.0.tgz", - "integrity": "sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA==", - "requires": { - "camel-case": "^4.1.2", - "clean-css": "~5.3.2", - "commander": "^10.0.0", - "entities": "^4.4.0", - "param-case": "^3.0.4", - "relateurl": "^0.2.7", - "terser": "^5.15.1" - }, - "dependencies": { - "commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==" - } - } - }, - "html-tags": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz", - "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==" - }, - "html-void-elements": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", - "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==" - }, - "html-webpack-plugin": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.3.tgz", - "integrity": "sha512-6YrDKTuqaP/TquFH7h4srYWsZx+x6k6+FbsTm0ziCwGHDP78Unr1r9F/H4+sGmMbX08GQcJ+K64x55b+7VM/jg==", - "requires": { - "@types/html-minifier-terser": "^6.0.0", - "html-minifier-terser": "^6.0.2", - "lodash": "^4.17.21", - "pretty-error": "^4.0.0", - "tapable": "^2.0.0" - }, - "dependencies": { - "commander": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==" - }, - "html-minifier-terser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", - "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", - "requires": { - "camel-case": "^4.1.2", - "clean-css": "^5.2.2", - "commander": "^8.3.0", - "he": "^1.2.0", - "param-case": "^3.0.4", - "relateurl": "^0.2.7", - "terser": "^5.10.0" - } - } - } - }, - "htmlparser2": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", - "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", - "requires": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "entities": "^4.4.0" - } - }, - "http-cache-semantics": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==" - }, - "http-deceiver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==" - }, - "http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "requires": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - } - }, - "http-parser-js": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", - "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==" - }, - "http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", - "requires": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - } - }, - "http-proxy-middleware": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", - "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", - "requires": { - "@types/http-proxy": "^1.17.8", - "http-proxy": "^1.18.1", - "is-glob": "^4.0.1", - "is-plain-obj": "^3.0.0", - "micromatch": "^4.0.2" - }, - "dependencies": { - "is-plain-obj": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", - "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==" - } - } - }, - "http2-wrapper": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.0.tgz", - "integrity": "sha512-kZB0wxMo0sh1PehyjJUWRFEd99KC5TLjZ2cULC4f9iqJBAmKQQXEICjxl5iPJRwP40dpeHFqqhm7tYCvODpqpQ==", - "requires": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.2.0" - } - }, - "human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==" - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "icss-utils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", - "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "requires": {} - }, - "ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" - }, - "ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==" - }, - "image-size": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.1.1.tgz", - "integrity": "sha512-541xKlUw6jr/6gGuk92F+mYM5zaFAc5ahphvkqvNe2bQ6gVBkd6bfrmVJ2t4KDAfikAYZyIqTnktX3i6/aQDrQ==", - "requires": { - "queue": "6.0.2" - } - }, - "immer": { - "version": "9.0.21", - "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", - "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==" - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "import-lazy": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", - "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==" - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==" - }, - "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==" - }, - "infima": { - "version": "0.2.0-alpha.43", - "resolved": "https://registry.npmjs.org/infima/-/infima-0.2.0-alpha.43.tgz", - "integrity": "sha512-2uw57LvUqW0rK/SWYnd/2rRfxNA5DDNOh33jxF7fy46VWoNhGxiUQyVZHbBMjQ33mQem0cjdDVwgWVAmlRfgyQ==" - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" - }, - "inline-style-parser": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", - "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==" - }, - "internmap": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", - "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==" - }, - "interpret": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==" - }, - "intersection-observer": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/intersection-observer/-/intersection-observer-0.10.0.tgz", - "integrity": "sha512-fn4bQ0Xq8FTej09YC/jqKZwtijpvARlRp6wxL5WTA6yPe2YWSJ5RJh7Nm79rK2qB0wr6iDQzH60XGq5V/7u8YQ==" - }, - "invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "requires": { - "loose-envify": "^1.0.0" - } - }, - "ipaddr.js": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.1.0.tgz", - "integrity": "sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ==" - }, - "is-alphabetical": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", - "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==" - }, - "is-alphanumerical": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", - "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", - "requires": { - "is-alphabetical": "^2.0.0", - "is-decimal": "^2.0.0" - } - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-ci": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", - "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", - "requires": { - "ci-info": "^3.2.0" - } - }, - "is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", - "requires": { - "hasown": "^2.0.0" - } - }, - "is-decimal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", - "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==" - }, - "is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==" - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==" - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-hexadecimal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", - "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==" - }, - "is-installed-globally": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", - "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", - "requires": { - "global-dirs": "^3.0.0", - "is-path-inside": "^3.0.2" - } - }, - "is-npm": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-6.0.0.tgz", - "integrity": "sha512-JEjxbSmtPSt1c8XTkVrlujcXdKV1/tvuQ7GwKcAlyiVLeYFQ2VHat8xfrDJsIkhCdF/tZ7CiIR3sy141c6+gPQ==" - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" - }, - "is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==" - }, - "is-path-cwd": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", - "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==" - }, - "is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==" - }, - "is-plain-obj": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", - "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==" - }, - "is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==" - }, - "is-reference": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz", - "integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==", - "requires": { - "@types/estree": "*" - } - }, - "is-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", - "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==" - }, - "is-root": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", - "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==" - }, - "is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" - }, - "is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "requires": { - "is-docker": "^2.0.0" - } - }, - "is-yarn-global": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.4.1.tgz", - "integrity": "sha512-/kppl+R+LO5VmhYSEWARUFjodS25D68gvj8W7z0I7OWhUla5xWu8KL6CtB2V0R6yqhnRgbcaREMr4EEM6htLPQ==" - }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==" - }, - "jest-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "requires": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - }, - "jest-worker": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", - "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "requires": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "dependencies": { - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jiti": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", - "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==" - }, - "joi": { - "version": "17.11.0", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.11.0.tgz", - "integrity": "sha512-NgB+lZLNoqISVy1rZocE9PZI36bL/77ie924Ri43yEvi9GUUMPeyVIr8KdFTMUlby1p0PBYMk9spIxEUQYqrJQ==", - "requires": { - "@hapi/hoek": "^9.0.0", - "@hapi/topo": "^5.0.0", - "@sideway/address": "^4.1.3", - "@sideway/formula": "^3.0.1", - "@sideway/pinpoint": "^2.0.0" - } - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "requires": { - "argparse": "^2.0.1" - } - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" - }, - "json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" - }, - "json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==" - }, - "jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" - } - }, - "keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "requires": { - "json-buffer": "3.0.1" - } - }, - "khroma": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz", - "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==" - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" - }, - "kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==" - }, - "latest-version": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-7.0.0.tgz", - "integrity": "sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg==", - "requires": { - "package-json": "^8.1.0" - } - }, - "launch-editor": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.6.1.tgz", - "integrity": "sha512-eB/uXmFVpY4zezmGp5XtU21kwo7GBbKB+EQ+UZeWtGb9yAM5xt/Evk+lYH3eRNAtId+ej4u7TYPFZ07w4s7rRw==", - "requires": { - "picocolors": "^1.0.0", - "shell-quote": "^1.8.1" - } - }, - "layout-base": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz", - "integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==" - }, - "leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==" - }, - "lilconfig": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", - "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==" - }, - "lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" - }, - "loader-runner": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==" - }, - "loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - } - }, - "locate-path": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", - "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", - "requires": { - "p-locate": "^6.0.0" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "lodash-es": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" - }, - "lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" - }, - "lodash.escape": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-4.0.1.tgz", - "integrity": "sha512-nXEOnb/jK9g0DYMr1/Xvq6l5xMD7GDG55+GSYIYmS0G4tBk/hURD4JR9WCavs04t33WmJx9kCyp9vJ+mr4BOUw==" - }, - "lodash.flatten": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==" - }, - "lodash.invokemap": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.invokemap/-/lodash.invokemap-4.6.0.tgz", - "integrity": "sha512-CfkycNtMqgUlfjfdh2BhKO/ZXrP8ePOX5lEU/g0R3ItJcnuxWDwokMGKx1hWcfOikmyOVx6X9IwWnDGlgKl61w==" - }, - "lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==" - }, - "lodash.pullall": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.pullall/-/lodash.pullall-4.2.0.tgz", - "integrity": "sha512-VhqxBKH0ZxPpLhiu68YD1KnHmbhQJQctcipvmFnqIBDYzcIHzf3Zpu0tpeOKtR4x76p9yohc506eGdOjTmyIBg==" - }, - "lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==" - }, - "lodash.uniqby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz", - "integrity": "sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==" - }, - "longest-streak": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", - "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==" - }, - "loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" - } - }, - "lower-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", - "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "requires": { - "tslib": "^2.0.3" - } - }, - "lowercase-keys": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", - "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==" - }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "requires": { - "yallist": "^3.0.2" - } - }, - "lz-string": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", - "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==" - }, - "markdown-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-2.0.0.tgz", - "integrity": "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==" - }, - "markdown-table": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.3.tgz", - "integrity": "sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==" - }, - "mdast-util-directive": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-directive/-/mdast-util-directive-3.0.0.tgz", - "integrity": "sha512-JUpYOqKI4mM3sZcNxmF/ox04XYFFkNwr0CFlrQIkCwbvH0xzMCqkMqAde9wRd80VAhaUrwFwKm2nxretdT1h7Q==", - "requires": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "parse-entities": "^4.0.0", - "stringify-entities": "^4.0.0", - "unist-util-visit-parents": "^6.0.0" - } - }, - "mdast-util-find-and-replace": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.1.tgz", - "integrity": "sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA==", - "requires": { - "@types/mdast": "^4.0.0", - "escape-string-regexp": "^5.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==" - } - } - }, - "mdast-util-from-markdown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.0.tgz", - "integrity": "sha512-n7MTOr/z+8NAX/wmhhDji8O3bRvPTV/U0oTCaZJkjhPSKTPhS3xufVhKGF8s1pJ7Ox4QgoIU7KHseh09S+9rTA==", - "requires": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark": "^4.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-decode-string": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "dependencies": { - "micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" - } - } - }, - "mdast-util-frontmatter": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-frontmatter/-/mdast-util-frontmatter-2.0.1.tgz", - "integrity": "sha512-LRqI9+wdgC25P0URIJY9vwocIzCcksduHQ9OF2joxQoyTNVduwLAFUzjoopuRJbJAReaKrNQKAZKL3uCMugWJA==", - "requires": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "escape-string-regexp": "^5.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "micromark-extension-frontmatter": "^2.0.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==" - } - } - }, - "mdast-util-gfm": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.0.0.tgz", - "integrity": "sha512-dgQEX5Amaq+DuUqf26jJqSK9qgixgd6rYDHAv4aTBuA92cTknZlKpPfa86Z/s8Dj8xsAQpFfBmPUHWJBWqS4Bw==", - "requires": { - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-gfm-autolink-literal": "^2.0.0", - "mdast-util-gfm-footnote": "^2.0.0", - "mdast-util-gfm-strikethrough": "^2.0.0", - "mdast-util-gfm-table": "^2.0.0", - "mdast-util-gfm-task-list-item": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - } - }, - "mdast-util-gfm-autolink-literal": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.0.tgz", - "integrity": "sha512-FyzMsduZZHSc3i0Px3PQcBT4WJY/X/RCtEJKuybiC6sjPqLv7h1yqAkmILZtuxMSsUyaLUWNp71+vQH2zqp5cg==", - "requires": { - "@types/mdast": "^4.0.0", - "ccount": "^2.0.0", - "devlop": "^1.0.0", - "mdast-util-find-and-replace": "^3.0.0", - "micromark-util-character": "^2.0.0" - }, - "dependencies": { - "micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "requires": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" - } - } - }, - "mdast-util-gfm-footnote": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.0.0.tgz", - "integrity": "sha512-5jOT2boTSVkMnQ7LTrd6n/18kqwjmuYqo7JUPe+tRCY6O7dAuTFMtTPauYYrMPpox9hlN0uOx/FL8XvEfG9/mQ==", - "requires": { - "@types/mdast": "^4.0.0", - "devlop": "^1.1.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0" - } - }, - "mdast-util-gfm-strikethrough": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", - "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", - "requires": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - } - }, - "mdast-util-gfm-table": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", - "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", - "requires": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "markdown-table": "^3.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - } - }, - "mdast-util-gfm-task-list-item": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", - "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", - "requires": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - } - }, - "mdast-util-mdx": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-3.0.0.tgz", - "integrity": "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==", - "requires": { - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-mdx-expression": "^2.0.0", - "mdast-util-mdx-jsx": "^3.0.0", - "mdast-util-mdxjs-esm": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - } - }, - "mdast-util-mdx-expression": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.0.tgz", - "integrity": "sha512-fGCu8eWdKUKNu5mohVGkhBXCXGnOTLuFqOvGMvdikr+J1w7lDJgxThOKpwRWzzbyXAU2hhSwsmssOY4yTokluw==", - "requires": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - } - }, - "mdast-util-mdx-jsx": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.1.1.tgz", - "integrity": "sha512-Di63TQEHbiApe6CFp/qQXCORHMHnmW2JFdr5PYH57LuEIPjijRHicAmL5wQu+B0/Q4p0qJaEOE1EkhiwxiNmAQ==", - "requires": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "ccount": "^2.0.0", - "devlop": "^1.1.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "parse-entities": "^4.0.0", - "stringify-entities": "^4.0.0", - "unist-util-remove-position": "^5.0.0", - "unist-util-stringify-position": "^4.0.0", - "vfile-message": "^4.0.0" - } - }, - "mdast-util-mdxjs-esm": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", - "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", - "requires": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - } - }, - "mdast-util-phrasing": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", - "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", - "requires": { - "@types/mdast": "^4.0.0", - "unist-util-is": "^6.0.0" - } - }, - "mdast-util-to-hast": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.1.0.tgz", - "integrity": "sha512-/e2l/6+OdGp/FB+ctrJ9Avz71AN/GRH3oi/3KAx/kMnoUsD6q0woXlDT8lLEeViVKE7oZxE7RXzvO3T8kF2/sA==", - "requires": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@ungap/structured-clone": "^1.0.0", - "devlop": "^1.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "trim-lines": "^3.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - } - }, - "mdast-util-to-markdown": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.0.tgz", - "integrity": "sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ==", - "requires": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "longest-streak": "^3.0.0", - "mdast-util-phrasing": "^4.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark-util-decode-string": "^2.0.0", - "unist-util-visit": "^5.0.0", - "zwitch": "^2.0.0" - } - }, - "mdast-util-to-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", - "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", - "requires": { - "@types/mdast": "^4.0.0" - } - }, - "mdn-data": { - "version": "2.0.30", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", - "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==" - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" - }, - "memfs": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", - "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", - "requires": { - "fs-monkey": "^1.0.4" - } - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" - }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" - }, - "mermaid": { - "version": "10.6.1", - "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-10.6.1.tgz", - "integrity": "sha512-Hky0/RpOw/1il9X8AvzOEChfJtVvmXm+y7JML5C//ePYMy0/9jCEmW1E1g86x9oDfW9+iVEdTV/i+M6KWRNs4A==", - "requires": { - "@braintree/sanitize-url": "^6.0.1", - "@types/d3-scale": "^4.0.3", - "@types/d3-scale-chromatic": "^3.0.0", - "cytoscape": "^3.23.0", - "cytoscape-cose-bilkent": "^4.1.0", - "cytoscape-fcose": "^2.1.0", - "d3": "^7.4.0", - "d3-sankey": "^0.12.3", - "dagre-d3-es": "7.0.10", - "dayjs": "^1.11.7", - "dompurify": "^3.0.5", - "elkjs": "^0.8.2", - "khroma": "^2.0.0", - "lodash-es": "^4.17.21", - "mdast-util-from-markdown": "^1.3.0", - "non-layered-tidy-tree-layout": "^2.0.2", - "stylis": "^4.1.3", - "ts-dedent": "^2.2.0", - "uuid": "^9.0.0", - "web-worker": "^1.2.0" - }, - "dependencies": { - "@types/mdast": { - "version": "3.0.15", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz", - "integrity": "sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==", - "requires": { - "@types/unist": "^2" - } - }, - "@types/unist": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz", - "integrity": "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==" - }, - "mdast-util-from-markdown": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-1.3.1.tgz", - "integrity": "sha512-4xTO/M8c82qBcnQc1tgpNtubGUW/Y1tBQ1B0i5CtSoelOLKFYlElIr3bvgREYYO5iRqbMY1YuqZng0GVOI8Qww==", - "requires": { - "@types/mdast": "^3.0.0", - "@types/unist": "^2.0.0", - "decode-named-character-reference": "^1.0.0", - "mdast-util-to-string": "^3.1.0", - "micromark": "^3.0.0", - "micromark-util-decode-numeric-character-reference": "^1.0.0", - "micromark-util-decode-string": "^1.0.0", - "micromark-util-normalize-identifier": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0", - "unist-util-stringify-position": "^3.0.0", - "uvu": "^0.5.0" - } - }, - "mdast-util-to-string": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.2.0.tgz", - "integrity": "sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==", - "requires": { - "@types/mdast": "^3.0.0" - } - }, - "micromark": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.2.0.tgz", - "integrity": "sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA==", - "requires": { - "@types/debug": "^4.0.0", - "debug": "^4.0.0", - "decode-named-character-reference": "^1.0.0", - "micromark-core-commonmark": "^1.0.1", - "micromark-factory-space": "^1.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-chunked": "^1.0.0", - "micromark-util-combine-extensions": "^1.0.0", - "micromark-util-decode-numeric-character-reference": "^1.0.0", - "micromark-util-encode": "^1.0.0", - "micromark-util-normalize-identifier": "^1.0.0", - "micromark-util-resolve-all": "^1.0.0", - "micromark-util-sanitize-uri": "^1.0.0", - "micromark-util-subtokenize": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.1", - "uvu": "^0.5.0" - } - }, - "micromark-core-commonmark": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-1.1.0.tgz", - "integrity": "sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw==", - "requires": { - "decode-named-character-reference": "^1.0.0", - "micromark-factory-destination": "^1.0.0", - "micromark-factory-label": "^1.0.0", - "micromark-factory-space": "^1.0.0", - "micromark-factory-title": "^1.0.0", - "micromark-factory-whitespace": "^1.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-chunked": "^1.0.0", - "micromark-util-classify-character": "^1.0.0", - "micromark-util-html-tag-name": "^1.0.0", - "micromark-util-normalize-identifier": "^1.0.0", - "micromark-util-resolve-all": "^1.0.0", - "micromark-util-subtokenize": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.1", - "uvu": "^0.5.0" - } - }, - "micromark-factory-destination": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-1.1.0.tgz", - "integrity": "sha512-XaNDROBgx9SgSChd69pjiGKbV+nfHGDPVYFs5dOoDd7ZnMAE+Cuu91BCpsY8RT2NP9vo/B8pds2VQNCLiu0zhg==", - "requires": { - "micromark-util-character": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0" - } - }, - "micromark-factory-label": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-1.1.0.tgz", - "integrity": "sha512-OLtyez4vZo/1NjxGhcpDSbHQ+m0IIGnT8BoPamh+7jVlzLJBH98zzuCoUeMxvM6WsNeh8wx8cKvqLiPHEACn0w==", - "requires": { - "micromark-util-character": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0", - "uvu": "^0.5.0" - } - }, - "micromark-factory-title": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-1.1.0.tgz", - "integrity": "sha512-J7n9R3vMmgjDOCY8NPw55jiyaQnH5kBdV2/UXCtZIpnHH3P6nHUKaH7XXEYuWwx/xUJcawa8plLBEjMPU24HzQ==", - "requires": { - "micromark-factory-space": "^1.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0" - } - }, - "micromark-factory-whitespace": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-1.1.0.tgz", - "integrity": "sha512-v2WlmiymVSp5oMg+1Q0N1Lxmt6pMhIHD457whWM7/GUlEks1hI9xj5w3zbc4uuMKXGisksZk8DzP2UyGbGqNsQ==", - "requires": { - "micromark-factory-space": "^1.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0" - } - }, - "micromark-util-chunked": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-1.1.0.tgz", - "integrity": "sha512-Ye01HXpkZPNcV6FiyoW2fGZDUw4Yc7vT0E9Sad83+bEDiCJ1uXu0S3mr8WLpsz3HaG3x2q0HM6CTuPdcZcluFQ==", - "requires": { - "micromark-util-symbol": "^1.0.0" - } - }, - "micromark-util-classify-character": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-1.1.0.tgz", - "integrity": "sha512-SL0wLxtKSnklKSUplok1WQFoGhUdWYKggKUiqhX+Swala+BtptGCu5iPRc+xvzJ4PXE/hwM3FNXsfEVgoZsWbw==", - "requires": { - "micromark-util-character": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0" - } - }, - "micromark-util-combine-extensions": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.1.0.tgz", - "integrity": "sha512-Q20sp4mfNf9yEqDL50WwuWZHUrCO4fEyeDCnMGmG5Pr0Cz15Uo7KBs6jq+dq0EgX4DPwwrh9m0X+zPV1ypFvUA==", - "requires": { - "micromark-util-chunked": "^1.0.0", - "micromark-util-types": "^1.0.0" - } - }, - "micromark-util-decode-numeric-character-reference": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.1.0.tgz", - "integrity": "sha512-m9V0ExGv0jB1OT21mrWcuf4QhP46pH1KkfWy9ZEezqHKAxkj4mPCy3nIH1rkbdMlChLHX531eOrymlwyZIf2iw==", - "requires": { - "micromark-util-symbol": "^1.0.0" - } - }, - "micromark-util-decode-string": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-1.1.0.tgz", - "integrity": "sha512-YphLGCK8gM1tG1bd54azwyrQRjCFcmgj2S2GoJDNnh4vYtnL38JS8M4gpxzOPNyHdNEpheyWXCTnnTDY3N+NVQ==", - "requires": { - "decode-named-character-reference": "^1.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-decode-numeric-character-reference": "^1.0.0", - "micromark-util-symbol": "^1.0.0" - } - }, - "micromark-util-encode": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-1.1.0.tgz", - "integrity": "sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw==" - }, - "micromark-util-html-tag-name": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.2.0.tgz", - "integrity": "sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q==" - }, - "micromark-util-normalize-identifier": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.1.0.tgz", - "integrity": "sha512-N+w5vhqrBihhjdpM8+5Xsxy71QWqGn7HYNUvch71iV2PM7+E3uWGox1Qp90loa1ephtCxG2ftRV/Conitc6P2Q==", - "requires": { - "micromark-util-symbol": "^1.0.0" - } - }, - "micromark-util-resolve-all": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-1.1.0.tgz", - "integrity": "sha512-b/G6BTMSg+bX+xVCshPTPyAu2tmA0E4X98NSR7eIbeC6ycCqCeE7wjfDIgzEbkzdEVJXRtOG4FbEm/uGbCRouA==", - "requires": { - "micromark-util-types": "^1.0.0" - } - }, - "micromark-util-sanitize-uri": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.2.0.tgz", - "integrity": "sha512-QO4GXv0XZfWey4pYFndLUKEAktKkG5kZTdUNaTAkzbuJxn2tNBOr+QtxR2XpWaMhbImT2dPzyLrPXLlPhph34A==", - "requires": { - "micromark-util-character": "^1.0.0", - "micromark-util-encode": "^1.0.0", - "micromark-util-symbol": "^1.0.0" - } - }, - "micromark-util-subtokenize": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-1.1.0.tgz", - "integrity": "sha512-kUQHyzRoxvZO2PuLzMt2P/dwVsTiivCK8icYTeR+3WgbuPqfHgPPy7nFKbeqRivBvn/3N3GBiNC+JRTMSxEC7A==", - "requires": { - "micromark-util-chunked": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0", - "uvu": "^0.5.0" - } - }, - "micromark-util-types": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz", - "integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==" - }, - "unist-util-stringify-position": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.3.tgz", - "integrity": "sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==", - "requires": { - "@types/unist": "^2.0.0" - } - }, - "uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==" - } - } - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" - }, - "micromark": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.0.tgz", - "integrity": "sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==", - "requires": { - "@types/debug": "^4.0.0", - "debug": "^4.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "dependencies": { - "micromark-factory-space": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", - "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", - "requires": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "requires": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" - } - } - }, - "micromark-core-commonmark": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.0.tgz", - "integrity": "sha512-jThOz/pVmAYUtkroV3D5c1osFXAMv9e0ypGDOIZuCeAe91/sD6BoE2Sjzt30yuXtwOYUmySOhMas/PVyh02itA==", - "requires": { - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-factory-destination": "^2.0.0", - "micromark-factory-label": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-factory-title": "^2.0.0", - "micromark-factory-whitespace": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-html-tag-name": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "dependencies": { - "micromark-factory-space": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", - "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", - "requires": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "requires": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" - } - } - }, - "micromark-extension-directive": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-3.0.0.tgz", - "integrity": "sha512-61OI07qpQrERc+0wEysLHMvoiO3s2R56x5u7glHq2Yqq6EHbH4dW25G9GfDdGCDYqA21KE6DWgNSzxSwHc2hSg==", - "requires": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-factory-whitespace": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "parse-entities": "^4.0.0" - }, - "dependencies": { - "micromark-factory-space": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", - "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", - "requires": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "requires": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" - } - } - }, - "micromark-extension-frontmatter": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-frontmatter/-/micromark-extension-frontmatter-2.0.0.tgz", - "integrity": "sha512-C4AkuM3dA58cgZha7zVnuVxBhDsbttIMiytjgsM2XbHAB2faRVaHRle40558FBN+DJcrLNCoqG5mlrpdU4cRtg==", - "requires": { - "fault": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "dependencies": { - "micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "requires": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" - } - } - }, - "micromark-extension-gfm": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", - "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", - "requires": { - "micromark-extension-gfm-autolink-literal": "^2.0.0", - "micromark-extension-gfm-footnote": "^2.0.0", - "micromark-extension-gfm-strikethrough": "^2.0.0", - "micromark-extension-gfm-table": "^2.0.0", - "micromark-extension-gfm-tagfilter": "^2.0.0", - "micromark-extension-gfm-task-list-item": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "micromark-extension-gfm-autolink-literal": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.0.0.tgz", - "integrity": "sha512-rTHfnpt/Q7dEAK1Y5ii0W8bhfJlVJFnJMHIPisfPK3gpVNuOP0VnRl96+YJ3RYWV/P4gFeQoGKNlT3RhuvpqAg==", - "requires": { - "micromark-util-character": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "dependencies": { - "micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "requires": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" - } - } - }, - "micromark-extension-gfm-footnote": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.0.0.tgz", - "integrity": "sha512-6Rzu0CYRKDv3BfLAUnZsSlzx3ak6HAoI85KTiijuKIz5UxZxbUI+pD6oHgw+6UtQuiRwnGRhzMmPRv4smcz0fg==", - "requires": { - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "dependencies": { - "micromark-factory-space": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", - "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", - "requires": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "requires": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" - } - } - }, - "micromark-extension-gfm-strikethrough": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.0.0.tgz", - "integrity": "sha512-c3BR1ClMp5fxxmwP6AoOY2fXO9U8uFMKs4ADD66ahLTNcwzSCyRVU4k7LPV5Nxo/VJiR4TdzxRQY2v3qIUceCw==", - "requires": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "dependencies": { - "micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" - } - } - }, - "micromark-extension-gfm-table": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.0.0.tgz", - "integrity": "sha512-PoHlhypg1ItIucOaHmKE8fbin3vTLpDOUg8KAr8gRCF1MOZI9Nquq2i/44wFvviM4WuxJzc3demT8Y3dkfvYrw==", - "requires": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "dependencies": { - "micromark-factory-space": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", - "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", - "requires": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "requires": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" - } - } - }, - "micromark-extension-gfm-tagfilter": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", - "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", - "requires": { - "micromark-util-types": "^2.0.0" - } - }, - "micromark-extension-gfm-task-list-item": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.0.1.tgz", - "integrity": "sha512-cY5PzGcnULaN5O7T+cOzfMoHjBW7j+T9D2sucA5d/KbsBTPcYdebm9zUd9zzdgJGCwahV+/W78Z3nbulBYVbTw==", - "requires": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "dependencies": { - "micromark-factory-space": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", - "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", - "requires": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "requires": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" - } - } - }, - "micromark-extension-mdx-expression": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-3.0.0.tgz", - "integrity": "sha512-sI0nwhUDz97xyzqJAbHQhp5TfaxEvZZZ2JDqUo+7NvyIYG6BZ5CPPqj2ogUoPJlmXHBnyZUzISg9+oUmU6tUjQ==", - "requires": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0", - "micromark-factory-mdx-expression": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-events-to-acorn": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "dependencies": { - "micromark-factory-space": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", - "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", - "requires": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "requires": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" - } - } - }, - "micromark-extension-mdx-jsx": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-3.0.0.tgz", - "integrity": "sha512-uvhhss8OGuzR4/N17L1JwvmJIpPhAd8oByMawEKx6NVdBCbesjH4t+vjEp3ZXft9DwvlKSD07fCeI44/N0Vf2w==", - "requires": { - "@types/acorn": "^4.0.0", - "@types/estree": "^1.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "micromark-factory-mdx-expression": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "vfile-message": "^4.0.0" - }, - "dependencies": { - "micromark-factory-space": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", - "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", - "requires": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "requires": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" - } - } - }, - "micromark-extension-mdx-md": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-mdx-md/-/micromark-extension-mdx-md-2.0.0.tgz", - "integrity": "sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==", - "requires": { - "micromark-util-types": "^2.0.0" - } - }, - "micromark-extension-mdxjs": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs/-/micromark-extension-mdxjs-3.0.0.tgz", - "integrity": "sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==", - "requires": { - "acorn": "^8.0.0", - "acorn-jsx": "^5.0.0", - "micromark-extension-mdx-expression": "^3.0.0", - "micromark-extension-mdx-jsx": "^3.0.0", - "micromark-extension-mdx-md": "^2.0.0", - "micromark-extension-mdxjs-esm": "^3.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "micromark-extension-mdxjs-esm": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-3.0.0.tgz", - "integrity": "sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==", - "requires": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-events-to-acorn": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unist-util-position-from-estree": "^2.0.0", - "vfile-message": "^4.0.0" - }, - "dependencies": { - "micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "requires": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" - } - } - }, - "micromark-factory-destination": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.0.tgz", - "integrity": "sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==", - "requires": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "dependencies": { - "micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "requires": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" - } - } - }, - "micromark-factory-label": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.0.tgz", - "integrity": "sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==", - "requires": { - "devlop": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "dependencies": { - "micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "requires": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" - } - } - }, - "micromark-factory-mdx-expression": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-2.0.1.tgz", - "integrity": "sha512-F0ccWIUHRLRrYp5TC9ZYXmZo+p2AM13ggbsW4T0b5CRKP8KHVRB8t4pwtBgTxtjRmwrK0Irwm7vs2JOZabHZfg==", - "requires": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-events-to-acorn": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unist-util-position-from-estree": "^2.0.0", - "vfile-message": "^4.0.0" - }, - "dependencies": { - "micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "requires": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" - } - } - }, - "micromark-factory-space": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-1.1.0.tgz", - "integrity": "sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ==", - "requires": { - "micromark-util-character": "^1.0.0", - "micromark-util-types": "^1.0.0" - }, - "dependencies": { - "micromark-util-types": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz", - "integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==" - } - } - }, - "micromark-factory-title": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.0.tgz", - "integrity": "sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A==", - "requires": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "dependencies": { - "micromark-factory-space": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", - "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", - "requires": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "requires": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" - } - } - }, - "micromark-factory-whitespace": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.0.tgz", - "integrity": "sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA==", - "requires": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "dependencies": { - "micromark-factory-space": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", - "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", - "requires": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "requires": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" - } - } - }, - "micromark-util-character": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-1.2.0.tgz", - "integrity": "sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg==", - "requires": { - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0" - }, - "dependencies": { - "micromark-util-types": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz", - "integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==" - } - } - }, - "micromark-util-chunked": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.0.tgz", - "integrity": "sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==", - "requires": { - "micromark-util-symbol": "^2.0.0" - }, - "dependencies": { - "micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" - } - } - }, - "micromark-util-classify-character": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.0.tgz", - "integrity": "sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw==", - "requires": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "dependencies": { - "micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "requires": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" - } - } - }, - "micromark-util-combine-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.0.tgz", - "integrity": "sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ==", - "requires": { - "micromark-util-chunked": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "micromark-util-decode-numeric-character-reference": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.1.tgz", - "integrity": "sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ==", - "requires": { - "micromark-util-symbol": "^2.0.0" - }, - "dependencies": { - "micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" - } - } - }, - "micromark-util-decode-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.0.tgz", - "integrity": "sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA==", - "requires": { - "decode-named-character-reference": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - }, - "dependencies": { - "micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "requires": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" - } - } - }, - "micromark-util-encode": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz", - "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==" - }, - "micromark-util-events-to-acorn": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-2.0.2.tgz", - "integrity": "sha512-Fk+xmBrOv9QZnEDguL9OI9/NQQp6Hz4FuQ4YmCb/5V7+9eAh1s6AYSvL20kHkD67YIg7EpE54TiSlcsf3vyZgA==", - "requires": { - "@types/acorn": "^4.0.0", - "@types/estree": "^1.0.0", - "@types/unist": "^3.0.0", - "devlop": "^1.0.0", - "estree-util-visit": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "vfile-message": "^4.0.0" - }, - "dependencies": { - "micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" - } - } - }, - "micromark-util-html-tag-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.0.tgz", - "integrity": "sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==" - }, - "micromark-util-normalize-identifier": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.0.tgz", - "integrity": "sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w==", - "requires": { - "micromark-util-symbol": "^2.0.0" - }, - "dependencies": { - "micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" - } - } - }, - "micromark-util-resolve-all": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.0.tgz", - "integrity": "sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA==", - "requires": { - "micromark-util-types": "^2.0.0" - } - }, - "micromark-util-sanitize-uri": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz", - "integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==", - "requires": { - "micromark-util-character": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - }, - "dependencies": { - "micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "requires": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" - } - } - }, - "micromark-util-subtokenize": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.0.tgz", - "integrity": "sha512-vc93L1t+gpR3p8jxeVdaYlbV2jTYteDje19rNSS/H5dlhxUYll5Fy6vJ2cDwP8RnsXi818yGty1ayP55y3W6fg==", - "requires": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "dependencies": { - "micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" - } - } - }, - "micromark-util-symbol": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.1.0.tgz", - "integrity": "sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==" - }, - "micromark-util-types": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.0.tgz", - "integrity": "sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==" - }, - "micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "requires": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - } - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" - }, - "mime-db": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", - "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==" - }, - "mime-types": { - "version": "2.1.18", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", - "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", - "requires": { - "mime-db": "~1.33.0" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" - }, - "mimic-response": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", - "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==" - }, - "mini-css-extract-plugin": { - "version": "2.7.6", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.6.tgz", - "integrity": "sha512-Qk7HcgaPkGG6eD77mLvZS1nmxlao3j+9PkrT9Uc7HAE1id3F41+DdBRYRYkbyfNRGzm8/YWtzhw7nVPmwhqTQw==", - "requires": { - "schema-utils": "^4.0.0" - } - }, - "minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" - }, - "mri": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", - "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==" - }, - "mrmime": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz", - "integrity": "sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==" - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "multicast-dns": { - "version": "7.2.5", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", - "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", - "requires": { - "dns-packet": "^5.2.2", - "thunky": "^1.0.2" - } - }, - "nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==" - }, - "negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" - }, - "neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" - }, - "next-tick": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", - "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" - }, - "no-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", - "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", - "requires": { - "lower-case": "^2.0.2", - "tslib": "^2.0.3" - } - }, - "node-emoji": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.1.3.tgz", - "integrity": "sha512-E2WEOVsgs7O16zsURJ/eH8BqhF029wGpEOnv7Urwdo2wmQanOACwJQh0devF9D9RhoZru0+9JXIS0dBXIAz+lA==", - "requires": { - "@sindresorhus/is": "^4.6.0", - "char-regex": "^1.0.2", - "emojilib": "^2.4.0", - "skin-tone": "^2.0.0" - } - }, - "node-forge": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", - "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==" - }, - "node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==" - }, - "non-layered-tidy-tree-layout": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/non-layered-tidy-tree-layout/-/non-layered-tidy-tree-layout-2.0.2.tgz", - "integrity": "sha512-gkXMxRzUH+PB0ax9dUN0yYF0S25BqeAYqhgMaLUFmpXLEk7Fcu8f4emJuOAY0V8kjDICxROIKsTAKsV/v355xw==" - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" - }, - "normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==" - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "requires": { - "path-key": "^3.0.0" - } - }, - "nprogress": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz", - "integrity": "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==" - }, - "nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "requires": { - "boolbase": "^1.0.0" - } - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" - }, - "object-inspect": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==" - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" - }, - "object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - } - }, - "obuf": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", - "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" - }, - "on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "requires": { - "ee-first": "1.1.1" - } - }, - "on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "open": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", - "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", - "requires": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" - } - }, - "opener": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", - "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==" - }, - "outvariant": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.0.tgz", - "integrity": "sha512-AlWY719RF02ujitly7Kk/0QlV+pXGFDHrHf9O2OKqyqgBieaPOIeuSkL8sRK6j2WK+/ZAURq2kZsY0d8JapUiw==" - }, - "p-cancelable": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", - "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==" - }, - "p-limit": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", - "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", - "requires": { - "yocto-queue": "^1.0.0" - } - }, - "p-locate": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", - "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", - "requires": { - "p-limit": "^4.0.0" - } - }, - "p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "requires": { - "aggregate-error": "^3.0.0" - } - }, - "p-retry": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", - "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", - "requires": { - "@types/retry": "0.12.0", - "retry": "^0.13.1" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" - }, - "package-json": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-8.1.1.tgz", - "integrity": "sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==", - "requires": { - "got": "^12.1.0", - "registry-auth-token": "^5.0.1", - "registry-url": "^6.0.0", - "semver": "^7.3.7" - } - }, - "param-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", - "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", - "requires": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "requires": { - "callsites": "^3.0.0" - } - }, - "parse-entities": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.1.tgz", - "integrity": "sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==", - "requires": { - "@types/unist": "^2.0.0", - "character-entities": "^2.0.0", - "character-entities-legacy": "^3.0.0", - "character-reference-invalid": "^2.0.0", - "decode-named-character-reference": "^1.0.0", - "is-alphanumerical": "^2.0.0", - "is-decimal": "^2.0.0", - "is-hexadecimal": "^2.0.0" - }, - "dependencies": { - "@types/unist": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz", - "integrity": "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==" - } - } - }, - "parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, - "parse-numeric-range": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/parse-numeric-range/-/parse-numeric-range-1.3.0.tgz", - "integrity": "sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ==" - }, - "parse5": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", - "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", - "requires": { - "entities": "^4.4.0" - } - }, - "parse5-htmlparser2-tree-adapter": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", - "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", - "requires": { - "domhandler": "^5.0.2", - "parse5": "^7.0.0" - } - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" - }, - "pascal-case": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", - "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", - "requires": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "path-exists": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", - "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==" - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" - }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==" - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" - }, - "path-to-regexp": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", - "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", - "requires": { - "isarray": "0.0.1" - } - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" - }, - "periscopic": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", - "integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==", - "requires": { - "@types/estree": "^1.0.0", - "estree-walker": "^3.0.0", - "is-reference": "^3.0.0" - } - }, - "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" - }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" - }, - "pkg-dir": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-7.0.0.tgz", - "integrity": "sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==", - "requires": { - "find-up": "^6.3.0" - } - }, - "pkg-up": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", - "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", - "requires": { - "find-up": "^3.0.0" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "requires": { - "p-limit": "^2.0.0" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==" - } - } - }, - "postcss": { - "version": "8.4.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", - "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", - "requires": { - "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.2.0" - } - }, - "postcss-calc": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-9.0.1.tgz", - "integrity": "sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==", - "requires": { - "postcss-selector-parser": "^6.0.11", - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-colormin": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-6.1.0.tgz", - "integrity": "sha512-x9yX7DOxeMAR+BgGVnNSAxmAj98NX/YxEMNFP+SDCEeNLb2r3i6Hh1ksMsnW8Ub5SLCpbescQqn9YEbE9554Sw==", - "requires": { - "browserslist": "^4.23.0", - "caniuse-api": "^3.0.0", - "colord": "^2.9.3", - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-convert-values": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-6.1.0.tgz", - "integrity": "sha512-zx8IwP/ts9WvUM6NkVSkiU902QZL1bwPhaVaLynPtCsOTqp+ZKbNi+s6XJg3rfqpKGA/oc7Oxk5t8pOQJcwl/w==", - "requires": { - "browserslist": "^4.23.0", - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-discard-comments": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-6.0.2.tgz", - "integrity": "sha512-65w/uIqhSBBfQmYnG92FO1mWZjJ4GL5b8atm5Yw2UgrwD7HiNiSSNwJor1eCFGzUgYnN/iIknhNRVqjrrpuglw==", - "requires": {} - }, - "postcss-discard-duplicates": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-6.0.3.tgz", - "integrity": "sha512-+JA0DCvc5XvFAxwx6f/e68gQu/7Z9ud584VLmcgto28eB8FqSFZwtrLwB5Kcp70eIoWP/HXqz4wpo8rD8gpsTw==", - "requires": {} - }, - "postcss-discard-empty": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-6.0.3.tgz", - "integrity": "sha512-znyno9cHKQsK6PtxL5D19Fj9uwSzC2mB74cpT66fhgOadEUPyXFkbgwm5tvc3bt3NAy8ltE5MrghxovZRVnOjQ==", - "requires": {} - }, - "postcss-discard-overridden": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-6.0.2.tgz", - "integrity": "sha512-j87xzI4LUggC5zND7KdjsI25APtyMuynXZSujByMaav2roV6OZX+8AaCUcZSWqckZpjAjRyFDdpqybgjFO0HJQ==", - "requires": {} - }, - "postcss-discard-unused": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-6.0.5.tgz", - "integrity": "sha512-wHalBlRHkaNnNwfC8z+ppX57VhvS+HWgjW508esjdaEYr3Mx7Gnn2xA4R/CKf5+Z9S5qsqC+Uzh4ueENWwCVUA==", - "requires": { - "postcss-selector-parser": "^6.0.16" - } - }, - "postcss-loader": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.3.3.tgz", - "integrity": "sha512-YgO/yhtevGO/vJePCQmTxiaEwER94LABZN0ZMT4A0vsak9TpO+RvKRs7EmJ8peIlB9xfXCsS7M8LjqncsUZ5HA==", - "requires": { - "cosmiconfig": "^8.2.0", - "jiti": "^1.18.2", - "semver": "^7.3.8" - } - }, - "postcss-merge-idents": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-6.0.3.tgz", - "integrity": "sha512-1oIoAsODUs6IHQZkLQGO15uGEbK3EAl5wi9SS8hs45VgsxQfMnxvt+L+zIr7ifZFIH14cfAeVe2uCTa+SPRa3g==", - "requires": { - "cssnano-utils": "^4.0.2", - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-merge-longhand": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-6.0.5.tgz", - "integrity": "sha512-5LOiordeTfi64QhICp07nzzuTDjNSO8g5Ksdibt44d+uvIIAE1oZdRn8y/W5ZtYgRH/lnLDlvi9F8btZcVzu3w==", - "requires": { - "postcss-value-parser": "^4.2.0", - "stylehacks": "^6.1.1" - } - }, - "postcss-merge-rules": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-6.1.1.tgz", - "integrity": "sha512-KOdWF0gju31AQPZiD+2Ar9Qjowz1LTChSjFFbS+e2sFgc4uHOp3ZvVX4sNeTlk0w2O31ecFGgrFzhO0RSWbWwQ==", - "requires": { - "browserslist": "^4.23.0", - "caniuse-api": "^3.0.0", - "cssnano-utils": "^4.0.2", - "postcss-selector-parser": "^6.0.16" - } - }, - "postcss-minify-font-values": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-6.1.0.tgz", - "integrity": "sha512-gklfI/n+9rTh8nYaSJXlCo3nOKqMNkxuGpTn/Qm0gstL3ywTr9/WRKznE+oy6fvfolH6dF+QM4nCo8yPLdvGJg==", - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-minify-gradients": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-6.0.3.tgz", - "integrity": "sha512-4KXAHrYlzF0Rr7uc4VrfwDJ2ajrtNEpNEuLxFgwkhFZ56/7gaE4Nr49nLsQDZyUe+ds+kEhf+YAUolJiYXF8+Q==", - "requires": { - "colord": "^2.9.3", - "cssnano-utils": "^4.0.2", - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-minify-params": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-6.1.0.tgz", - "integrity": "sha512-bmSKnDtyyE8ujHQK0RQJDIKhQ20Jq1LYiez54WiaOoBtcSuflfK3Nm596LvbtlFcpipMjgClQGyGr7GAs+H1uA==", - "requires": { - "browserslist": "^4.23.0", - "cssnano-utils": "^4.0.2", - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-minify-selectors": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-6.0.4.tgz", - "integrity": "sha512-L8dZSwNLgK7pjTto9PzWRoMbnLq5vsZSTu8+j1P/2GB8qdtGQfn+K1uSvFgYvgh83cbyxT5m43ZZhUMTJDSClQ==", - "requires": { - "postcss-selector-parser": "^6.0.16" - } - }, - "postcss-modules-extract-imports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", - "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", - "requires": {} - }, - "postcss-modules-local-by-default": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.3.tgz", - "integrity": "sha512-2/u2zraspoACtrbFRnTijMiQtb4GW4BvatjaG/bCjYQo8kLTdevCUlwuBHx2sCnSyrI3x3qj4ZK1j5LQBgzmwA==", - "requires": { - "icss-utils": "^5.0.0", - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.1.0" - } - }, - "postcss-modules-scope": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", - "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", - "requires": { - "postcss-selector-parser": "^6.0.4" - } - }, - "postcss-modules-values": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", - "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", - "requires": { - "icss-utils": "^5.0.0" - } - }, - "postcss-normalize-charset": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-6.0.2.tgz", - "integrity": "sha512-a8N9czmdnrjPHa3DeFlwqst5eaL5W8jYu3EBbTTkI5FHkfMhFZh1EGbku6jhHhIzTA6tquI2P42NtZ59M/H/kQ==", - "requires": {} - }, - "postcss-normalize-display-values": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-6.0.2.tgz", - "integrity": "sha512-8H04Mxsb82ON/aAkPeq8kcBbAtI5Q2a64X/mnRRfPXBq7XeogoQvReqxEfc0B4WPq1KimjezNC8flUtC3Qz6jg==", - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-normalize-positions": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-6.0.2.tgz", - "integrity": "sha512-/JFzI441OAB9O7VnLA+RtSNZvQ0NCFZDOtp6QPFo1iIyawyXg0YI3CYM9HBy1WvwCRHnPep/BvI1+dGPKoXx/Q==", - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-normalize-repeat-style": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-6.0.2.tgz", - "integrity": "sha512-YdCgsfHkJ2jEXwR4RR3Tm/iOxSfdRt7jplS6XRh9Js9PyCR/aka/FCb6TuHT2U8gQubbm/mPmF6L7FY9d79VwQ==", - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-normalize-string": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-6.0.2.tgz", - "integrity": "sha512-vQZIivlxlfqqMp4L9PZsFE4YUkWniziKjQWUtsxUiVsSSPelQydwS8Wwcuw0+83ZjPWNTl02oxlIvXsmmG+CiQ==", - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-normalize-timing-functions": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-6.0.2.tgz", - "integrity": "sha512-a+YrtMox4TBtId/AEwbA03VcJgtyW4dGBizPl7e88cTFULYsprgHWTbfyjSLyHeBcK/Q9JhXkt2ZXiwaVHoMzA==", - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-normalize-unicode": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-6.1.0.tgz", - "integrity": "sha512-QVC5TQHsVj33otj8/JD869Ndr5Xcc/+fwRh4HAsFsAeygQQXm+0PySrKbr/8tkDKzW+EVT3QkqZMfFrGiossDg==", - "requires": { - "browserslist": "^4.23.0", - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-normalize-url": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-6.0.2.tgz", - "integrity": "sha512-kVNcWhCeKAzZ8B4pv/DnrU1wNh458zBNp8dh4y5hhxih5RZQ12QWMuQrDgPRw3LRl8mN9vOVfHl7uhvHYMoXsQ==", - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-normalize-whitespace": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-6.0.2.tgz", - "integrity": "sha512-sXZ2Nj1icbJOKmdjXVT9pnyHQKiSAyuNQHSgRCUgThn2388Y9cGVDR+E9J9iAYbSbLHI+UUwLVl1Wzco/zgv0Q==", - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-ordered-values": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-6.0.2.tgz", - "integrity": "sha512-VRZSOB+JU32RsEAQrO94QPkClGPKJEL/Z9PCBImXMhIeK5KAYo6slP/hBYlLgrCjFxyqvn5VC81tycFEDBLG1Q==", - "requires": { - "cssnano-utils": "^4.0.2", - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-reduce-idents": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-6.0.3.tgz", - "integrity": "sha512-G3yCqZDpsNPoQgbDUy3T0E6hqOQ5xigUtBQyrmq3tn2GxlyiL0yyl7H+T8ulQR6kOcHJ9t7/9H4/R2tv8tJbMA==", - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-reduce-initial": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-6.1.0.tgz", - "integrity": "sha512-RarLgBK/CrL1qZags04oKbVbrrVK2wcxhvta3GCxrZO4zveibqbRPmm2VI8sSgCXwoUHEliRSbOfpR0b/VIoiw==", - "requires": { - "browserslist": "^4.23.0", - "caniuse-api": "^3.0.0" - } - }, - "postcss-reduce-transforms": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-6.0.2.tgz", - "integrity": "sha512-sB+Ya++3Xj1WaT9+5LOOdirAxP7dJZms3GRcYheSPi1PiTMigsxHAdkrbItHxwYHr4kt1zL7mmcHstgMYT+aiA==", - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-selector-parser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.0.tgz", - "integrity": "sha512-UMz42UD0UY0EApS0ZL9o1XnLhSTtvvvLe5Dc2H2O56fvRZi+KulDyf5ctDhhtYJBGKStV2FL1fy6253cmLgqVQ==", - "requires": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - } - }, - "postcss-sort-media-queries": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/postcss-sort-media-queries/-/postcss-sort-media-queries-5.2.0.tgz", - "integrity": "sha512-AZ5fDMLD8SldlAYlvi8NIqo0+Z8xnXU2ia0jxmuhxAU+Lqt9K+AlmLNJ/zWEnE9x+Zx3qL3+1K20ATgNOr3fAA==", - "requires": { - "sort-css-media-queries": "2.2.0" - } - }, - "postcss-svgo": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-6.0.3.tgz", - "integrity": "sha512-dlrahRmxP22bX6iKEjOM+c8/1p+81asjKT+V5lrgOH944ryx/OHpclnIbGsKVd3uWOXFLYJwCVf0eEkJGvO96g==", - "requires": { - "postcss-value-parser": "^4.2.0", - "svgo": "^3.2.0" - } - }, - "postcss-unique-selectors": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-6.0.4.tgz", - "integrity": "sha512-K38OCaIrO8+PzpArzkLKB42dSARtC2tmG6PvD4b1o1Q2E9Os8jzfWFfSy/rixsHwohtsDdFtAWGjFVFUdwYaMg==", - "requires": { - "postcss-selector-parser": "^6.0.16" - } - }, - "postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" - }, - "postcss-zindex": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-6.0.2.tgz", - "integrity": "sha512-5BxW9l1evPB/4ZIc+2GobEBoKC+h8gPGCMi+jxsYvd2x0mjq7wazk6DrP71pStqxE9Foxh5TVnonbWpFZzXaYg==", - "requires": {} - }, - "prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", - "dev": true - }, - "pretty-error": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", - "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", - "requires": { - "lodash": "^4.17.20", - "renderkid": "^3.0.0" - } - }, - "pretty-time": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pretty-time/-/pretty-time-1.1.0.tgz", - "integrity": "sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==" - }, - "prism-react-renderer": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-2.3.0.tgz", - "integrity": "sha512-UYRg2TkVIaI6tRVHC5OJ4/BxqPUxJkJvq/odLT/ykpt1zGYXooNperUxQcCvi87LyRnR4nCh81ceOA+e7nrydg==", - "requires": { - "@types/prismjs": "^1.26.0", - "clsx": "^2.0.0" - } - }, - "prismjs": { - "version": "1.29.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", - "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==" - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "requires": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - } - }, - "prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "requires": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - }, - "dependencies": { - "react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - } - } - }, - "property-information": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.4.1.tgz", - "integrity": "sha512-OHYtXfu5aI2sS2LWFSN5rgJjrQ4pCy8i1jubJLe2QvMF8JJ++HXTUIVWFLfXJoaOfvYYjk2SN8J2wFUWIGXT4w==" - }, - "proto-list": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", - "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==" - }, - "proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "requires": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "dependencies": { - "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" - } - } - }, - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==" - }, - "pupa": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/pupa/-/pupa-3.1.0.tgz", - "integrity": "sha512-FLpr4flz5xZTSJxSeaheeMKN/EDzMdK7b8PTOC6a5PYFKTucWbdqjgqaEyH0shFiSJrVB1+Qqi4Tk19ccU6Aug==", - "requires": { - "escape-goat": "^4.0.0" - } - }, - "qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "requires": { - "side-channel": "^1.0.4" - } - }, - "queue": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", - "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", - "requires": { - "inherits": "~2.0.3" - } - }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" - }, - "quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==" - }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "range-parser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", - "integrity": "sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==" - }, - "raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", - "requires": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "dependencies": { - "bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" - } - } - }, - "raw-loader": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-4.0.2.tgz", - "integrity": "sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA==", - "requires": { - "loader-utils": "^2.0.0", - "schema-utils": "^3.0.0" - }, - "dependencies": { - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "requires": {} - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "requires": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - } - } - } - }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==" - } - } - }, - "react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", - "requires": { - "loose-envify": "^1.1.0" - } - }, - "react-dev-utils": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", - "integrity": "sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==", - "requires": { - "@babel/code-frame": "^7.16.0", - "address": "^1.1.2", - "browserslist": "^4.18.1", - "chalk": "^4.1.2", - "cross-spawn": "^7.0.3", - "detect-port-alt": "^1.1.6", - "escape-string-regexp": "^4.0.0", - "filesize": "^8.0.6", - "find-up": "^5.0.0", - "fork-ts-checker-webpack-plugin": "^6.5.0", - "global-modules": "^2.0.0", - "globby": "^11.0.4", - "gzip-size": "^6.0.0", - "immer": "^9.0.7", - "is-root": "^2.1.0", - "loader-utils": "^3.2.0", - "open": "^8.4.0", - "pkg-up": "^3.1.0", - "prompts": "^2.4.2", - "react-error-overlay": "^6.0.11", - "recursive-readdir": "^2.2.2", - "shell-quote": "^1.7.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "dependencies": { - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "loader-utils": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz", - "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==" - }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "requires": { - "p-locate": "^5.0.0" - } - }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "requires": { - "p-limit": "^3.0.2" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" - }, - "yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" - } - } - }, - "react-devtools-inline": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/react-devtools-inline/-/react-devtools-inline-4.4.0.tgz", - "integrity": "sha512-ES0GolSrKO8wsKbsEkVeiR/ZAaHQTY4zDh1UW8DImVmm8oaGLl3ijJDvSGe+qDRKPZdPRnDtWWnSvvrgxXdThQ==", - "requires": { - "es6-symbol": "^3" - } - }, - "react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", - "requires": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" - } - }, - "react-error-overlay": { - "version": "6.0.11", - "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", - "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" - }, - "react-fast-compare": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", - "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==" - }, - "react-helmet-async": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-1.3.0.tgz", - "integrity": "sha512-9jZ57/dAn9t3q6hneQS0wukqC2ENOBgMNVEhb/ZG9ZSxUetzVIw4iAmEU38IaVg3QGYauQPhSeUTuIUtFglWpg==", - "requires": { - "@babel/runtime": "^7.12.5", - "invariant": "^2.2.4", - "prop-types": "^15.7.2", - "react-fast-compare": "^3.2.0", - "shallowequal": "^1.1.0" - } - }, - "react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" - }, - "react-json-view-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/react-json-view-lite/-/react-json-view-lite-1.4.0.tgz", - "integrity": "sha512-wh6F6uJyYAmQ4fK0e8dSQMEWuvTs2Wr3el3sLD9bambX1+pSWUVXIz1RFaoy3TI1mZ0FqdpKq9YgbgTTgyrmXA==", - "requires": {} - }, - "react-loadable": { - "version": "npm:@docusaurus/react-loadable@6.0.0", - "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-6.0.0.tgz", - "integrity": "sha512-YMMxTUQV/QFSnbgrP3tjDzLHRg7vsbMn8e9HAa8o/1iXoiomo48b7sk/kkmWEuWNDPJVlKSJRB6Y2fHqdJk+SQ==", - "requires": { - "@types/react": "*" - } - }, - "react-loadable-ssr-addon-v5-slorber": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/react-loadable-ssr-addon-v5-slorber/-/react-loadable-ssr-addon-v5-slorber-1.0.1.tgz", - "integrity": "sha512-lq3Lyw1lGku8zUEJPDxsNm1AfYHBrO9Y1+olAYwpUJ2IGFBskM0DMKok97A6LWUpHm+o7IvQBOWu9MLenp9Z+A==", - "requires": { - "@babel/runtime": "^7.10.3" - } - }, - "react-router": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.3.4.tgz", - "integrity": "sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA==", - "requires": { - "@babel/runtime": "^7.12.13", - "history": "^4.9.0", - "hoist-non-react-statics": "^3.1.0", - "loose-envify": "^1.3.1", - "path-to-regexp": "^1.7.0", - "prop-types": "^15.6.2", - "react-is": "^16.6.0", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0" - }, - "dependencies": { - "react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - } - } - }, - "react-router-config": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/react-router-config/-/react-router-config-5.1.1.tgz", - "integrity": "sha512-DuanZjaD8mQp1ppHjgnnUnyOlqYXZVjnov/JzFhjLEwd3Z4dYjMSnqrEzzGThH47vpCOqPPwJM2FtthLeJ8Pbg==", - "requires": { - "@babel/runtime": "^7.1.2" - } - }, - "react-router-dom": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.3.4.tgz", - "integrity": "sha512-m4EqFMHv/Ih4kpcBCONHbkT68KoAeHN4p3lAGoNryfHi0dMy0kCzEZakiKRsvg5wHZ/JLrLW8o8KomWiz/qbYQ==", - "requires": { - "@babel/runtime": "^7.12.13", - "history": "^4.9.0", - "loose-envify": "^1.3.1", - "prop-types": "^15.6.2", - "react-router": "5.3.4", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0" - } - }, - "readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "requires": { - "picomatch": "^2.2.1" - } - }, - "reading-time": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/reading-time/-/reading-time-1.5.0.tgz", - "integrity": "sha512-onYyVhBNr4CmAxFsKS7bz+uTLRakypIe4R+5A824vBSkQy/hB3fZepoVEf8OVAxzLvK+H/jm9TzpI3ETSm64Kg==" - }, - "rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", - "requires": { - "resolve": "^1.1.6" - } - }, - "recursive-readdir": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", - "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", - "requires": { - "minimatch": "^3.0.5" - } - }, - "regenerate": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" - }, - "regenerate-unicode-properties": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz", - "integrity": "sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==", - "requires": { - "regenerate": "^1.4.2" - } - }, - "regenerator-runtime": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", - "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" - }, - "regenerator-transform": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", - "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", - "requires": { - "@babel/runtime": "^7.8.4" - } - }, - "regexpu-core": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", - "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", - "requires": { - "@babel/regjsgen": "^0.8.0", - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.1.0", - "regjsparser": "^0.9.1", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.1.0" - } - }, - "registry-auth-token": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.0.2.tgz", - "integrity": "sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ==", - "requires": { - "@pnpm/npm-conf": "^2.1.0" - } - }, - "registry-url": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-6.0.1.tgz", - "integrity": "sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==", - "requires": { - "rc": "1.2.8" - } - }, - "regjsparser": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", - "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", - "requires": { - "jsesc": "~0.5.0" - }, - "dependencies": { - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==" - } - } - }, - "rehype-raw": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz", - "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==", - "requires": { - "@types/hast": "^3.0.0", - "hast-util-raw": "^9.0.0", - "vfile": "^6.0.0" - } - }, - "relateurl": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", - "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==" - }, - "remark-directive": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/remark-directive/-/remark-directive-3.0.0.tgz", - "integrity": "sha512-l1UyWJ6Eg1VPU7Hm/9tt0zKtReJQNOA4+iDMAxTyZNWnJnFlbS/7zhiel/rogTLQ2vMYwDzSJa4BiVNqGlqIMA==", - "requires": { - "@types/mdast": "^4.0.0", - "mdast-util-directive": "^3.0.0", - "micromark-extension-directive": "^3.0.0", - "unified": "^11.0.0" - } - }, - "remark-emoji": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/remark-emoji/-/remark-emoji-4.0.1.tgz", - "integrity": "sha512-fHdvsTR1dHkWKev9eNyhTo4EFwbUvJ8ka9SgeWkMPYFX4WoI7ViVBms3PjlQYgw5TLvNQso3GUB/b/8t3yo+dg==", - "requires": { - "@types/mdast": "^4.0.2", - "emoticon": "^4.0.1", - "mdast-util-find-and-replace": "^3.0.1", - "node-emoji": "^2.1.0", - "unified": "^11.0.4" - } - }, - "remark-frontmatter": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/remark-frontmatter/-/remark-frontmatter-5.0.0.tgz", - "integrity": "sha512-XTFYvNASMe5iPN0719nPrdItC9aU0ssC4v14mH1BCi1u0n1gAocqcujWUrByftZTbLhRtiKRyjYTSIOcr69UVQ==", - "requires": { - "@types/mdast": "^4.0.0", - "mdast-util-frontmatter": "^2.0.0", - "micromark-extension-frontmatter": "^2.0.0", - "unified": "^11.0.0" - } - }, - "remark-gfm": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.0.tgz", - "integrity": "sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA==", - "requires": { - "@types/mdast": "^4.0.0", - "mdast-util-gfm": "^3.0.0", - "micromark-extension-gfm": "^3.0.0", - "remark-parse": "^11.0.0", - "remark-stringify": "^11.0.0", - "unified": "^11.0.0" - } - }, - "remark-mdx": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-3.0.1.tgz", - "integrity": "sha512-3Pz3yPQ5Rht2pM5R+0J2MrGoBSrzf+tJG94N+t/ilfdh8YLyyKYtidAYwTveB20BoHAcwIopOUqhcmh2F7hGYA==", - "requires": { - "mdast-util-mdx": "^3.0.0", - "micromark-extension-mdxjs": "^3.0.0" - } - }, - "remark-parse": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", - "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", - "requires": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unified": "^11.0.0" - } - }, - "remark-rehype": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.0.tgz", - "integrity": "sha512-z3tJrAs2kIs1AqIIy6pzHmAHlF1hWQ+OdY4/hv+Wxe35EhyLKcajL33iUEn3ScxtFox9nUvRufR/Zre8Q08H/g==", - "requires": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "mdast-util-to-hast": "^13.0.0", - "unified": "^11.0.0", - "vfile": "^6.0.0" - } - }, - "remark-stringify": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", - "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", - "requires": { - "@types/mdast": "^4.0.0", - "mdast-util-to-markdown": "^2.0.0", - "unified": "^11.0.0" - } - }, - "renderkid": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", - "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", - "requires": { - "css-select": "^4.1.3", - "dom-converter": "^0.2.0", - "htmlparser2": "^6.1.0", - "lodash": "^4.17.21", - "strip-ansi": "^6.0.1" - }, - "dependencies": { - "css-select": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", - "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", - "requires": { - "boolbase": "^1.0.0", - "css-what": "^6.0.1", - "domhandler": "^4.3.1", - "domutils": "^2.8.0", - "nth-check": "^2.0.1" - } - }, - "dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", - "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - } - }, - "domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "requires": { - "domelementtype": "^2.2.0" - } - }, - "domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "requires": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - } - }, - "entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" - }, - "htmlparser2": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", - "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", - "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^4.0.0", - "domutils": "^2.5.2", - "entities": "^2.0.0" - } - } - } - }, - "require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" - }, - "require-like": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz", - "integrity": "sha512-oyrU88skkMtDdauHDuKVrgR+zuItqr6/c//FXzvmxRGMexSDc6hNvJInGW3LL46n+8b50RykrvwSUIIQH2LQ5A==" - }, - "requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" - }, - "resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "requires": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "resolve-alpn": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", - "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==" - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" - }, - "resolve-pathname": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz", - "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==" - }, - "responselike": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", - "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", - "requires": { - "lowercase-keys": "^3.0.0" - } - }, - "retry": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==" - }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "requires": { - "glob": "^7.1.3" - } - }, - "robust-predicates": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", - "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==" - }, - "rtl-detect": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/rtl-detect/-/rtl-detect-1.1.2.tgz", - "integrity": "sha512-PGMBq03+TTG/p/cRB7HCLKJ1MgDIi07+QU1faSjiYRfmY5UsAttV9Hs08jDAHVwcOwmVLcSJkpwyfXszVjWfIQ==" - }, - "rtlcss": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-4.1.1.tgz", - "integrity": "sha512-/oVHgBtnPNcggP2aVXQjSy6N1mMAfHg4GSag0QtZBlD5bdDgAHwr4pydqJGd+SUCu9260+Pjqbjwtvu7EMH1KQ==", - "requires": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0", - "postcss": "^8.4.21", - "strip-json-comments": "^3.1.1" - } - }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "requires": { - "queue-microtask": "^1.2.2" - } - }, - "rw": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", - "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" - }, - "sade": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", - "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", - "requires": { - "mri": "^1.1.0" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "sax": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", - "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==" - }, - "scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", - "requires": { - "loose-envify": "^1.1.0" - } - }, - "schema-utils": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", - "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", - "requires": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - } - }, - "search-insights": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.14.0.tgz", - "integrity": "sha512-OLN6MsPMCghDOqlCtsIsYgtsC0pnwVTyT9Mu6A3ewOj1DxvzZF6COrn2g86E/c05xbktB0XN04m/t1Z+n+fTGw==", - "peer": true - }, - "section-matter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", - "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", - "requires": { - "extend-shallow": "^2.0.1", - "kind-of": "^6.0.0" - } - }, - "select-hose": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", - "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==" - }, - "selfsigned": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", - "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", - "requires": { - "@types/node-forge": "^1.3.0", - "node-forge": "^1" - } - }, - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "requires": { - "lru-cache": "^6.0.0" - }, - "dependencies": { - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "requires": { - "yallist": "^4.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - } - } - }, - "semver-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-4.0.0.tgz", - "integrity": "sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==", - "requires": { - "semver": "^7.3.5" - } - }, - "send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", - "requires": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - }, - "dependencies": { - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - } - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" - } - } - }, - "serialize-javascript": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", - "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", - "requires": { - "randombytes": "^2.1.0" - } - }, - "serve-handler": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.5.tgz", - "integrity": "sha512-ijPFle6Hwe8zfmBxJdE+5fta53fdIY0lHISJvuikXB3VYFafRjMRpOffSPvCYsbKyBA7pvy9oYr/BT1O3EArlg==", - "requires": { - "bytes": "3.0.0", - "content-disposition": "0.5.2", - "fast-url-parser": "1.1.3", - "mime-types": "2.1.18", - "minimatch": "3.1.2", - "path-is-inside": "1.0.2", - "path-to-regexp": "2.2.1", - "range-parser": "1.2.0" - }, - "dependencies": { - "path-to-regexp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.2.1.tgz", - "integrity": "sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ==" - } - } - }, - "serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", - "requires": { - "accepts": "~1.3.4", - "batch": "0.6.1", - "debug": "2.6.9", - "escape-html": "~1.0.3", - "http-errors": "~1.6.2", - "mime-types": "~2.1.17", - "parseurl": "~1.3.2" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==" - }, - "http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" - }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==" - } - } - }, - "serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.18.0" - } - }, - "set-function-length": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", - "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", - "requires": { - "define-data-property": "^1.1.1", - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" - } - }, - "setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, - "shallow-clone": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", - "requires": { - "kind-of": "^6.0.2" - } - }, - "shallowequal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", - "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" - }, - "shell-quote": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", - "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==" - }, - "shelljs": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", - "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", - "requires": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - } - }, - "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - } - }, - "signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" - }, - "sirv": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.3.tgz", - "integrity": "sha512-O9jm9BsID1P+0HOi81VpXPoDxYP374pkOLzACAoyUQ/3OUVndNpsz6wMnY2z+yOxzbllCKZrM+9QrWsv4THnyA==", - "requires": { - "@polka/url": "^1.0.0-next.20", - "mrmime": "^1.0.0", - "totalist": "^3.0.0" - } - }, - "sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" - }, - "sitemap": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-7.1.2.tgz", - "integrity": "sha512-ARCqzHJ0p4gWt+j7NlU5eDlIO9+Rkr/JhPFZKKQ1l5GCus7rJH4UdrlVAh0xC/gDS/Qir2UMxqYNHtsKr2rpCw==", - "requires": { - "@types/node": "^17.0.5", - "@types/sax": "^1.2.1", - "arg": "^5.0.0", - "sax": "^1.2.4" - }, - "dependencies": { - "@types/node": { - "version": "17.0.45", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", - "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==" - } - } - }, - "skin-tone": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/skin-tone/-/skin-tone-2.0.0.tgz", - "integrity": "sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==", - "requires": { - "unicode-emoji-modifier-base": "^1.0.0" - } - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" - }, - "snake-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", - "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", - "requires": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "sockjs": { - "version": "0.3.24", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", - "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", - "requires": { - "faye-websocket": "^0.11.3", - "uuid": "^8.3.2", - "websocket-driver": "^0.7.4" - } - }, - "sort-css-media-queries": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/sort-css-media-queries/-/sort-css-media-queries-2.2.0.tgz", - "integrity": "sha512-0xtkGhWCC9MGt/EzgnvbbbKhqWjl1+/rncmhTh5qCpbYguXh6S/qwePfv/JQ8jePXXmqingylxoC49pCkSPIbA==" - }, - "source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==" - }, - "source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==" - }, - "source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } - } - }, - "space-separated-tokens": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", - "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==" - }, - "spdy": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", - "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", - "requires": { - "debug": "^4.1.0", - "handle-thing": "^2.0.0", - "http-deceiver": "^1.2.7", - "select-hose": "^2.0.0", - "spdy-transport": "^3.0.0" - } - }, - "spdy-transport": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", - "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", - "requires": { - "debug": "^4.1.0", - "detect-node": "^2.0.4", - "hpack.js": "^2.1.6", - "obuf": "^1.1.2", - "readable-stream": "^3.0.6", - "wbuf": "^1.7.3" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" - }, - "srcset": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/srcset/-/srcset-4.0.0.tgz", - "integrity": "sha512-wvLeHgcVHKO8Sc/H/5lkGreJQVeYMm9rlmt8PuR1xE31rIuXhuzznUUqAt8MqLhB3MqJdFzlNAfpcWnxiFUcPw==" - }, - "static-browser-server": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/static-browser-server/-/static-browser-server-1.0.3.tgz", - "integrity": "sha512-ZUyfgGDdFRbZGGJQ1YhiM930Yczz5VlbJObrQLlk24+qNHVQx4OlLcYswEUo3bIyNAbQUIUR9Yr5/Hqjzqb4zA==", - "requires": { - "@open-draft/deferred-promise": "^2.1.0", - "dotenv": "^16.0.3", - "mime-db": "^1.52.0", - "outvariant": "^1.3.0" - }, - "dependencies": { - "mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" - } - } - }, - "statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" - }, - "std-env": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.4.3.tgz", - "integrity": "sha512-f9aPhy8fYBuMN+sNfakZV18U39PbalgjXG3lLB9WkaYTxijru61wb57V9wxxNthXM5Sd88ETBWi29qLAsHO52Q==" - }, - "strict-event-emitter": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.4.6.tgz", - "integrity": "sha512-12KWeb+wixJohmnwNFerbyiBrAlq5qJLwIt38etRtKtmmHyDSoGlIqFE9wx+4IwG0aDjI7GV8tc8ZccjWZZtTg==" - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "requires": { - "safe-buffer": "~5.2.0" - } - }, - "string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "requires": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==" - }, - "strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "requires": { - "ansi-regex": "^6.0.1" - } - } - } - }, - "stringify-entities": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.3.tgz", - "integrity": "sha512-BP9nNHMhhfcMbiuQKCqMjhDP5yBCAxsPu4pHFFzJ6Alo9dZgY4VLDPutXqIjpRiMoKdp7Av85Gr73Q5uH9k7+g==", - "requires": { - "character-entities-html4": "^2.0.0", - "character-entities-legacy": "^3.0.0" - } - }, - "stringify-object": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", - "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", - "requires": { - "get-own-enumerable-property-symbols": "^3.0.0", - "is-obj": "^1.0.1", - "is-regexp": "^1.0.0" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-bom-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", - "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==" - }, - "strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==" - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" - }, - "style-mod": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.0.tgz", - "integrity": "sha512-Ca5ib8HrFn+f+0n4N4ScTIA9iTOQ7MaGS1ylHcoVqW9J7w2w8PzN6g9gKmTYgGEBH8e120+RCmhpje6jC5uGWA==" - }, - "style-to-object": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.4.4.tgz", - "integrity": "sha512-HYNoHZa2GorYNyqiCaBgsxvcJIn7OHq6inEga+E6Ke3m5JkoqpQbnFssk4jwe+K7AhGa2fcha4wSOf1Kn01dMg==", - "requires": { - "inline-style-parser": "0.1.1" - } - }, - "stylehacks": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-6.1.1.tgz", - "integrity": "sha512-gSTTEQ670cJNoaeIp9KX6lZmm8LJ3jPB5yJmX8Zq/wQxOsAFXV3qjWzHas3YYk1qesuVIyYWWUpZ0vSE/dTSGg==", - "requires": { - "browserslist": "^4.23.0", - "postcss-selector-parser": "^6.0.16" - } - }, - "stylis": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.0.tgz", - "integrity": "sha512-E87pIogpwUsUwXw7dNyU4QDjdgVMy52m+XEOPEKUn161cCzWjjhPSQhByfd1CcNvrOLnXQ6OnnZDwnJrz/Z4YQ==" - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "requires": { - "has-flag": "^4.0.0" - } - }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" - }, - "svg-parser": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", - "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==" - }, - "svgo": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz", - "integrity": "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==", - "requires": { - "@trysound/sax": "0.2.0", - "commander": "^7.2.0", - "css-select": "^5.1.0", - "css-tree": "^2.3.1", - "css-what": "^6.1.0", - "csso": "^5.0.5", - "picocolors": "^1.0.0" - }, - "dependencies": { - "commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==" - } - } - }, - "tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==" - }, - "terser": { - "version": "5.24.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.24.0.tgz", - "integrity": "sha512-ZpGR4Hy3+wBEzVEnHvstMvqpD/nABNelQn/z2r0fjVWGQsN3bpOLzQlqDxmb4CDZnXq5lpjnQ+mHQLAOpfM5iw==", - "requires": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.8.2", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "dependencies": { - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" - } - } - }, - "terser-webpack-plugin": { - "version": "5.3.9", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz", - "integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==", - "requires": { - "@jridgewell/trace-mapping": "^0.3.17", - "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.1", - "terser": "^5.16.8" - }, - "dependencies": { - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "requires": {} - }, - "jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "requires": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - } - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "requires": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - } - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" - }, - "thunky": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", - "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" - }, - "tiny-invariant": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", - "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==" - }, - "tiny-warning": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", - "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==" - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "requires": { - "is-number": "^7.0.0" - } - }, - "toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" - }, - "totalist": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", - "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==" - }, - "trim-lines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", - "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==" - }, - "trough": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", - "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==" - }, - "ts-dedent": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", - "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==" - }, - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" - }, - "type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" - }, - "type-fest": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", - "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==" - }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "dependencies": { - "mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" - }, - "mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "requires": { - "mime-db": "1.52.0" - } - } - } - }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "requires": { - "is-typedarray": "^1.0.0" - } - }, - "typescript": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", - "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==" - }, - "undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" - }, - "unicode-canonical-property-names-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", - "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==" - }, - "unicode-emoji-modifier-base": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unicode-emoji-modifier-base/-/unicode-emoji-modifier-base-1.0.0.tgz", - "integrity": "sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==" - }, - "unicode-match-property-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", - "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", - "requires": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" - } - }, - "unicode-match-property-value-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", - "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==" - }, - "unicode-property-aliases-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", - "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==" - }, - "unified": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.4.tgz", - "integrity": "sha512-apMPnyLjAX+ty4OrNap7yumyVAMlKx5IWU2wlzzUdYJO9A8f1p9m/gywF/GM2ZDFcjQPrx59Mc90KwmxsoklxQ==", - "requires": { - "@types/unist": "^3.0.0", - "bail": "^2.0.0", - "devlop": "^1.0.0", - "extend": "^3.0.0", - "is-plain-obj": "^4.0.0", - "trough": "^2.0.0", - "vfile": "^6.0.0" - } - }, - "unique-string": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz", - "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==", - "requires": { - "crypto-random-string": "^4.0.0" - } - }, - "unist-util-is": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", - "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", - "requires": { - "@types/unist": "^3.0.0" - } - }, - "unist-util-position": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", - "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", - "requires": { - "@types/unist": "^3.0.0" - } - }, - "unist-util-position-from-estree": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unist-util-position-from-estree/-/unist-util-position-from-estree-2.0.0.tgz", - "integrity": "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==", - "requires": { - "@types/unist": "^3.0.0" - } - }, - "unist-util-remove-position": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz", - "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==", - "requires": { - "@types/unist": "^3.0.0", - "unist-util-visit": "^5.0.0" - } - }, - "unist-util-stringify-position": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", - "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", - "requires": { - "@types/unist": "^3.0.0" - } - }, - "unist-util-visit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", - "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", - "requires": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - } - }, - "unist-util-visit-parents": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", - "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", - "requires": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - } - }, - "universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==" - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" - }, - "update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", - "requires": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - } - }, - "update-notifier": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-6.0.2.tgz", - "integrity": "sha512-EDxhTEVPZZRLWYcJ4ZXjGFN0oP7qYvbXWzEgRm/Yql4dHX5wDbvh89YHP6PK1lzZJYrMtXUuZZz8XGK+U6U1og==", - "requires": { - "boxen": "^7.0.0", - "chalk": "^5.0.1", - "configstore": "^6.0.0", - "has-yarn": "^3.0.0", - "import-lazy": "^4.0.0", - "is-ci": "^3.0.1", - "is-installed-globally": "^0.4.0", - "is-npm": "^6.0.0", - "is-yarn-global": "^0.4.0", - "latest-version": "^7.0.0", - "pupa": "^3.1.0", - "semver": "^7.3.7", - "semver-diff": "^4.0.0", - "xdg-basedir": "^5.1.0" - }, - "dependencies": { - "boxen": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.1.1.tgz", - "integrity": "sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==", - "requires": { - "ansi-align": "^3.0.1", - "camelcase": "^7.0.1", - "chalk": "^5.2.0", - "cli-boxes": "^3.0.0", - "string-width": "^5.1.2", - "type-fest": "^2.13.0", - "widest-line": "^4.0.1", - "wrap-ansi": "^8.1.0" - } - }, - "camelcase": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz", - "integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==" - }, - "chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==" - } - } - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "requires": { - "punycode": "^2.1.0" - }, - "dependencies": { - "punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==" - } - } - }, - "url-loader": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz", - "integrity": "sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==", - "requires": { - "loader-utils": "^2.0.0", - "mime-types": "^2.1.27", - "schema-utils": "^3.0.0" - }, - "dependencies": { - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "requires": {} - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" - }, - "mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "requires": { - "mime-db": "1.52.0" - } - }, - "schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "requires": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - } - } - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "utila": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", - "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==" - }, - "utility-types": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz", - "integrity": "sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==" - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" - }, - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" - }, - "uvu": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.6.tgz", - "integrity": "sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==", - "requires": { - "dequal": "^2.0.0", - "diff": "^5.0.0", - "kleur": "^4.0.3", - "sade": "^1.7.3" - }, - "dependencies": { - "kleur": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", - "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==" - } - } - }, - "value-equal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", - "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==" - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" - }, - "vfile": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.1.tgz", - "integrity": "sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==", - "requires": { - "@types/unist": "^3.0.0", - "unist-util-stringify-position": "^4.0.0", - "vfile-message": "^4.0.0" - } - }, - "vfile-location": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.2.tgz", - "integrity": "sha512-NXPYyxyBSH7zB5U6+3uDdd6Nybz6o6/od9rk8bp9H8GR3L+cm/fC0uUTbqBmUTnMCUDslAGBOIKNfvvb+gGlDg==", - "requires": { - "@types/unist": "^3.0.0", - "vfile": "^6.0.0" - } - }, - "vfile-message": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", - "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", - "requires": { - "@types/unist": "^3.0.0", - "unist-util-stringify-position": "^4.0.0" - } - }, - "w3c-keyname": { - "version": "2.2.8", - "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", - "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==" - }, - "watchpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", - "requires": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - } - }, - "wbuf": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", - "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", - "requires": { - "minimalistic-assert": "^1.0.0" - } - }, - "web-namespaces": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", - "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==" - }, - "web-worker": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.2.0.tgz", - "integrity": "sha512-PgF341avzqyx60neE9DD+XS26MMNMoUQRz9NOZwW32nPQrF6p77f1htcnjBSEV8BGMKZ16choqUG4hyI0Hx7mA==" - }, - "webpack": { - "version": "5.89.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.89.0.tgz", - "integrity": "sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==", - "requires": { - "@types/eslint-scope": "^3.7.3", - "@types/estree": "^1.0.0", - "@webassemblyjs/ast": "^1.11.5", - "@webassemblyjs/wasm-edit": "^1.11.5", - "@webassemblyjs/wasm-parser": "^1.11.5", - "acorn": "^8.7.1", - "acorn-import-assertions": "^1.9.0", - "browserslist": "^4.14.5", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.15.0", - "es-module-lexer": "^1.2.1", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.2.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.7", - "watchpack": "^2.4.0", - "webpack-sources": "^3.2.3" - }, - "dependencies": { - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "requires": {} - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" - }, - "mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "requires": { - "mime-db": "1.52.0" - } - }, - "schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "requires": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - } - } - } - }, - "webpack-bundle-analyzer": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.9.1.tgz", - "integrity": "sha512-jnd6EoYrf9yMxCyYDPj8eutJvtjQNp8PHmni/e/ulydHBWhT5J3menXt3HEkScsu9YqMAcG4CfFjs3rj5pVU1w==", - "requires": { - "@discoveryjs/json-ext": "0.5.7", - "acorn": "^8.0.4", - "acorn-walk": "^8.0.0", - "commander": "^7.2.0", - "escape-string-regexp": "^4.0.0", - "gzip-size": "^6.0.0", - "is-plain-object": "^5.0.0", - "lodash.debounce": "^4.0.8", - "lodash.escape": "^4.0.1", - "lodash.flatten": "^4.4.0", - "lodash.invokemap": "^4.6.0", - "lodash.pullall": "^4.2.0", - "lodash.uniqby": "^4.7.0", - "opener": "^1.5.2", - "picocolors": "^1.0.0", - "sirv": "^2.0.3", - "ws": "^7.3.1" - }, - "dependencies": { - "commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==" - } - } - }, - "webpack-dev-middleware": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", - "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==", - "requires": { - "colorette": "^2.0.10", - "memfs": "^3.4.3", - "mime-types": "^2.1.31", - "range-parser": "^1.2.1", - "schema-utils": "^4.0.0" - }, - "dependencies": { - "mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" - }, - "mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "requires": { - "mime-db": "1.52.0" - } - }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" - } - } - }, - "webpack-dev-server": { - "version": "4.15.1", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.1.tgz", - "integrity": "sha512-5hbAst3h3C3L8w6W4P96L5vaV0PxSmJhxZvWKYIdgxOQm8pNZ5dEOmmSLBVpP85ReeyRt6AS1QJNyo/oFFPeVA==", - "requires": { - "@types/bonjour": "^3.5.9", - "@types/connect-history-api-fallback": "^1.3.5", - "@types/express": "^4.17.13", - "@types/serve-index": "^1.9.1", - "@types/serve-static": "^1.13.10", - "@types/sockjs": "^0.3.33", - "@types/ws": "^8.5.5", - "ansi-html-community": "^0.0.8", - "bonjour-service": "^1.0.11", - "chokidar": "^3.5.3", - "colorette": "^2.0.10", - "compression": "^1.7.4", - "connect-history-api-fallback": "^2.0.0", - "default-gateway": "^6.0.3", - "express": "^4.17.3", - "graceful-fs": "^4.2.6", - "html-entities": "^2.3.2", - "http-proxy-middleware": "^2.0.3", - "ipaddr.js": "^2.0.1", - "launch-editor": "^2.6.0", - "open": "^8.0.9", - "p-retry": "^4.5.0", - "rimraf": "^3.0.2", - "schema-utils": "^4.0.0", - "selfsigned": "^2.1.1", - "serve-index": "^1.9.1", - "sockjs": "^0.3.24", - "spdy": "^4.0.2", - "webpack-dev-middleware": "^5.3.1", - "ws": "^8.13.0" - }, - "dependencies": { - "ws": { - "version": "8.14.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz", - "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==", - "requires": {} - } - } - }, - "webpack-merge": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", - "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", - "requires": { - "clone-deep": "^4.0.1", - "flat": "^5.0.2", - "wildcard": "^2.0.0" - } - }, - "webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==" - }, - "webpackbar": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/webpackbar/-/webpackbar-5.0.2.tgz", - "integrity": "sha512-BmFJo7veBDgQzfWXl/wwYXr/VFus0614qZ8i9znqcl9fnEdiVkdbi0TedLQ6xAK92HZHDJ0QmyQ0fmuZPAgCYQ==", - "requires": { - "chalk": "^4.1.0", - "consola": "^2.15.3", - "pretty-time": "^1.1.0", - "std-env": "^3.0.1" - } - }, - "websocket-driver": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", - "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", - "requires": { - "http-parser-js": ">=0.5.1", - "safe-buffer": ">=5.1.0", - "websocket-extensions": ">=0.1.1" - } - }, - "websocket-extensions": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", - "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==" - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "requires": { - "isexe": "^2.0.0" - } - }, - "widest-line": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", - "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==", - "requires": { - "string-width": "^5.0.1" - } - }, - "wildcard": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", - "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==" - }, - "wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "requires": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==" - }, - "ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==" - }, - "strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "requires": { - "ansi-regex": "^6.0.1" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, - "write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "requires": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", - "requires": {} - }, - "xdg-basedir": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-5.1.0.tgz", - "integrity": "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==" - }, - "xml-js": { - "version": "1.6.11", - "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz", - "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==", - "requires": { - "sax": "^1.2.4" - } - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - }, - "yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" - }, - "yocto-queue": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", - "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==" - }, - "zwitch": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", - "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==" - } - } -} +{ + "name": "alova-website", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "alova-website", + "version": "1.0.0", + "dependencies": { + "@codesandbox/sandpack-react": "^2.9.0", + "@codesandbox/sandpack-themes": "^2.0.21", + "@docusaurus/core": "^3.4.0", + "@docusaurus/preset-classic": "^3.4.0", + "@docusaurus/theme-mermaid": "^3.4.0", + "@docusaurus/tsconfig": "^3.4.0", + "@mdx-js/react": "^3.0.0", + "clsx": "^2.0.0", + "docusaurus-plugin-sass": "^0.2.5", + "prism-react-renderer": "^2.1.0", + "raw-loader": "^4.0.2", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "sass": "^1.77.6" + }, + "devDependencies": { + "@docusaurus/module-type-aliases": "^3.1.1", + "prettier": "^2.8.8", + "typescript": "^5.2.2" + }, + "engines": { + "node": ">=16.14" + } + }, + "node_modules/@algolia/autocomplete-core": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.9.3.tgz", + "integrity": "sha512-009HdfugtGCdC4JdXUbVJClA0q0zh24yyePn+KUGk3rP7j8FEe/m5Yo/z65gn6nP/cM39PxpzqKrL7A6fP6PPw==", + "dependencies": { + "@algolia/autocomplete-plugin-algolia-insights": "1.9.3", + "@algolia/autocomplete-shared": "1.9.3" + } + }, + "node_modules/@algolia/autocomplete-plugin-algolia-insights": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.9.3.tgz", + "integrity": "sha512-a/yTUkcO/Vyy+JffmAnTWbr4/90cLzw+CC3bRbhnULr/EM0fGNvM13oQQ14f2moLMcVDyAx/leczLlAOovhSZg==", + "dependencies": { + "@algolia/autocomplete-shared": "1.9.3" + }, + "peerDependencies": { + "search-insights": ">= 1 < 3" + } + }, + "node_modules/@algolia/autocomplete-preset-algolia": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.9.3.tgz", + "integrity": "sha512-d4qlt6YmrLMYy95n5TB52wtNDr6EgAIPH81dvvvW8UmuWRgxEtY0NJiPwl/h95JtG2vmRM804M0DSwMCNZlzRA==", + "dependencies": { + "@algolia/autocomplete-shared": "1.9.3" + }, + "peerDependencies": { + "@algolia/client-search": ">= 4.9.1 < 6", + "algoliasearch": ">= 4.9.1 < 6" + } + }, + "node_modules/@algolia/autocomplete-shared": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.9.3.tgz", + "integrity": "sha512-Wnm9E4Ye6Rl6sTTqjoymD+l8DjSTHsHboVRYrKgEt8Q7UHm9nYbqhN/i0fhUYA3OAEH7WA8x3jfpnmJm3rKvaQ==", + "peerDependencies": { + "@algolia/client-search": ">= 4.9.1 < 6", + "algoliasearch": ">= 4.9.1 < 6" + } + }, + "node_modules/@algolia/cache-browser-local-storage": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.23.3.tgz", + "integrity": "sha512-vRHXYCpPlTDE7i6UOy2xE03zHF2C8MEFjPN2v7fRbqVpcOvAUQK81x3Kc21xyb5aSIpYCjWCZbYZuz8Glyzyyg==", + "dependencies": { + "@algolia/cache-common": "4.23.3" + } + }, + "node_modules/@algolia/cache-common": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.23.3.tgz", + "integrity": "sha512-h9XcNI6lxYStaw32pHpB1TMm0RuxphF+Ik4o7tcQiodEdpKK+wKufY6QXtba7t3k8eseirEMVB83uFFF3Nu54A==" + }, + "node_modules/@algolia/cache-in-memory": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.23.3.tgz", + "integrity": "sha512-yvpbuUXg/+0rbcagxNT7un0eo3czx2Uf0y4eiR4z4SD7SiptwYTpbuS0IHxcLHG3lq22ukx1T6Kjtk/rT+mqNg==", + "dependencies": { + "@algolia/cache-common": "4.23.3" + } + }, + "node_modules/@algolia/client-account": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.23.3.tgz", + "integrity": "sha512-hpa6S5d7iQmretHHF40QGq6hz0anWEHGlULcTIT9tbUssWUriN9AUXIFQ8Ei4w9azD0hc1rUok9/DeQQobhQMA==", + "dependencies": { + "@algolia/client-common": "4.23.3", + "@algolia/client-search": "4.23.3", + "@algolia/transporter": "4.23.3" + } + }, + "node_modules/@algolia/client-analytics": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.23.3.tgz", + "integrity": "sha512-LBsEARGS9cj8VkTAVEZphjxTjMVCci+zIIiRhpFun9jGDUlS1XmhCW7CTrnaWeIuCQS/2iPyRqSy1nXPjcBLRA==", + "dependencies": { + "@algolia/client-common": "4.23.3", + "@algolia/client-search": "4.23.3", + "@algolia/requester-common": "4.23.3", + "@algolia/transporter": "4.23.3" + } + }, + "node_modules/@algolia/client-common": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.23.3.tgz", + "integrity": "sha512-l6EiPxdAlg8CYhroqS5ybfIczsGUIAC47slLPOMDeKSVXYG1n0qGiz4RjAHLw2aD0xzh2EXZ7aRguPfz7UKDKw==", + "dependencies": { + "@algolia/requester-common": "4.23.3", + "@algolia/transporter": "4.23.3" + } + }, + "node_modules/@algolia/client-personalization": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.23.3.tgz", + "integrity": "sha512-3E3yF3Ocr1tB/xOZiuC3doHQBQ2zu2MPTYZ0d4lpfWads2WTKG7ZzmGnsHmm63RflvDeLK/UVx7j2b3QuwKQ2g==", + "dependencies": { + "@algolia/client-common": "4.23.3", + "@algolia/requester-common": "4.23.3", + "@algolia/transporter": "4.23.3" + } + }, + "node_modules/@algolia/client-search": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.23.3.tgz", + "integrity": "sha512-P4VAKFHqU0wx9O+q29Q8YVuaowaZ5EM77rxfmGnkHUJggh28useXQdopokgwMeYw2XUht49WX5RcTQ40rZIabw==", + "dependencies": { + "@algolia/client-common": "4.23.3", + "@algolia/requester-common": "4.23.3", + "@algolia/transporter": "4.23.3" + } + }, + "node_modules/@algolia/events": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@algolia/events/-/events-4.0.1.tgz", + "integrity": "sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ==" + }, + "node_modules/@algolia/logger-common": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.23.3.tgz", + "integrity": "sha512-y9kBtmJwiZ9ZZ+1Ek66P0M68mHQzKRxkW5kAAXYN/rdzgDN0d2COsViEFufxJ0pb45K4FRcfC7+33YB4BLrZ+g==" + }, + "node_modules/@algolia/logger-console": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.23.3.tgz", + "integrity": "sha512-8xoiseoWDKuCVnWP8jHthgaeobDLolh00KJAdMe9XPrWPuf1by732jSpgy2BlsLTaT9m32pHI8CRfrOqQzHv3A==", + "dependencies": { + "@algolia/logger-common": "4.23.3" + } + }, + "node_modules/@algolia/recommend": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-4.23.3.tgz", + "integrity": "sha512-9fK4nXZF0bFkdcLBRDexsnGzVmu4TSYZqxdpgBW2tEyfuSSY54D4qSRkLmNkrrz4YFvdh2GM1gA8vSsnZPR73w==", + "dependencies": { + "@algolia/cache-browser-local-storage": "4.23.3", + "@algolia/cache-common": "4.23.3", + "@algolia/cache-in-memory": "4.23.3", + "@algolia/client-common": "4.23.3", + "@algolia/client-search": "4.23.3", + "@algolia/logger-common": "4.23.3", + "@algolia/logger-console": "4.23.3", + "@algolia/requester-browser-xhr": "4.23.3", + "@algolia/requester-common": "4.23.3", + "@algolia/requester-node-http": "4.23.3", + "@algolia/transporter": "4.23.3" + } + }, + "node_modules/@algolia/requester-browser-xhr": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.23.3.tgz", + "integrity": "sha512-jDWGIQ96BhXbmONAQsasIpTYWslyjkiGu0Quydjlowe+ciqySpiDUrJHERIRfELE5+wFc7hc1Q5hqjGoV7yghw==", + "dependencies": { + "@algolia/requester-common": "4.23.3" + } + }, + "node_modules/@algolia/requester-common": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.23.3.tgz", + "integrity": "sha512-xloIdr/bedtYEGcXCiF2muajyvRhwop4cMZo+K2qzNht0CMzlRkm8YsDdj5IaBhshqfgmBb3rTg4sL4/PpvLYw==" + }, + "node_modules/@algolia/requester-node-http": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.23.3.tgz", + "integrity": "sha512-zgu++8Uj03IWDEJM3fuNl34s746JnZOWn1Uz5taV1dFyJhVM/kTNw9Ik7YJWiUNHJQXcaD8IXD1eCb0nq/aByA==", + "dependencies": { + "@algolia/requester-common": "4.23.3" + } + }, + "node_modules/@algolia/transporter": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.23.3.tgz", + "integrity": "sha512-Wjl5gttqnf/gQKJA+dafnD0Y6Yw97yvfY8R9h0dQltX1GXTgNs1zWgvtWW0tHl1EgMdhAyw189uWiZMnL3QebQ==", + "dependencies": { + "@algolia/cache-common": "4.23.3", + "@algolia/logger-common": "4.23.3", + "@algolia/requester-common": "4.23.3" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", + "dependencies": { + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/code-frame/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.2.tgz", + "integrity": "sha512-0S9TQMmDHlqAZ2ITT95irXKfxN9bncq8ZCoJhun3nHL/lLUxd2NKBJYoNGWH7S0hz6fRQwWlAWn/ILM0C70KZQ==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.5.tgz", + "integrity": "sha512-Cwc2XjUrG4ilcfOw4wBAK+enbdgwAcAJCfGUItPBKR7Mjw4aEfAFYrLxeRp4jWgtNIKn3n2AlBOfwwafl+42/g==", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.5", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.23.5", + "@babel/parser": "^7.23.5", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.5", + "@babel/types": "^7.23.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.5.tgz", + "integrity": "sha512-BPssCHrBD+0YrxviOa3QzpqwhNIXKEtOa2jQrm4FlmkC2apYgRnQcmPWiGZDlGxiNtltnUFolMe8497Esry+jA==", + "dependencies": { + "@babel/types": "^7.23.5", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", + "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz", + "integrity": "sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==", + "dependencies": { + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", + "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", + "dependencies": { + "@babel/compat-data": "^7.22.9", + "@babel/helper-validator-option": "^7.22.15", + "browserslist": "^4.21.9", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.15.tgz", + "integrity": "sha512-jKkwA59IXcvSaiK2UN45kKwSC9o+KuoXsBDvHvU/7BecYIp8GQ2UwrVvFgJASUT+hBnwJx6MhvMCuMzwZZ7jlg==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-member-expression-to-functions": "^7.22.15", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz", + "integrity": "sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "regexpu-core": "^5.3.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.3.tgz", + "integrity": "sha512-WBrLmuPP47n7PNwsZ57pqam6G/RGo1vw/87b0Blc53tZNGZ4x7YvZ6HgQe2vo1W/FR20OgjeZuGXzudPiXHFug==", + "dependencies": { + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz", + "integrity": "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==", + "dependencies": { + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", + "dependencies": { + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", + "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.7.tgz", + "integrity": "sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz", + "integrity": "sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-wrap-function": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz", + "integrity": "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-member-expression-to-functions": "^7.22.15", + "@babel/helper-optimise-call-expression": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", + "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", + "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", + "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz", + "integrity": "sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==", + "dependencies": { + "@babel/helper-function-name": "^7.22.5", + "@babel/template": "^7.22.15", + "@babel/types": "^7.22.19" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.5.tgz", + "integrity": "sha512-oO7us8FzTEsG3U6ag9MfdF1iA/7Z6dz+MtFhifZk8C8o453rGJFFWUP1t+ULM9TUIAzC9uxXEiXjOiVMyd7QPg==", + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.5", + "@babel/types": "^7.23.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.5.tgz", + "integrity": "sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ==", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.15.tgz", + "integrity": "sha512-FB9iYlz7rURmRJyXRKEnalYPPdn87H5no108cyuQQyMwlpJ2SJtpIUBI27kdTin956pz+LPypkPVPUTlxOmrsg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.15.tgz", + "integrity": "sha512-Hyph9LseGvAeeXzikV88bczhsrLrIZqDPxO+sSmAunMPaGrBGhfMWzCPYTtiW9t+HzSE2wtV8e5cc5P6r1xMDQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-transform-optional-chaining": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.22.5.tgz", + "integrity": "sha512-rdV97N7KqsRzeNGoWUOK6yUsWarLjE5Su/Snk9IYPU9CwkWHs4t+rTGOvffTR8XGkJMTAdLfO0xVnXm8wugIJg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.22.5.tgz", + "integrity": "sha512-KwvoWDeNKPETmozyFE0P2rOLqh39EoQHNjqizrI5B8Vt0ZNS7M56s7dAiAqbYfiAYOuIzIh96z3iR2ktgu3tEg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", + "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", + "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.22.5.tgz", + "integrity": "sha512-26lTNXoVRdAnsaDXPpvCNUq+OVWEVC6bx7Vvz9rC53F2bagUWW4u4ii2+h8Fejfh7RYqPxn+libeFBBck9muEw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.2.tgz", + "integrity": "sha512-BBYVGxbDVHfoeXbOwcagAkOQAm9NxoTdMGfTqghu1GrvadSaw6iW3Je6IcL5PNOw8VwjxqBECXy50/iCQSY/lQ==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-remap-async-to-generator": "^7.22.20", + "@babel/plugin-syntax-async-generators": "^7.8.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.22.5.tgz", + "integrity": "sha512-b1A8D8ZzE/VhNDoV1MSJTnpKkCG5bJo+19R4o4oy03zM7ws8yEMK755j61Dc3EyvdysbqH5BOOTquJ7ZX9C6vQ==", + "dependencies": { + "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-remap-async-to-generator": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.22.5.tgz", + "integrity": "sha512-tdXZ2UdknEKQWKJP1KMNmuF5Lx3MymtMN/pvA+p/VEkhK8jVcQ1fzSy8KM9qRYhAf2/lV33hoMPKI/xaI9sADA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.0.tgz", + "integrity": "sha512-cOsrbmIOXmf+5YbL99/S49Y3j46k/T16b9ml8bm9lP6N9US5iQ2yBK7gpui1pg0V/WMcXdkfKbTb7HXq9u+v4g==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.22.5.tgz", + "integrity": "sha512-nDkQ0NfkOhPTq8YCLiWNxp1+f9fCobEjCb0n8WdbNUBc4IB5V7P1QnX9IjpSoquKrXF5SKojHleVNs2vGeHCHQ==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.11.tgz", + "integrity": "sha512-GMM8gGmqI7guS/llMFk1bJDkKfn3v3C4KHK9Yg1ey5qcHcOlKb0QvcMrgzvxo+T03/4szNh5lghY+fEC98Kq9g==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.22.11", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.15.tgz", + "integrity": "sha512-VbbC3PGjBdE0wAWDdHM9G8Gm977pnYI0XpqMd6LrKISj8/DJXEsWqgRuTYaNE9Bv0JGhTZUzHDlMk18IpOuoqw==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.9", + "@babel/helper-split-export-declaration": "^7.22.6", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.22.5.tgz", + "integrity": "sha512-4GHWBgRf0krxPX+AaPtgBAlTgTeZmqDynokHOX7aqqAB4tHs3U2Y02zH6ETFdLZGcg9UQSD1WCmkVrE9ErHeOg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/template": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.0.tgz", + "integrity": "sha512-vaMdgNXFkYrB+8lbgniSYWHsgqK5gjaMNcc84bMIOMRLH0L9AqYq3hwMdvnyqj1OPqea8UtjPEuS/DCenah1wg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.22.5.tgz", + "integrity": "sha512-5/Yk9QxCQCl+sOIB1WelKnVRxTJDSAIxtJLL2/pqL14ZVlbH0fUQUZa/T5/UnQtBNgghR7mfB8ERBKyKPCi7Vw==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.22.5.tgz", + "integrity": "sha512-dEnYD+9BBgld5VBXHnF/DbYGp3fqGMsyxKbtD1mDyIA7AkTSpKXFhCVuj/oQVOoALfBs77DudA0BE4d5mcpmqw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.11.tgz", + "integrity": "sha512-g/21plo58sfteWjaO0ZNVb+uEOkJNjAaHhbejrnBmu011l/eNDScmkbjCC3l4FKb10ViaGU4aOkFznSu2zRHgA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.22.5.tgz", + "integrity": "sha512-vIpJFNM/FjZ4rh1myqIya9jXwrwwgFRHPjT3DkUA9ZLHuzox8jiXkOLvwm1H+PQIP3CqfC++WPKeuDi0Sjdj1g==", + "dependencies": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.11.tgz", + "integrity": "sha512-xa7aad7q7OiT8oNZ1mU7NrISjlSkVdMbNxn9IuLZyL9AJEhs1Apba3I+u5riX1dIkdptP5EKDG5XDPByWxtehw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.15.tgz", + "integrity": "sha512-me6VGeHsx30+xh9fbDLLPi0J1HzmeIIyenoOQHuw2D4m2SAU3NrspX5XxJLBpqn5yrLzrlw2Iy3RA//Bx27iOA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.22.5.tgz", + "integrity": "sha512-UIzQNMS0p0HHiQm3oelztj+ECwFnj+ZRV4KnguvlsD2of1whUeM6o7wGNj6oLwcDoAXQ8gEqfgC24D+VdIcevg==", + "dependencies": { + "@babel/helper-compilation-targets": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-json-strings": { + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.11.tgz", + "integrity": "sha512-CxT5tCqpA9/jXFlme9xIBCc5RPtdDq3JpkkhgHQqtDdiTnTI0jtZ0QzXhr5DILeYifDPp2wvY2ad+7+hLMW5Pw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-json-strings": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.22.5.tgz", + "integrity": "sha512-fTLj4D79M+mepcw3dgFBTIDYpbcB9Sm0bpm4ppXPaO+U+PKFFyV9MGRvS0gvGw62sd10kT5lRMKXAADb9pWy8g==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.11.tgz", + "integrity": "sha512-qQwRTP4+6xFCDV5k7gZBF3C31K34ut0tbEcTKxlX/0KXxm9GLcO14p570aWxFvVzx6QAfPgq7gaeIHXJC8LswQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.22.5.tgz", + "integrity": "sha512-RZEdkNtzzYCFl9SE9ATaUMTj2hqMb4StarOJLrZRbqqU4HSBE7UlBw9WBWQiDzrJZJdUWiMTVDI6Gv/8DPvfew==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.23.0.tgz", + "integrity": "sha512-xWT5gefv2HGSm4QHtgc1sYPbseOyf+FFDo2JbpE25GWl5BqTGO9IMwTYJRoIdjsF85GE+VegHxSCUt5EvoYTAw==", + "dependencies": { + "@babel/helper-module-transforms": "^7.23.0", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.0.tgz", + "integrity": "sha512-32Xzss14/UVc7k9g775yMIvkVK8xwKE0DPdP5JTapr3+Z9w4tzeOuLNY6BXDQR6BdnzIlXnCGAzsk/ICHBLVWQ==", + "dependencies": { + "@babel/helper-module-transforms": "^7.23.0", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-simple-access": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.0.tgz", + "integrity": "sha512-qBej6ctXZD2f+DhlOC9yO47yEYgUh5CZNz/aBoH4j/3NOlRfJXJbY7xDQCqQVf9KbrqGzIWER1f23doHGrIHFg==", + "dependencies": { + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-module-transforms": "^7.23.0", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.22.5.tgz", + "integrity": "sha512-+S6kzefN/E1vkSsKx8kmQuqeQsvCKCd1fraCM7zXm4SFoggI099Tr4G8U81+5gtMdUeMQ4ipdQffbKLX0/7dBQ==", + "dependencies": { + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz", + "integrity": "sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.22.5.tgz", + "integrity": "sha512-AsF7K0Fx/cNKVyk3a+DW0JLo+Ua598/NxMRvxDnkpCIGFh43+h/v2xyhRUYf6oD8gE4QtL83C7zZVghMjHd+iw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.11.tgz", + "integrity": "sha512-YZWOw4HxXrotb5xsjMJUDlLgcDXSfO9eCmdl1bgW4+/lAGdkjaEvOnQ4p5WKKdUgSzO39dgPl0pTnfxm0OAXcg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-numeric-separator": { + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.11.tgz", + "integrity": "sha512-3dzU4QGPsILdJbASKhF/V2TVP+gJya1PsueQCxIPCEcerqF21oEcrob4mzjsp2Py/1nLfF5m+xYNMDpmA8vffg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.15.tgz", + "integrity": "sha512-fEB+I1+gAmfAyxZcX1+ZUwLeAuuf8VIg67CTznZE0MqVFumWkh8xWtn58I4dxdVf080wn7gzWoF8vndOViJe9Q==", + "dependencies": { + "@babel/compat-data": "^7.22.9", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.22.5.tgz", + "integrity": "sha512-klXqyaT9trSjIUrcsYIfETAzmOEZL3cBYqOYLJxBHfMFFggmXOv+NYSX/Jbs9mzMVESw/WycLFPRx8ba/b2Ipw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.11.tgz", + "integrity": "sha512-rli0WxesXUeCJnMYhzAglEjLWVDF6ahb45HuprcmQuLidBJFWjNnOzssk2kuc6e33FlLaiZhG/kUIzUMWdBKaQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.0.tgz", + "integrity": "sha512-sBBGXbLJjxTzLBF5rFWaikMnOGOk/BmK6vVByIdEggZ7Vn6CvWXZyRkkLFK6WE0IF8jSliyOkUN6SScFgzCM0g==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.15.tgz", + "integrity": "sha512-hjk7qKIqhyzhhUvRT683TYQOFa/4cQKwQy7ALvTpODswN40MljzNDa0YldevS6tGbxwaEKVn502JmY0dP7qEtQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.22.5.tgz", + "integrity": "sha512-PPjh4gyrQnGe97JTalgRGMuU4icsZFnWkzicB/fUtzlKUqvsWBKEpPPfr5a2JiyirZkHxnAqkQMO5Z5B2kK3fA==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.11.tgz", + "integrity": "sha512-sSCbqZDBKHetvjSwpyWzhuHkmW5RummxJBVbYLkGkaiTOWGxml7SXt0iWa03bzxFIx7wOj3g/ILRd0RcJKBeSQ==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.22.11", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.22.5.tgz", + "integrity": "sha512-TiOArgddK3mK/x1Qwf5hay2pxI6wCZnvQqrFSqbtg1GLl2JcNMitVH/YnqjP+M31pLUeTfzY1HAXFDnUBV30rQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-constant-elements": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.24.7.tgz", + "integrity": "sha512-7LidzZfUXyfZ8/buRW6qIIHBY8wAZ1OrY9c/wTr8YhZ6vMPo+Uc/CVFLYY1spZrEQlD4w5u8wjqk5NQ3OVqQKA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-display-name": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.22.5.tgz", + "integrity": "sha512-PVk3WPYudRF5z4GKMEYUrLjPl38fJSKNaEOkFuoprioowGuWN6w2RKznuFNSlJx7pzzXXStPUnNSOEO0jL5EVw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.22.15.tgz", + "integrity": "sha512-oKckg2eZFa8771O/5vi7XeTvmM6+O9cxZu+kanTU7tD4sin5nO/G8jGJhq8Hvt2Z0kUoEDRayuZLaUlYl8QuGA==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-jsx": "^7.22.5", + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-development": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.22.5.tgz", + "integrity": "sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A==", + "dependencies": { + "@babel/plugin-transform-react-jsx": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-pure-annotations": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.22.5.tgz", + "integrity": "sha512-gP4k85wx09q+brArVinTXhWiyzLl9UpmGva0+mWyKxk6JZequ05x3eUcIUE+FyttPKJFRRVtAvQaJ6YF9h1ZpA==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.10.tgz", + "integrity": "sha512-F28b1mDt8KcT5bUyJc/U9nwzw6cV+UmTeRlXYIl2TNqMMJif0Jeey9/RQ3C4NOd2zp0/TRsDns9ttj2L523rsw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "regenerator-transform": "^0.15.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.22.5.tgz", + "integrity": "sha512-DTtGKFRQUDm8svigJzZHzb/2xatPc6TzNvAIJ5GqOKDsGFYgAskjRulbR/vGsPKq3OPqtexnz327qYpP57RFyA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.23.2.tgz", + "integrity": "sha512-XOntj6icgzMS58jPVtQpiuF6ZFWxQiJavISGx5KGjRj+3gqZr8+N6Kx+N9BApWzgS+DOjIZfXXj0ZesenOWDyA==", + "dependencies": { + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "babel-plugin-polyfill-corejs2": "^0.4.6", + "babel-plugin-polyfill-corejs3": "^0.8.5", + "babel-plugin-polyfill-regenerator": "^0.5.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.22.5.tgz", + "integrity": "sha512-vM4fq9IXHscXVKzDv5itkO1X52SmdFBFcMIBZ2FRn2nqVYqw6dBexUgMvAjHW+KXpPPViD/Yo3GrDEBaRC0QYA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.22.5.tgz", + "integrity": "sha512-5ZzDQIGyvN4w8+dMmpohL6MBo+l2G7tfC/O2Dg7/hjpgeWvUx8FzfeOKxGog9IimPa4YekaQ9PlDqTLOljkcxg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.22.5.tgz", + "integrity": "sha512-zf7LuNpHG0iEeiyCNwX4j3gDg1jgt1k3ZdXBKbZSoA3BbGQGvMiSvfbZRR3Dr3aeJe3ooWFZxOOG3IRStYp2Bw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.22.5.tgz", + "integrity": "sha512-5ciOehRNf+EyUeewo8NkbQiUs4d6ZxiHo6BcBcnFlgiJfu16q0bQUw9Jvo0b0gBKFG1SMhDSjeKXSYuJLeFSMA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.22.5.tgz", + "integrity": "sha512-bYkI5lMzL4kPii4HHEEChkD0rkc+nvnlR6+o/qdqR6zrm0Sv/nodmyLhlq2DO0YKLUNd2VePmPRjJXSBh9OIdA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.15.tgz", + "integrity": "sha512-1uirS0TnijxvQLnlv5wQBwOX3E1wCFX7ITv+9pBV2wKEk4K+M5tqDaoNXnTH8tjEIYHLO98MwiTWO04Ggz4XuA==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-typescript": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.10.tgz", + "integrity": "sha512-lRfaRKGZCBqDlRU3UIFovdp9c9mEvlylmpod0/OatICsSfuQ9YFthRo1tpTkGsklEefZdqlEFdY4A2dwTb6ohg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-property-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.22.5.tgz", + "integrity": "sha512-HCCIb+CbJIAE6sXn5CjFQXMwkCClcOfPCzTlilJ8cUatfzwHlWQkbtV0zD338u9dZskwvuOYTuuaMaA8J5EI5A==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.22.5.tgz", + "integrity": "sha512-028laaOKptN5vHJf9/Arr/HiJekMd41hOEZYvNsrsXqJ7YPYuX2bQxh31fkZzGmq3YqHRJzYFFAVYvKfMPKqyg==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-sets-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.22.5.tgz", + "integrity": "sha512-lhMfi4FC15j13eKrh3DnYHjpGj6UKQHtNKTbtc1igvAhRy4+kLhV07OpLcsN0VgDEw/MjAvJO4BdMJsHwMhzCg==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.23.2.tgz", + "integrity": "sha512-BW3gsuDD+rvHL2VO2SjAUNTBe5YrjsTiDyqamPDWY723na3/yPQ65X5oQkFVJZ0o50/2d+svm1rkPoJeR1KxVQ==", + "dependencies": { + "@babel/compat-data": "^7.23.2", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-option": "^7.22.15", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.22.15", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.22.15", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-import-assertions": "^7.22.5", + "@babel/plugin-syntax-import-attributes": "^7.22.5", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.22.5", + "@babel/plugin-transform-async-generator-functions": "^7.23.2", + "@babel/plugin-transform-async-to-generator": "^7.22.5", + "@babel/plugin-transform-block-scoped-functions": "^7.22.5", + "@babel/plugin-transform-block-scoping": "^7.23.0", + "@babel/plugin-transform-class-properties": "^7.22.5", + "@babel/plugin-transform-class-static-block": "^7.22.11", + "@babel/plugin-transform-classes": "^7.22.15", + "@babel/plugin-transform-computed-properties": "^7.22.5", + "@babel/plugin-transform-destructuring": "^7.23.0", + "@babel/plugin-transform-dotall-regex": "^7.22.5", + "@babel/plugin-transform-duplicate-keys": "^7.22.5", + "@babel/plugin-transform-dynamic-import": "^7.22.11", + "@babel/plugin-transform-exponentiation-operator": "^7.22.5", + "@babel/plugin-transform-export-namespace-from": "^7.22.11", + "@babel/plugin-transform-for-of": "^7.22.15", + "@babel/plugin-transform-function-name": "^7.22.5", + "@babel/plugin-transform-json-strings": "^7.22.11", + "@babel/plugin-transform-literals": "^7.22.5", + "@babel/plugin-transform-logical-assignment-operators": "^7.22.11", + "@babel/plugin-transform-member-expression-literals": "^7.22.5", + "@babel/plugin-transform-modules-amd": "^7.23.0", + "@babel/plugin-transform-modules-commonjs": "^7.23.0", + "@babel/plugin-transform-modules-systemjs": "^7.23.0", + "@babel/plugin-transform-modules-umd": "^7.22.5", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", + "@babel/plugin-transform-new-target": "^7.22.5", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.11", + "@babel/plugin-transform-numeric-separator": "^7.22.11", + "@babel/plugin-transform-object-rest-spread": "^7.22.15", + "@babel/plugin-transform-object-super": "^7.22.5", + "@babel/plugin-transform-optional-catch-binding": "^7.22.11", + "@babel/plugin-transform-optional-chaining": "^7.23.0", + "@babel/plugin-transform-parameters": "^7.22.15", + "@babel/plugin-transform-private-methods": "^7.22.5", + "@babel/plugin-transform-private-property-in-object": "^7.22.11", + "@babel/plugin-transform-property-literals": "^7.22.5", + "@babel/plugin-transform-regenerator": "^7.22.10", + "@babel/plugin-transform-reserved-words": "^7.22.5", + "@babel/plugin-transform-shorthand-properties": "^7.22.5", + "@babel/plugin-transform-spread": "^7.22.5", + "@babel/plugin-transform-sticky-regex": "^7.22.5", + "@babel/plugin-transform-template-literals": "^7.22.5", + "@babel/plugin-transform-typeof-symbol": "^7.22.5", + "@babel/plugin-transform-unicode-escapes": "^7.22.10", + "@babel/plugin-transform-unicode-property-regex": "^7.22.5", + "@babel/plugin-transform-unicode-regex": "^7.22.5", + "@babel/plugin-transform-unicode-sets-regex": "^7.22.5", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "@babel/types": "^7.23.0", + "babel-plugin-polyfill-corejs2": "^0.4.6", + "babel-plugin-polyfill-corejs3": "^0.8.5", + "babel-plugin-polyfill-regenerator": "^0.5.3", + "core-js-compat": "^3.31.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/preset-react": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.22.15.tgz", + "integrity": "sha512-Csy1IJ2uEh/PecCBXXoZGAZBeCATTuePzCSB7dLYWS0vOEj6CNpjxIhW4duWwZodBNueH7QO14WbGn8YyeuN9w==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-option": "^7.22.15", + "@babel/plugin-transform-react-display-name": "^7.22.5", + "@babel/plugin-transform-react-jsx": "^7.22.15", + "@babel/plugin-transform-react-jsx-development": "^7.22.5", + "@babel/plugin-transform-react-pure-annotations": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-typescript": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.23.2.tgz", + "integrity": "sha512-u4UJc1XsS1GhIGteM8rnGiIvf9rJpiVgMEeCnwlLA7WJPC+jcXWJAGxYmeqs5hOZD8BbAfnV5ezBOxQbb4OUxA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-option": "^7.22.15", + "@babel/plugin-syntax-jsx": "^7.22.5", + "@babel/plugin-transform-modules-commonjs": "^7.23.0", + "@babel/plugin-transform-typescript": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" + }, + "node_modules/@babel/runtime": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz", + "integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/runtime-corejs3": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.23.2.tgz", + "integrity": "sha512-54cIh74Z1rp4oIjsHjqN+WM4fMyCBYe+LpZ9jWm51CZ1fbH3SkAzQD/3XLoNkjbJ7YEmjobLXyvQrFypRHOrXw==", + "dependencies": { + "core-js-pure": "^3.30.2", + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.5.tgz", + "integrity": "sha512-czx7Xy5a6sapWWRx61m1Ke1Ra4vczu1mCTtJam5zRTBOonfdJ+S/B6HYmGYu3fJtr8GGET3si6IhgWVBhJ/m8w==", + "dependencies": { + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.5", + "@babel/types": "^7.23.5", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.5.tgz", + "integrity": "sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w==", + "dependencies": { + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@braintree/sanitize-url": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-6.0.4.tgz", + "integrity": "sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A==" + }, + "node_modules/@codemirror/autocomplete": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.10.2.tgz", + "integrity": "sha512-3dCL7b0j2GdtZzWN5j7HDpRAJ26ip07R4NGYz7QYthIYMiX8I4E4TNrYcdTayPJGeVQtd/xe7lWU4XL7THFb/w==", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0" + }, + "peerDependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@codemirror/commands": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.3.0.tgz", + "integrity": "sha512-tFfcxRIlOWiQDFhjBSWJ10MxcvbCIsRr6V64SgrcaY0MwNk32cUOcCuNlWo8VjV4qRQCgNgUAnIeo0svkk4R5Q==", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.2.0", + "@codemirror/view": "^6.0.0", + "@lezer/common": "^1.1.0" + } + }, + "node_modules/@codemirror/lang-css": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.2.1.tgz", + "integrity": "sha512-/UNWDNV5Viwi/1lpr/dIXJNWiwDxpw13I4pTUAsNxZdg6E0mI2kTQb0P2iHczg1Tu+H4EBgJR+hYhKiHKko7qg==", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.0.2", + "@lezer/css": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-html": { + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.6.tgz", + "integrity": "sha512-E4C8CVupBksXvgLSme/zv31x91g06eZHSph7NczVxZW+/K+3XgJGWNT//2WLzaKSBoxpAjaOi5ZnPU1SHhjh3A==", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/lang-css": "^6.0.0", + "@codemirror/lang-javascript": "^6.0.0", + "@codemirror/language": "^6.4.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0", + "@lezer/css": "^1.1.0", + "@lezer/html": "^1.3.0" + } + }, + "node_modules/@codemirror/lang-javascript": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.1.tgz", + "integrity": "sha512-jlFOXTejVyiQCW3EQwvKH0m99bUYIw40oPmFjSX2VS78yzfe0HELZ+NEo9Yfo1MkGRpGlj3Gnu4rdxV1EnAs5A==", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.6.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0", + "@lezer/javascript": "^1.0.0" + } + }, + "node_modules/@codemirror/language": { + "version": "6.9.2", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.9.2.tgz", + "integrity": "sha512-QGTQXSpAKDIzaSE96zNK1UfIUhPgkT1CLjh1N5qVzZuxgsEOhz5RqaN8QCIdyOQklGLx3MgHd9YrE3X3+Pl1ow==", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@lezer/common": "^1.1.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0", + "style-mod": "^4.0.0" + } + }, + "node_modules/@codemirror/lint": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.4.2.tgz", + "integrity": "sha512-wzRkluWb1ptPKdzlsrbwwjYCPLgzU6N88YBAmlZi8WFyuiEduSd05MnJYNogzyc8rPK7pj6m95ptUApc8sHKVA==", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/state": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.3.1.tgz", + "integrity": "sha512-88e4HhMtKJyw6fKprGaN/yZfiaoGYOi2nM45YCUC6R/kex9sxFWBDGatS1vk4lMgnWmdIIB9tk8Gj1LmL8YfvA==" + }, + "node_modules/@codemirror/view": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.22.0.tgz", + "integrity": "sha512-6zLj4YIoIpfTGKrDMTbeZRpa8ih4EymMCKmddEDcJWrCdp/N1D46B38YEz4creTb4T177AVS9EyXkLeC/HL2jA==", + "dependencies": { + "@codemirror/state": "^6.1.4", + "style-mod": "^4.1.0", + "w3c-keyname": "^2.2.4" + } + }, + "node_modules/@codesandbox/nodebox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/@codesandbox/nodebox/-/nodebox-0.1.8.tgz", + "integrity": "sha512-2VRS6JDSk+M+pg56GA6CryyUSGPjBEe8Pnae0QL3jJF1mJZJVMDKr93gJRtBbLkfZN6LD/DwMtf+2L0bpWrjqg==", + "dependencies": { + "outvariant": "^1.4.0", + "strict-event-emitter": "^0.4.3" + } + }, + "node_modules/@codesandbox/sandpack-client": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@codesandbox/sandpack-client/-/sandpack-client-2.9.0.tgz", + "integrity": "sha512-KkG/YusBsL0RnI3P079cckHrRleLaGQVM5Plzn6xWOPM04Dce52MMkr6XIU77ZMjZm/zZ5pmgXLW7M6HoyIy/A==", + "dependencies": { + "@codesandbox/nodebox": "0.1.8", + "buffer": "^6.0.3", + "dequal": "^2.0.2", + "outvariant": "1.4.0", + "static-browser-server": "1.0.3" + } + }, + "node_modules/@codesandbox/sandpack-react": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@codesandbox/sandpack-react/-/sandpack-react-2.9.0.tgz", + "integrity": "sha512-A0quGugVyWbRktpdq2Nc6W1+XV0QnHq1lbqDCHAs2ijfWxvhhNaqMr6lWDaG/NTUGRNLUNETbipcNaBeC0dxhw==", + "dependencies": { + "@codemirror/autocomplete": "^6.4.0", + "@codemirror/commands": "^6.1.3", + "@codemirror/lang-css": "^6.0.1", + "@codemirror/lang-html": "^6.4.0", + "@codemirror/lang-javascript": "^6.1.2", + "@codemirror/language": "^6.3.2", + "@codemirror/state": "^6.2.0", + "@codemirror/view": "^6.7.1", + "@codesandbox/sandpack-client": "^2.9.0", + "@lezer/highlight": "^1.1.3", + "@react-hook/intersection-observer": "^3.1.1", + "@stitches/core": "^1.2.6", + "anser": "^2.1.1", + "clean-set": "^1.1.2", + "codesandbox-import-util-types": "^2.2.3", + "dequal": "^2.0.2", + "escape-carriage": "^1.3.1", + "lz-string": "^1.4.4", + "react-devtools-inline": "4.4.0", + "react-is": "^17.0.2" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18", + "react-dom": "^16.8.0 || ^17 || ^18" + } + }, + "node_modules/@codesandbox/sandpack-themes": { + "version": "2.0.21", + "resolved": "https://registry.npmjs.org/@codesandbox/sandpack-themes/-/sandpack-themes-2.0.21.tgz", + "integrity": "sha512-CMH/MO/dh6foPYb/3eSn2Cu/J3+1+/81Fsaj7VggICkCrmRk0qG5dmgjGAearPTnRkOGORIPHuRqwNXgw0E6YQ==" + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@docsearch/css": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.6.0.tgz", + "integrity": "sha512-+sbxb71sWre+PwDK7X2T8+bhS6clcVMLwBPznX45Qu6opJcgRjAp7gYSDzVFp187J+feSj5dNBN1mJoi6ckkUQ==" + }, + "node_modules/@docsearch/react": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.6.0.tgz", + "integrity": "sha512-HUFut4ztcVNmqy9gp/wxNbC7pTOHhgVVkHVGCACTuLhUKUhKAF9KYHJtMiLUJxEqiFLQiuri1fWF8zqwM/cu1w==", + "dependencies": { + "@algolia/autocomplete-core": "1.9.3", + "@algolia/autocomplete-preset-algolia": "1.9.3", + "@docsearch/css": "3.6.0", + "algoliasearch": "^4.19.1" + }, + "peerDependencies": { + "@types/react": ">= 16.8.0 < 19.0.0", + "react": ">= 16.8.0 < 19.0.0", + "react-dom": ">= 16.8.0 < 19.0.0", + "search-insights": ">= 1 < 3" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "search-insights": { + "optional": true + } + } + }, + "node_modules/@docusaurus/core": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.4.0.tgz", + "integrity": "sha512-g+0wwmN2UJsBqy2fQRQ6fhXruoEa62JDeEa5d8IdTJlMoaDaEDfHh7WjwGRn4opuTQWpjAwP/fbcgyHKlE+64w==", + "dependencies": { + "@babel/core": "^7.23.3", + "@babel/generator": "^7.23.3", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-transform-runtime": "^7.22.9", + "@babel/preset-env": "^7.22.9", + "@babel/preset-react": "^7.22.5", + "@babel/preset-typescript": "^7.22.5", + "@babel/runtime": "^7.22.6", + "@babel/runtime-corejs3": "^7.22.6", + "@babel/traverse": "^7.22.8", + "@docusaurus/cssnano-preset": "3.4.0", + "@docusaurus/logger": "3.4.0", + "@docusaurus/mdx-loader": "3.4.0", + "@docusaurus/utils": "3.4.0", + "@docusaurus/utils-common": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", + "autoprefixer": "^10.4.14", + "babel-loader": "^9.1.3", + "babel-plugin-dynamic-import-node": "^2.3.3", + "boxen": "^6.2.1", + "chalk": "^4.1.2", + "chokidar": "^3.5.3", + "clean-css": "^5.3.2", + "cli-table3": "^0.6.3", + "combine-promises": "^1.1.0", + "commander": "^5.1.0", + "copy-webpack-plugin": "^11.0.0", + "core-js": "^3.31.1", + "css-loader": "^6.8.1", + "css-minimizer-webpack-plugin": "^5.0.1", + "cssnano": "^6.1.2", + "del": "^6.1.1", + "detect-port": "^1.5.1", + "escape-html": "^1.0.3", + "eta": "^2.2.0", + "eval": "^0.1.8", + "file-loader": "^6.2.0", + "fs-extra": "^11.1.1", + "html-minifier-terser": "^7.2.0", + "html-tags": "^3.3.1", + "html-webpack-plugin": "^5.5.3", + "leven": "^3.1.0", + "lodash": "^4.17.21", + "mini-css-extract-plugin": "^2.7.6", + "p-map": "^4.0.0", + "postcss": "^8.4.26", + "postcss-loader": "^7.3.3", + "prompts": "^2.4.2", + "react-dev-utils": "^12.0.1", + "react-helmet-async": "^1.3.0", + "react-loadable": "npm:@docusaurus/react-loadable@6.0.0", + "react-loadable-ssr-addon-v5-slorber": "^1.0.1", + "react-router": "^5.3.4", + "react-router-config": "^5.1.1", + "react-router-dom": "^5.3.4", + "rtl-detect": "^1.0.4", + "semver": "^7.5.4", + "serve-handler": "^6.1.5", + "shelljs": "^0.8.5", + "terser-webpack-plugin": "^5.3.9", + "tslib": "^2.6.0", + "update-notifier": "^6.0.2", + "url-loader": "^4.1.1", + "webpack": "^5.88.1", + "webpack-bundle-analyzer": "^4.9.0", + "webpack-dev-server": "^4.15.1", + "webpack-merge": "^5.9.0", + "webpackbar": "^5.0.2" + }, + "bin": { + "docusaurus": "bin/docusaurus.mjs" + }, + "engines": { + "node": ">=18.0" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@docusaurus/cssnano-preset": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-3.4.0.tgz", + "integrity": "sha512-qwLFSz6v/pZHy/UP32IrprmH5ORce86BGtN0eBtG75PpzQJAzp9gefspox+s8IEOr0oZKuQ/nhzZ3xwyc3jYJQ==", + "dependencies": { + "cssnano-preset-advanced": "^6.1.2", + "postcss": "^8.4.38", + "postcss-sort-media-queries": "^5.2.0", + "tslib": "^2.6.0" + }, + "engines": { + "node": ">=18.0" + } + }, + "node_modules/@docusaurus/logger": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-3.4.0.tgz", + "integrity": "sha512-bZwkX+9SJ8lB9kVRkXw+xvHYSMGG4bpYHKGXeXFvyVc79NMeeBSGgzd4TQLHH+DYeOJoCdl8flrFJVxlZ0wo/Q==", + "dependencies": { + "chalk": "^4.1.2", + "tslib": "^2.6.0" + }, + "engines": { + "node": ">=18.0" + } + }, + "node_modules/@docusaurus/mdx-loader": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.4.0.tgz", + "integrity": "sha512-kSSbrrk4nTjf4d+wtBA9H+FGauf2gCax89kV8SUSJu3qaTdSIKdWERlngsiHaCFgZ7laTJ8a67UFf+xlFPtuTw==", + "dependencies": { + "@docusaurus/logger": "3.4.0", + "@docusaurus/utils": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", + "@mdx-js/mdx": "^3.0.0", + "@slorber/remark-comment": "^1.0.0", + "escape-html": "^1.0.3", + "estree-util-value-to-estree": "^3.0.1", + "file-loader": "^6.2.0", + "fs-extra": "^11.1.1", + "image-size": "^1.0.2", + "mdast-util-mdx": "^3.0.0", + "mdast-util-to-string": "^4.0.0", + "rehype-raw": "^7.0.0", + "remark-directive": "^3.0.0", + "remark-emoji": "^4.0.0", + "remark-frontmatter": "^5.0.0", + "remark-gfm": "^4.0.0", + "stringify-object": "^3.3.0", + "tslib": "^2.6.0", + "unified": "^11.0.3", + "unist-util-visit": "^5.0.0", + "url-loader": "^4.1.1", + "vfile": "^6.0.1", + "webpack": "^5.88.1" + }, + "engines": { + "node": ">=18.0" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@docusaurus/module-type-aliases": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.4.0.tgz", + "integrity": "sha512-A1AyS8WF5Bkjnb8s+guTDuYmUiwJzNrtchebBHpc0gz0PyHJNMaybUlSrmJjHVcGrya0LKI4YcR3lBDQfXRYLw==", + "dependencies": { + "@docusaurus/types": "3.4.0", + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router-config": "*", + "@types/react-router-dom": "*", + "react-helmet-async": "*", + "react-loadable": "npm:@docusaurus/react-loadable@6.0.0" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/@docusaurus/plugin-content-blog": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.4.0.tgz", + "integrity": "sha512-vv6ZAj78ibR5Jh7XBUT4ndIjmlAxkijM3Sx5MAAzC1gyv0vupDQNhzuFg1USQmQVj3P5I6bquk12etPV3LJ+Xw==", + "dependencies": { + "@docusaurus/core": "3.4.0", + "@docusaurus/logger": "3.4.0", + "@docusaurus/mdx-loader": "3.4.0", + "@docusaurus/types": "3.4.0", + "@docusaurus/utils": "3.4.0", + "@docusaurus/utils-common": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", + "cheerio": "^1.0.0-rc.12", + "feed": "^4.2.2", + "fs-extra": "^11.1.1", + "lodash": "^4.17.21", + "reading-time": "^1.5.0", + "srcset": "^4.0.0", + "tslib": "^2.6.0", + "unist-util-visit": "^5.0.0", + "utility-types": "^3.10.0", + "webpack": "^5.88.1" + }, + "engines": { + "node": ">=18.0" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@docusaurus/plugin-content-docs": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.4.0.tgz", + "integrity": "sha512-HkUCZffhBo7ocYheD9oZvMcDloRnGhBMOZRyVcAQRFmZPmNqSyISlXA1tQCIxW+r478fty97XXAGjNYzBjpCsg==", + "dependencies": { + "@docusaurus/core": "3.4.0", + "@docusaurus/logger": "3.4.0", + "@docusaurus/mdx-loader": "3.4.0", + "@docusaurus/module-type-aliases": "3.4.0", + "@docusaurus/types": "3.4.0", + "@docusaurus/utils": "3.4.0", + "@docusaurus/utils-common": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", + "@types/react-router-config": "^5.0.7", + "combine-promises": "^1.1.0", + "fs-extra": "^11.1.1", + "js-yaml": "^4.1.0", + "lodash": "^4.17.21", + "tslib": "^2.6.0", + "utility-types": "^3.10.0", + "webpack": "^5.88.1" + }, + "engines": { + "node": ">=18.0" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@docusaurus/plugin-content-pages": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.4.0.tgz", + "integrity": "sha512-h2+VN/0JjpR8fIkDEAoadNjfR3oLzB+v1qSXbIAKjQ46JAHx3X22n9nqS+BWSQnTnp1AjkjSvZyJMekmcwxzxg==", + "dependencies": { + "@docusaurus/core": "3.4.0", + "@docusaurus/mdx-loader": "3.4.0", + "@docusaurus/types": "3.4.0", + "@docusaurus/utils": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", + "fs-extra": "^11.1.1", + "tslib": "^2.6.0", + "webpack": "^5.88.1" + }, + "engines": { + "node": ">=18.0" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@docusaurus/plugin-debug": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-3.4.0.tgz", + "integrity": "sha512-uV7FDUNXGyDSD3PwUaf5YijX91T5/H9SX4ErEcshzwgzWwBtK37nUWPU3ZLJfeTavX3fycTOqk9TglpOLaWkCg==", + "dependencies": { + "@docusaurus/core": "3.4.0", + "@docusaurus/types": "3.4.0", + "@docusaurus/utils": "3.4.0", + "fs-extra": "^11.1.1", + "react-json-view-lite": "^1.2.0", + "tslib": "^2.6.0" + }, + "engines": { + "node": ">=18.0" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@docusaurus/plugin-google-analytics": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.4.0.tgz", + "integrity": "sha512-mCArluxEGi3cmYHqsgpGGt3IyLCrFBxPsxNZ56Mpur0xSlInnIHoeLDH7FvVVcPJRPSQ9/MfRqLsainRw+BojA==", + "dependencies": { + "@docusaurus/core": "3.4.0", + "@docusaurus/types": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", + "tslib": "^2.6.0" + }, + "engines": { + "node": ">=18.0" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@docusaurus/plugin-google-gtag": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.4.0.tgz", + "integrity": "sha512-Dsgg6PLAqzZw5wZ4QjUYc8Z2KqJqXxHxq3vIoyoBWiLEEfigIs7wHR+oiWUQy3Zk9MIk6JTYj7tMoQU0Jm3nqA==", + "dependencies": { + "@docusaurus/core": "3.4.0", + "@docusaurus/types": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", + "@types/gtag.js": "^0.0.12", + "tslib": "^2.6.0" + }, + "engines": { + "node": ">=18.0" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@docusaurus/plugin-google-tag-manager": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.4.0.tgz", + "integrity": "sha512-O9tX1BTwxIhgXpOLpFDueYA9DWk69WCbDRrjYoMQtFHSkTyE7RhNgyjSPREUWJb9i+YUg3OrsvrBYRl64FCPCQ==", + "dependencies": { + "@docusaurus/core": "3.4.0", + "@docusaurus/types": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", + "tslib": "^2.6.0" + }, + "engines": { + "node": ">=18.0" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@docusaurus/plugin-sitemap": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.4.0.tgz", + "integrity": "sha512-+0VDvx9SmNrFNgwPoeoCha+tRoAjopwT0+pYO1xAbyLcewXSemq+eLxEa46Q1/aoOaJQ0qqHELuQM7iS2gp33Q==", + "dependencies": { + "@docusaurus/core": "3.4.0", + "@docusaurus/logger": "3.4.0", + "@docusaurus/types": "3.4.0", + "@docusaurus/utils": "3.4.0", + "@docusaurus/utils-common": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", + "fs-extra": "^11.1.1", + "sitemap": "^7.1.1", + "tslib": "^2.6.0" + }, + "engines": { + "node": ">=18.0" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@docusaurus/preset-classic": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-3.4.0.tgz", + "integrity": "sha512-Ohj6KB7siKqZaQhNJVMBBUzT3Nnp6eTKqO+FXO3qu/n1hJl3YLwVKTWBg28LF7MWrKu46UuYavwMRxud0VyqHg==", + "dependencies": { + "@docusaurus/core": "3.4.0", + "@docusaurus/plugin-content-blog": "3.4.0", + "@docusaurus/plugin-content-docs": "3.4.0", + "@docusaurus/plugin-content-pages": "3.4.0", + "@docusaurus/plugin-debug": "3.4.0", + "@docusaurus/plugin-google-analytics": "3.4.0", + "@docusaurus/plugin-google-gtag": "3.4.0", + "@docusaurus/plugin-google-tag-manager": "3.4.0", + "@docusaurus/plugin-sitemap": "3.4.0", + "@docusaurus/theme-classic": "3.4.0", + "@docusaurus/theme-common": "3.4.0", + "@docusaurus/theme-search-algolia": "3.4.0", + "@docusaurus/types": "3.4.0" + }, + "engines": { + "node": ">=18.0" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@docusaurus/theme-classic": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-3.4.0.tgz", + "integrity": "sha512-0IPtmxsBYv2adr1GnZRdMkEQt1YW6tpzrUPj02YxNpvJ5+ju4E13J5tB4nfdaen/tfR1hmpSPlTFPvTf4kwy8Q==", + "dependencies": { + "@docusaurus/core": "3.4.0", + "@docusaurus/mdx-loader": "3.4.0", + "@docusaurus/module-type-aliases": "3.4.0", + "@docusaurus/plugin-content-blog": "3.4.0", + "@docusaurus/plugin-content-docs": "3.4.0", + "@docusaurus/plugin-content-pages": "3.4.0", + "@docusaurus/theme-common": "3.4.0", + "@docusaurus/theme-translations": "3.4.0", + "@docusaurus/types": "3.4.0", + "@docusaurus/utils": "3.4.0", + "@docusaurus/utils-common": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", + "@mdx-js/react": "^3.0.0", + "clsx": "^2.0.0", + "copy-text-to-clipboard": "^3.2.0", + "infima": "0.2.0-alpha.43", + "lodash": "^4.17.21", + "nprogress": "^0.2.0", + "postcss": "^8.4.26", + "prism-react-renderer": "^2.3.0", + "prismjs": "^1.29.0", + "react-router-dom": "^5.3.4", + "rtlcss": "^4.1.0", + "tslib": "^2.6.0", + "utility-types": "^3.10.0" + }, + "engines": { + "node": ">=18.0" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@docusaurus/theme-common": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.4.0.tgz", + "integrity": "sha512-0A27alXuv7ZdCg28oPE8nH/Iz73/IUejVaCazqu9elS4ypjiLhK3KfzdSQBnL/g7YfHSlymZKdiOHEo8fJ0qMA==", + "dependencies": { + "@docusaurus/mdx-loader": "3.4.0", + "@docusaurus/module-type-aliases": "3.4.0", + "@docusaurus/plugin-content-blog": "3.4.0", + "@docusaurus/plugin-content-docs": "3.4.0", + "@docusaurus/plugin-content-pages": "3.4.0", + "@docusaurus/utils": "3.4.0", + "@docusaurus/utils-common": "3.4.0", + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router-config": "*", + "clsx": "^2.0.0", + "parse-numeric-range": "^1.3.0", + "prism-react-renderer": "^2.3.0", + "tslib": "^2.6.0", + "utility-types": "^3.10.0" + }, + "engines": { + "node": ">=18.0" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@docusaurus/theme-mermaid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-mermaid/-/theme-mermaid-3.4.0.tgz", + "integrity": "sha512-3w5QW0HEZ2O6x2w6lU3ZvOe1gNXP2HIoKDMJBil1VmLBc9PmpAG17VmfhI/p3L2etNmOiVs5GgniUqvn8AFEGQ==", + "dependencies": { + "@docusaurus/core": "3.4.0", + "@docusaurus/module-type-aliases": "3.4.0", + "@docusaurus/theme-common": "3.4.0", + "@docusaurus/types": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", + "mermaid": "^10.4.0", + "tslib": "^2.6.0" + }, + "engines": { + "node": ">=18.0" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@docusaurus/theme-search-algolia": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.4.0.tgz", + "integrity": "sha512-aiHFx7OCw4Wck1z6IoShVdUWIjntC8FHCw9c5dR8r3q4Ynh+zkS8y2eFFunN/DL6RXPzpnvKCg3vhLQYJDmT9Q==", + "dependencies": { + "@docsearch/react": "^3.5.2", + "@docusaurus/core": "3.4.0", + "@docusaurus/logger": "3.4.0", + "@docusaurus/plugin-content-docs": "3.4.0", + "@docusaurus/theme-common": "3.4.0", + "@docusaurus/theme-translations": "3.4.0", + "@docusaurus/utils": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", + "algoliasearch": "^4.18.0", + "algoliasearch-helper": "^3.13.3", + "clsx": "^2.0.0", + "eta": "^2.2.0", + "fs-extra": "^11.1.1", + "lodash": "^4.17.21", + "tslib": "^2.6.0", + "utility-types": "^3.10.0" + }, + "engines": { + "node": ">=18.0" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@docusaurus/theme-translations": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.4.0.tgz", + "integrity": "sha512-zSxCSpmQCCdQU5Q4CnX/ID8CSUUI3fvmq4hU/GNP/XoAWtXo9SAVnM3TzpU8Gb//H3WCsT8mJcTfyOk3d9ftNg==", + "dependencies": { + "fs-extra": "^11.1.1", + "tslib": "^2.6.0" + }, + "engines": { + "node": ">=18.0" + } + }, + "node_modules/@docusaurus/tsconfig": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/tsconfig/-/tsconfig-3.4.0.tgz", + "integrity": "sha512-0qENiJ+TRaeTzcg4olrnh0BQ7eCxTgbYWBnWUeQDc84UYkt/T3pDNnm3SiQkqPb+YQ1qtYFlC0RriAElclo8Dg==" + }, + "node_modules/@docusaurus/types": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.4.0.tgz", + "integrity": "sha512-4jcDO8kXi5Cf9TcyikB/yKmz14f2RZ2qTRerbHAsS+5InE9ZgSLBNLsewtFTcTOXSVcbU3FoGOzcNWAmU1TR0A==", + "dependencies": { + "@mdx-js/mdx": "^3.0.0", + "@types/history": "^4.7.11", + "@types/react": "*", + "commander": "^5.1.0", + "joi": "^17.9.2", + "react-helmet-async": "^1.3.0", + "utility-types": "^3.10.0", + "webpack": "^5.88.1", + "webpack-merge": "^5.9.0" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@docusaurus/utils": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.4.0.tgz", + "integrity": "sha512-fRwnu3L3nnWaXOgs88BVBmG1yGjcQqZNHG+vInhEa2Sz2oQB+ZjbEMO5Rh9ePFpZ0YDiDUhpaVjwmS+AU2F14g==", + "dependencies": { + "@docusaurus/logger": "3.4.0", + "@docusaurus/utils-common": "3.4.0", + "@svgr/webpack": "^8.1.0", + "escape-string-regexp": "^4.0.0", + "file-loader": "^6.2.0", + "fs-extra": "^11.1.1", + "github-slugger": "^1.5.0", + "globby": "^11.1.0", + "gray-matter": "^4.0.3", + "jiti": "^1.20.0", + "js-yaml": "^4.1.0", + "lodash": "^4.17.21", + "micromatch": "^4.0.5", + "prompts": "^2.4.2", + "resolve-pathname": "^3.0.0", + "shelljs": "^0.8.5", + "tslib": "^2.6.0", + "url-loader": "^4.1.1", + "utility-types": "^3.10.0", + "webpack": "^5.88.1" + }, + "engines": { + "node": ">=18.0" + }, + "peerDependencies": { + "@docusaurus/types": "*" + }, + "peerDependenciesMeta": { + "@docusaurus/types": { + "optional": true + } + } + }, + "node_modules/@docusaurus/utils-common": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.4.0.tgz", + "integrity": "sha512-NVx54Wr4rCEKsjOH5QEVvxIqVvm+9kh7q8aYTU5WzUU9/Hctd6aTrcZ3G0Id4zYJ+AeaG5K5qHA4CY5Kcm2iyQ==", + "dependencies": { + "tslib": "^2.6.0" + }, + "engines": { + "node": ">=18.0" + }, + "peerDependencies": { + "@docusaurus/types": "*" + }, + "peerDependenciesMeta": { + "@docusaurus/types": { + "optional": true + } + } + }, + "node_modules/@docusaurus/utils-validation": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.4.0.tgz", + "integrity": "sha512-hYQ9fM+AXYVTWxJOT1EuNaRnrR2WGpRdLDQG07O8UOpsvCPWUVOeo26Rbm0JWY2sGLfzAb+tvJ62yF+8F+TV0g==", + "dependencies": { + "@docusaurus/logger": "3.4.0", + "@docusaurus/utils": "3.4.0", + "@docusaurus/utils-common": "3.4.0", + "fs-extra": "^11.2.0", + "joi": "^17.9.2", + "js-yaml": "^4.1.0", + "lodash": "^4.17.21", + "tslib": "^2.6.0" + }, + "engines": { + "node": ">=18.0" + } + }, + "node_modules/@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" + }, + "node_modules/@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", + "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", + "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", + "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" + }, + "node_modules/@lezer/common": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.1.0.tgz", + "integrity": "sha512-XPIN3cYDXsoJI/oDWoR2tD++juVrhgIago9xyKhZ7IhGlzdDM9QgC8D8saKNCz5pindGcznFr2HBSsEQSWnSjw==" + }, + "node_modules/@lezer/css": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.1.3.tgz", + "integrity": "sha512-SjSM4pkQnQdJDVc80LYzEaMiNy9txsFbI7HsMgeVF28NdLaAdHNtQ+kB/QqDUzRBV/75NTXjJ/R5IdC8QQGxMg==", + "dependencies": { + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/highlight": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.1.6.tgz", + "integrity": "sha512-cmSJYa2us+r3SePpRCjN5ymCqCPv+zyXmDl0ciWtVaNiORT/MxM7ZgOMQZADD0o51qOaOg24qc/zBViOIwAjJg==", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@lezer/html": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.6.tgz", + "integrity": "sha512-Kk9HJARZTc0bAnMQUqbtuhFVsB4AnteR2BFUWfZV7L/x1H0aAKz6YabrfJ2gk/BEgjh9L3hg5O4y2IDZRBdzuQ==", + "dependencies": { + "@lezer/common": "^1.0.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/javascript": { + "version": "1.4.9", + "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.4.9.tgz", + "integrity": "sha512-7Uv8mBBE6l44spgWEZvEMdDqGV+FIuY7kJ1o5TFm+jxIuxydO3PcKJYiINij09igd1D/9P7l2KDqpkN8c3bM6A==", + "dependencies": { + "@lezer/highlight": "^1.1.3", + "@lezer/lr": "^1.3.0" + } + }, + "node_modules/@lezer/lr": { + "version": "1.3.14", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.3.14.tgz", + "integrity": "sha512-z5mY4LStlA3yL7aHT/rqgG614cfcvklS+8oFRFBYrs4YaWLJyKKM4+nN6KopToX0o9Hj6zmH6M5kinOYuy06ug==", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@mdx-js/mdx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-3.0.1.tgz", + "integrity": "sha512-eIQ4QTrOWyL3LWEe/bu6Taqzq2HQvHcyTMaOrI95P2/LmJE7AsfPfgJGuFLPVqBUE1BC1rik3VIhU+s9u72arA==", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdx": "^2.0.0", + "collapse-white-space": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-build-jsx": "^3.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "estree-util-to-js": "^2.0.0", + "estree-walker": "^3.0.0", + "hast-util-to-estree": "^3.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "markdown-extensions": "^2.0.0", + "periscopic": "^3.0.0", + "remark-mdx": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "source-map": "^0.7.0", + "unified": "^11.0.0", + "unist-util-position-from-estree": "^2.0.0", + "unist-util-stringify-position": "^4.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@mdx-js/react": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.0.0.tgz", + "integrity": "sha512-nDctevR9KyYFyV+m+/+S4cpzCWHqj+iHDHq3QrsWezcC+B17uZdIWgCguESUkwFhM3n/56KxWVE3V6EokrmONQ==", + "dependencies": { + "@types/mdx": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=16", + "react": ">=16" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@open-draft/deferred-promise": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz", + "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==" + }, + "node_modules/@pnpm/config.env-replace": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", + "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==", + "engines": { + "node": ">=12.22.0" + } + }, + "node_modules/@pnpm/network.ca-file": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz", + "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==", + "dependencies": { + "graceful-fs": "4.2.10" + }, + "engines": { + "node": ">=12.22.0" + } + }, + "node_modules/@pnpm/network.ca-file/node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" + }, + "node_modules/@pnpm/npm-conf": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-2.2.2.tgz", + "integrity": "sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==", + "dependencies": { + "@pnpm/config.env-replace": "^1.1.0", + "@pnpm/network.ca-file": "^1.0.1", + "config-chain": "^1.1.11" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@polka/url": { + "version": "1.0.0-next.23", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.23.tgz", + "integrity": "sha512-C16M+IYz0rgRhWZdCmK+h58JMv8vijAA61gmz2rspCSwKwzBebpdcsiUmwrtJRdphuY30i6BSLEOP8ppbNLyLg==" + }, + "node_modules/@react-hook/intersection-observer": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@react-hook/intersection-observer/-/intersection-observer-3.1.1.tgz", + "integrity": "sha512-OTDx8/wFaRvzFtKl1dEUEXSOqK2zVJHporiTTdC2xO++0e9FEx9wIrPis5q3lqtXeZH9zYGLbk+aB75qNFbbuw==", + "dependencies": { + "@react-hook/passive-layout-effect": "^1.2.0", + "intersection-observer": "^0.10.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/@react-hook/passive-layout-effect": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@react-hook/passive-layout-effect/-/passive-layout-effect-1.2.1.tgz", + "integrity": "sha512-IwEphTD75liO8g+6taS+4oqz+nnroocNfWVHWz7j+N+ZO2vYrc6PV1q7GQhuahL0IOR7JccFTsFKQ/mb6iZWAg==", + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/@sideway/address": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz", + "integrity": "sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@sideway/formula": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==" + }, + "node_modules/@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==" + }, + "node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@slorber/remark-comment": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@slorber/remark-comment/-/remark-comment-1.0.0.tgz", + "integrity": "sha512-RCE24n7jsOj1M0UPvIQCHTe7fI0sFL4S2nwKVWwHyVr/wI/H8GosgsJGyhnsZoGFnD/P2hLf1mSbrrgSLN93NA==", + "dependencies": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.1.0", + "micromark-util-symbol": "^1.0.1" + } + }, + "node_modules/@stitches/core": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@stitches/core/-/core-1.2.8.tgz", + "integrity": "sha512-Gfkvwk9o9kE9r9XNBmJRfV8zONvXThnm1tcuojL04Uy5uRyqg93DC83lDebl0rocZCfKSjUv+fWYtMQmEDJldg==" + }, + "node_modules/@svgr/babel-plugin-add-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz", + "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz", + "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-svg-dynamic-title": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz", + "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-svg-em-dimensions": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz", + "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-transform-react-native-svg": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz", + "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-transform-svg-component": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz", + "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-preset": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz", + "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==", + "dependencies": { + "@svgr/babel-plugin-add-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0", + "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0", + "@svgr/babel-plugin-svg-dynamic-title": "8.0.0", + "@svgr/babel-plugin-svg-em-dimensions": "8.0.0", + "@svgr/babel-plugin-transform-react-native-svg": "8.1.0", + "@svgr/babel-plugin-transform-svg-component": "8.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/core": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", + "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", + "dependencies": { + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "camelcase": "^6.2.0", + "cosmiconfig": "^8.1.3", + "snake-case": "^3.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/hast-util-to-babel-ast": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz", + "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==", + "dependencies": { + "@babel/types": "^7.21.3", + "entities": "^4.4.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/plugin-jsx": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz", + "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==", + "dependencies": { + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "@svgr/hast-util-to-babel-ast": "8.0.0", + "svg-parser": "^2.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@svgr/core": "*" + } + }, + "node_modules/@svgr/plugin-svgo": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-8.1.0.tgz", + "integrity": "sha512-Ywtl837OGO9pTLIN/onoWLmDQ4zFUycI1g76vuKGEz6evR/ZTJlJuz3G/fIkb6OVBJ2g0o6CGJzaEjfmEo3AHA==", + "dependencies": { + "cosmiconfig": "^8.1.3", + "deepmerge": "^4.3.1", + "svgo": "^3.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@svgr/core": "*" + } + }, + "node_modules/@svgr/webpack": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-8.1.0.tgz", + "integrity": "sha512-LnhVjMWyMQV9ZmeEy26maJk+8HTIbd59cH4F2MJ439k9DqejRisfFNGAPvRYlKETuh9LrImlS8aKsBgKjMA8WA==", + "dependencies": { + "@babel/core": "^7.21.3", + "@babel/plugin-transform-react-constant-elements": "^7.21.3", + "@babel/preset-env": "^7.20.2", + "@babel/preset-react": "^7.18.6", + "@babel/preset-typescript": "^7.21.0", + "@svgr/core": "8.1.0", + "@svgr/plugin-jsx": "8.1.0", + "@svgr/plugin-svgo": "8.1.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@szmarczak/http-timer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", + "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", + "dependencies": { + "defer-to-connect": "^2.0.1" + }, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@types/acorn": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@types/acorn/-/acorn-4.0.6.tgz", + "integrity": "sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ==", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bonjour": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz", + "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect-history-api-fallback": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.3.tgz", + "integrity": "sha512-6mfQ6iNvhSKCZJoY6sIG3m0pKkdUcweVNOLuBBKvoWGzl2yRxOJcYOTRyLKt3nxXvBLJWa6QkW//tgbIwJehmA==", + "dependencies": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "node_modules/@types/d3-scale": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz", + "integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-scale-chromatic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.0.3.tgz", + "integrity": "sha512-laXM4+1o5ImZv3RpFAsTRn3TEkzqkytiOY0Dz0sq5cnd1dtNlk6sHLon4OvqaiJb28T0S/TdsBI3Sjsy+keJrw==" + }, + "node_modules/@types/d3-time": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.3.tgz", + "integrity": "sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==" + }, + "node_modules/@types/debug": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.11.tgz", + "integrity": "sha512-R2qflTjHDs4CL6D/6TkqBeIHr54WzZfIxN729xvCNlYIVp2LknlnCro5Yo3frNaX2E5gO9pZ3/QAPVdGmu+q9w==", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/eslint": { + "version": "8.44.7", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.7.tgz", + "integrity": "sha512-f5ORu2hcBbKei97U73mf+l9t4zTGl74IqZ0GQk4oVea/VS8tQZYkUveSYojk+frraAVYId0V2WC9O4PTNru2FQ==", + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" + }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.17.41", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.41.tgz", + "integrity": "sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/gtag.js": { + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/@types/gtag.js/-/gtag.js-0.0.12.tgz", + "integrity": "sha512-YQV9bUsemkzG81Ea295/nF/5GijnD2Af7QhEofh7xu+kvCN6RdodgNwwGWXB5GMI3NoyvQo0odNctoH/qLMIpg==" + }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/history": { + "version": "4.7.11", + "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.11.tgz", + "integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==" + }, + "node_modules/@types/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==" + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==" + }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==" + }, + "node_modules/@types/http-proxy": { + "version": "1.17.14", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.14.tgz", + "integrity": "sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" + }, + "node_modules/@types/mdast": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.3.tgz", + "integrity": "sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/mdx": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.10.tgz", + "integrity": "sha512-Rllzc5KHk0Al5/WANwgSPl1/CwjqCy+AZrGd78zuK+jO9aDM6ffblZ+zIjgPNAaEBmlO0RYDvLNh7wD0zKVgEg==" + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==" + }, + "node_modules/@types/ms": { + "version": "0.7.34", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", + "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==" + }, + "node_modules/@types/node": { + "version": "20.9.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.0.tgz", + "integrity": "sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/node-forge": { + "version": "1.3.9", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.9.tgz", + "integrity": "sha512-meK88cx/sTalPSLSoCzkiUB4VPIFHmxtXm5FaaqRDqBX2i/Sy8bJ4odsan0b20RBjPh06dAQ+OTTdnyQyhJZyQ==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==" + }, + "node_modules/@types/prismjs": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.3.tgz", + "integrity": "sha512-A0D0aTXvjlqJ5ZILMz3rNfDBOx9hHxLZYv2by47Sm/pqW35zzjusrZTryatjN/Rf8Us2gZrJD+KeHbUSTux1Cw==" + }, + "node_modules/@types/prop-types": { + "version": "15.7.10", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.10.tgz", + "integrity": "sha512-mxSnDQxPqsZxmeShFH+uwQ4kO4gcJcGahjjMFeLbKE95IAZiiZyiEepGZjtXJ7hN/yfu0bu9xN2ajcU0JcxX6A==" + }, + "node_modules/@types/qs": { + "version": "6.9.10", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.10.tgz", + "integrity": "sha512-3Gnx08Ns1sEoCrWssEgTSJs/rsT2vhGP+Ja9cnnk9k4ALxinORlQneLXFeFKOTJMOeZUFD1s7w+w2AphTpvzZw==" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==" + }, + "node_modules/@types/react": { + "version": "18.2.37", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.37.tgz", + "integrity": "sha512-RGAYMi2bhRgEXT3f4B92WTohopH6bIXw05FuGlmJEnv/omEn190+QYEIYxIAuIBdKgboYYdVved2p1AxZVQnaw==", + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-router": { + "version": "5.1.20", + "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.20.tgz", + "integrity": "sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==", + "dependencies": { + "@types/history": "^4.7.11", + "@types/react": "*" + } + }, + "node_modules/@types/react-router-config": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/@types/react-router-config/-/react-router-config-5.0.10.tgz", + "integrity": "sha512-Wn6c/tXdEgi9adCMtDwx8Q2vGty6TsPTc/wCQQ9kAlye8UqFxj0vGFWWuhywNfkwqth+SOgJxQTLTZukrqDQmQ==", + "dependencies": { + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router": "^5.1.0" + } + }, + "node_modules/@types/react-router-dom": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.3.3.tgz", + "integrity": "sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==", + "dependencies": { + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router": "*" + } + }, + "node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" + }, + "node_modules/@types/sax": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz", + "integrity": "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/scheduler": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.6.tgz", + "integrity": "sha512-Vlktnchmkylvc9SnwwwozTv04L/e1NykF5vgoQ0XTmI8DD+wxfjQuHuvHS3p0r2jz2x2ghPs2h1FVeDirIteWA==" + }, + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-index": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz", + "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz", + "integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==", + "dependencies": { + "@types/http-errors": "*", + "@types/mime": "*", + "@types/node": "*" + } + }, + "node_modules/@types/sockjs": { + "version": "0.3.36", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", + "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/unist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", + "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==" + }, + "node_modules/@types/ws": { + "version": "8.5.9", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.9.tgz", + "integrity": "sha512-jbdrY0a8lxfdTp/+r7Z4CkycbOFN8WX+IOchLJr3juT/xzbJ8URyTVSJ/hvNdadTgM1mnedb47n+Y31GsFnQlg==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/yargs": { + "version": "17.0.32", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", + "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==" + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==" + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", + "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==" + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==" + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", + "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==" + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", + "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==" + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", + "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==" + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", + "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-opt": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6", + "@webassemblyjs/wast-printer": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", + "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", + "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", + "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", + "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/accepts/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/accepts/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", + "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-assertions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.0.tgz", + "integrity": "sha512-FS7hV565M5l1R08MXqo8odwMTB02C2UqzB17RVgu9EyuYFBqJZ3/ZY97sQD5FewVu1UyDFc1yztUDrAwT0EypA==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/address": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/address/-/address-1.2.2.tgz", + "integrity": "sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/algoliasearch": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.23.3.tgz", + "integrity": "sha512-Le/3YgNvjW9zxIQMRhUHuhiUjAlKY/zsdZpfq4dlLqg6mEm0nL6yk+7f2hDOtLpxsgE4jSzDmvHL7nXdBp5feg==", + "dependencies": { + "@algolia/cache-browser-local-storage": "4.23.3", + "@algolia/cache-common": "4.23.3", + "@algolia/cache-in-memory": "4.23.3", + "@algolia/client-account": "4.23.3", + "@algolia/client-analytics": "4.23.3", + "@algolia/client-common": "4.23.3", + "@algolia/client-personalization": "4.23.3", + "@algolia/client-search": "4.23.3", + "@algolia/logger-common": "4.23.3", + "@algolia/logger-console": "4.23.3", + "@algolia/recommend": "4.23.3", + "@algolia/requester-browser-xhr": "4.23.3", + "@algolia/requester-common": "4.23.3", + "@algolia/requester-node-http": "4.23.3", + "@algolia/transporter": "4.23.3" + } + }, + "node_modules/algoliasearch-helper": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.21.0.tgz", + "integrity": "sha512-hjVOrL15I3Y3K8xG0icwG1/tWE+MocqBrhW6uVBWpU+/kVEMK0BnM2xdssj6mZM61eJ4iRxHR0djEI3ENOpR8w==", + "dependencies": { + "@algolia/events": "^4.0.1" + }, + "peerDependencies": { + "algoliasearch": ">= 3.1 < 6" + } + }, + "node_modules/anser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/anser/-/anser-2.1.1.tgz", + "integrity": "sha512-nqLm4HxOTpeLOxcmB3QWmV5TcDFhW9y/fyQ+hivtDFcK4OQ+pQ5fzPnXHM1Mfcm0VkLtvVi1TCPr++Qy0Q/3EQ==" + }, + "node_modules/ansi-align": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", + "dependencies": { + "string-width": "^4.1.0" + } + }, + "node_modules/ansi-align/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/ansi-align/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "engines": [ + "node >= 0.8.0" + ], + "bin": { + "ansi-html": "bin/ansi-html" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/astring": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/astring/-/astring-1.8.6.tgz", + "integrity": "sha512-ISvCdHdlTDlH5IpxQJIex7BWBywFWgjJSVdwst+/iQCoEYnyOaQ95+X1JGshuBjGp6nxKUy1jMgE3zPqN7fQdg==", + "bin": { + "astring": "bin/astring" + } + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/autoprefixer": { + "version": "10.4.19", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz", + "integrity": "sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "browserslist": "^4.23.0", + "caniuse-lite": "^1.0.30001599", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/babel-loader": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.1.3.tgz", + "integrity": "sha512-xG3ST4DglodGf8qSwv0MdeWLhrDsw/32QMdTO5T1ZIp9gQur0HkCyFs7Awskr10JKXFXwpAhiCuYX5oGXnRGbw==", + "dependencies": { + "find-cache-dir": "^4.0.0", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 14.15.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0", + "webpack": ">=5" + } + }, + "node_modules/babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "dependencies": { + "object.assign": "^4.1.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.6.tgz", + "integrity": "sha512-jhHiWVZIlnPbEUKSSNb9YoWcQGdlTLq7z1GHL4AjFxaoOUMuuEVJ+Y4pAaQUGOGk93YsVCKPbqbfw3m0SM6H8Q==", + "dependencies": { + "@babel/compat-data": "^7.22.6", + "@babel/helper-define-polyfill-provider": "^0.4.3", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.8.6", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.6.tgz", + "integrity": "sha512-leDIc4l4tUgU7str5BWLS2h8q2N4Nf6lGZP6UrNDxdtfF2g69eJ5L0H7S8A5Ln/arfFAfHor5InAdZuIOwZdgQ==", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.4.3", + "core-js-compat": "^3.33.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.3.tgz", + "integrity": "sha512-8sHeDOmXC8csczMrYEOf0UTNa4yE2SxV5JGeT/LP1n0OYVDUUFPxG9vdk2AlDlIit4t+Kf0xCtpgXPBwnn/9pw==", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.4.3" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==" + }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/bonjour-service": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.1.1.tgz", + "integrity": "sha512-Z/5lQRMOG9k7W+FkeGTNjh7htqn/2LMnfOvBZ8pynNZCM9MwkQkI3zeI4oz09uWdcgmgHugVvBqxGg4VQJ5PCg==", + "dependencies": { + "array-flatten": "^2.1.2", + "dns-equal": "^1.0.0", + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" + }, + "node_modules/boxen": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-6.2.1.tgz", + "integrity": "sha512-H4PEsJXfFI/Pt8sjDWbHlQPx4zL/bvSQjcilJmaulGt5mLDorHOHpmdXAJcBcmru7PhYSp/cDMWRko4ZUMFkSw==", + "dependencies": { + "ansi-align": "^3.0.1", + "camelcase": "^6.2.0", + "chalk": "^4.1.2", + "cli-boxes": "^3.0.0", + "string-width": "^5.0.1", + "type-fest": "^2.5.0", + "widest-line": "^4.0.1", + "wrap-ansi": "^8.0.1" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", + "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001587", + "electron-to-chromium": "^1.4.668", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cacheable-lookup": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", + "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", + "engines": { + "node": ">=14.16" + } + }, + "node_modules/cacheable-request": { + "version": "10.2.14", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz", + "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==", + "dependencies": { + "@types/http-cache-semantics": "^4.0.2", + "get-stream": "^6.0.1", + "http-cache-semantics": "^4.1.1", + "keyv": "^4.5.3", + "mimic-response": "^4.0.0", + "normalize-url": "^8.0.0", + "responselike": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/cacheable-request/node_modules/normalize-url": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.0.tgz", + "integrity": "sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw==", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/call-bind": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", + "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", + "dependencies": { + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.1", + "set-function-length": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dependencies": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001632", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001632.tgz", + "integrity": "sha512-udx3o7yHJfUxMLkGohMlVHCvFvWmirKh9JAH/d7WOLPetlH+LTL5cocMZ0t7oZx/mdlOWXti97xLZWc8uURRHg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "engines": { + "node": ">=10" + } + }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/cheerio": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", + "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "htmlparser2": "^8.0.1", + "parse5": "^7.0.0", + "parse5-htmlparser2-tree-adapter": "^7.0.0" + }, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/clean-css": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.2.tgz", + "integrity": "sha512-JVJbM+f3d3Q704rF4bqQ5UUyTtuJ0JRKNbTKVEeujCCBoMdkEi+V+e8oktO9qGQNSvHrFTM6JZRXrUvGR1czww==", + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 10.0" + } + }, + "node_modules/clean-css/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/clean-set": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/clean-set/-/clean-set-1.1.2.tgz", + "integrity": "sha512-cA8uCj0qSoG9e0kevyOWXwPaELRPVg5Pxp6WskLMwerx257Zfnh8Nl0JBH59d7wQzij2CK7qEfJQK3RjuKKIug==" + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-boxes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", + "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-table3": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz", + "integrity": "sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==", + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "node_modules/cli-table3/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/cli-table3/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/clone-deep/node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/clsx": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz", + "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==", + "engines": { + "node": ">=6" + } + }, + "node_modules/codesandbox-import-util-types": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/codesandbox-import-util-types/-/codesandbox-import-util-types-2.2.3.tgz", + "integrity": "sha512-Qj00p60oNExthP2oR3vvXmUGjukij+rxJGuiaKM6tyUmSyimdZsqHI/TUvFFClAffk9s7hxGnQgWQ8KCce27qQ==" + }, + "node_modules/collapse-white-space": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-2.1.0.tgz", + "integrity": "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/colord": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==" + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==" + }, + "node_modules/combine-promises": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/combine-promises/-/combine-promises-1.2.0.tgz", + "integrity": "sha512-VcQB1ziGD0NXrhKxiwyNbCDmRzs/OShMs2GqW2DlU2A/Sd0nQxE1oWDAE5O0ygSx5mgQOn9eIFh7yKPgFRVkPQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/common-path-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", + "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==" + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compressible/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dependencies": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/compression/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "node_modules/configstore": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-6.0.0.tgz", + "integrity": "sha512-cD31W1v3GqUlQvbBCGcXmd2Nj9SvLDOP1oQ0YFuLETufzSPaKp11rYBsSOm7rCsW3OnIRAFM3OxRhceaXNYHkA==", + "dependencies": { + "dot-prop": "^6.0.1", + "graceful-fs": "^4.2.6", + "unique-string": "^3.0.0", + "write-file-atomic": "^3.0.3", + "xdg-basedir": "^5.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/yeoman/configstore?sponsor=1" + } + }, + "node_modules/connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/consola": { + "version": "2.15.3", + "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", + "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==" + }, + "node_modules/content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" + }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/copy-text-to-clipboard": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/copy-text-to-clipboard/-/copy-text-to-clipboard-3.2.0.tgz", + "integrity": "sha512-RnJFp1XR/LOBDckxTib5Qjr/PMfkatD0MUCQgdpqS8MdKiNUzBjAQBEN6oUy+jW7LI93BBG3DtMB2KOOKpGs2Q==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/copy-webpack-plugin": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz", + "integrity": "sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==", + "dependencies": { + "fast-glob": "^3.2.11", + "glob-parent": "^6.0.1", + "globby": "^13.1.1", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + } + }, + "node_modules/copy-webpack-plugin/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/copy-webpack-plugin/node_modules/globby": { + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", + "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", + "dependencies": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.3.0", + "ignore": "^5.2.4", + "merge2": "^1.4.1", + "slash": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/copy-webpack-plugin/node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/core-js": { + "version": "3.33.2", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.33.2.tgz", + "integrity": "sha512-XeBzWI6QL3nJQiHmdzbAOiMYqjrb7hwU7A39Qhvd/POSa/t9E1AeZyEZx3fNvp/vtM8zXwhoL0FsiS0hD0pruQ==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-compat": { + "version": "3.33.2", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.33.2.tgz", + "integrity": "sha512-axfo+wxFVxnqf8RvxTzoAlzW4gRoacrHeoFlc9n0x50+7BEyZL/Rt3hicaED1/CEd7I6tPCPVUYcJwCMO5XUYw==", + "dependencies": { + "browserslist": "^4.22.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-pure": { + "version": "3.33.2", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.33.2.tgz", + "integrity": "sha512-a8zeCdyVk7uF2elKIGz67AjcXOxjRbwOLz8SbklEso1V+2DoW4OkAMZN9S9GBgvZIaqQi/OemFX4OiSoQEmg1Q==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "node_modules/cose-base": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz", + "integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==", + "dependencies": { + "layout-base": "^1.0.0" + } + }, + "node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "dependencies": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/crelt": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==" + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-random-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz", + "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==", + "dependencies": { + "type-fest": "^1.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/crypto-random-string/node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/css-declaration-sorter": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.2.0.tgz", + "integrity": "sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow==", + "engines": { + "node": "^14 || ^16 || >=18" + }, + "peerDependencies": { + "postcss": "^8.0.9" + } + }, + "node_modules/css-loader": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.8.1.tgz", + "integrity": "sha512-xDAXtEVGlD0gJ07iclwWVkLoZOpEvAWaSyf6W18S2pOC//K8+qUDIx8IIT3D+HjnmkJPQeesOPv5aiUaJsCM2g==", + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.21", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.3", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.3.8" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/css-minimizer-webpack-plugin": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-5.0.1.tgz", + "integrity": "sha512-3caImjKFQkS+ws1TGcFn0V1HyDJFq1Euy589JlD6/3rV2kj+w7r5G9WDMgSHvpvXHNZ2calVypZWuEDQd9wfLg==", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "cssnano": "^6.0.1", + "jest-worker": "^29.4.3", + "postcss": "^8.4.24", + "schema-utils": "^4.0.1", + "serialize-javascript": "^6.0.1" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@parcel/css": { + "optional": true + }, + "@swc/css": { + "optional": true + }, + "clean-css": { + "optional": true + }, + "csso": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "lightningcss": { + "optional": true + } + } + }, + "node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-tree": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "dependencies": { + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-6.1.2.tgz", + "integrity": "sha512-rYk5UeX7VAM/u0lNqewCdasdtPK81CgX8wJFLEIXHbV2oldWRgJAsZrdhRXkV1NJzA2g850KiFm9mMU2HxNxMA==", + "dependencies": { + "cssnano-preset-default": "^6.1.2", + "lilconfig": "^3.1.1" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/cssnano" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/cssnano-preset-advanced": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/cssnano-preset-advanced/-/cssnano-preset-advanced-6.1.2.tgz", + "integrity": "sha512-Nhao7eD8ph2DoHolEzQs5CfRpiEP0xa1HBdnFZ82kvqdmbwVBUr2r1QuQ4t1pi+D1ZpqpcO4T+wy/7RxzJ/WPQ==", + "dependencies": { + "autoprefixer": "^10.4.19", + "browserslist": "^4.23.0", + "cssnano-preset-default": "^6.1.2", + "postcss-discard-unused": "^6.0.5", + "postcss-merge-idents": "^6.0.3", + "postcss-reduce-idents": "^6.0.3", + "postcss-zindex": "^6.0.2" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/cssnano-preset-default": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-6.1.2.tgz", + "integrity": "sha512-1C0C+eNaeN8OcHQa193aRgYexyJtU8XwbdieEjClw+J9d94E41LwT6ivKH0WT+fYwYWB0Zp3I3IZ7tI/BbUbrg==", + "dependencies": { + "browserslist": "^4.23.0", + "css-declaration-sorter": "^7.2.0", + "cssnano-utils": "^4.0.2", + "postcss-calc": "^9.0.1", + "postcss-colormin": "^6.1.0", + "postcss-convert-values": "^6.1.0", + "postcss-discard-comments": "^6.0.2", + "postcss-discard-duplicates": "^6.0.3", + "postcss-discard-empty": "^6.0.3", + "postcss-discard-overridden": "^6.0.2", + "postcss-merge-longhand": "^6.0.5", + "postcss-merge-rules": "^6.1.1", + "postcss-minify-font-values": "^6.1.0", + "postcss-minify-gradients": "^6.0.3", + "postcss-minify-params": "^6.1.0", + "postcss-minify-selectors": "^6.0.4", + "postcss-normalize-charset": "^6.0.2", + "postcss-normalize-display-values": "^6.0.2", + "postcss-normalize-positions": "^6.0.2", + "postcss-normalize-repeat-style": "^6.0.2", + "postcss-normalize-string": "^6.0.2", + "postcss-normalize-timing-functions": "^6.0.2", + "postcss-normalize-unicode": "^6.1.0", + "postcss-normalize-url": "^6.0.2", + "postcss-normalize-whitespace": "^6.0.2", + "postcss-ordered-values": "^6.0.2", + "postcss-reduce-initial": "^6.1.0", + "postcss-reduce-transforms": "^6.0.2", + "postcss-svgo": "^6.0.3", + "postcss-unique-selectors": "^6.0.4" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/cssnano-utils": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-4.0.2.tgz", + "integrity": "sha512-ZR1jHg+wZ8o4c3zqf1SIUSTIvm/9mU343FMR6Obe/unskbvpGhZOo1J6d/r8D1pzkRQYuwbcH3hToOuoA2G7oQ==", + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/csso": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", + "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", + "dependencies": { + "css-tree": "~2.2.0" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/css-tree": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", + "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", + "dependencies": { + "mdn-data": "2.0.28", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.28", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", + "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==" + }, + "node_modules/csstype": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" + }, + "node_modules/cytoscape": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.27.0.tgz", + "integrity": "sha512-pPZJilfX9BxESwujODz5pydeGi+FBrXq1rcaB1mfhFXXFJ9GjE6CNndAk+8jPzoXGD+16LtSS4xlYEIUiW4Abg==", + "dependencies": { + "heap": "^0.2.6", + "lodash": "^4.17.21" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/cytoscape-cose-bilkent": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz", + "integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==", + "dependencies": { + "cose-base": "^1.0.0" + }, + "peerDependencies": { + "cytoscape": "^3.2.0" + } + }, + "node_modules/cytoscape-fcose": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz", + "integrity": "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==", + "dependencies": { + "cose-base": "^2.2.0" + }, + "peerDependencies": { + "cytoscape": "^3.2.0" + } + }, + "node_modules/cytoscape-fcose/node_modules/cose-base": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-2.2.0.tgz", + "integrity": "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==", + "dependencies": { + "layout-base": "^2.0.0" + } + }, + "node_modules/cytoscape-fcose/node_modules/layout-base": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-2.0.1.tgz", + "integrity": "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==" + }, + "node_modules/d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "dependencies": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "node_modules/d3": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.8.5.tgz", + "integrity": "sha512-JgoahDG51ncUfJu6wX/1vWQEqOflgXyl4MaHqlcSruTez7yhaRKR9i8VjjcQGeS2en/jnFivXuaIMnseMMt0XA==", + "dependencies": { + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-axis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-brush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-chord": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", + "dependencies": { + "d3-path": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-contour": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", + "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", + "dependencies": { + "d3-array": "^3.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "dependencies": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/d3-dsv/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "dependencies": { + "d3-dsv": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.0.tgz", + "integrity": "sha512-JEo5HxXDdDYXCaWdwLRt79y7giK8SbhZJbFWXqbRTolCHFI5jRqteLzCsq51NKbUoX0PjBVSohxrx+NoOUujYA==", + "dependencies": { + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-polygon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-sankey": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz", + "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==", + "dependencies": { + "d3-array": "1 - 2", + "d3-shape": "^1.2.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-array": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", + "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", + "dependencies": { + "internmap": "^1.0.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==" + }, + "node_modules/d3-sankey/node_modules/d3-shape": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", + "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", + "dependencies": { + "d3-path": "1" + } + }, + "node_modules/d3-sankey/node_modules/internmap": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", + "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==" + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale-chromatic": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz", + "integrity": "sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==", + "dependencies": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dagre-d3-es": { + "version": "7.0.10", + "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.10.tgz", + "integrity": "sha512-qTCQmEhcynucuaZgY5/+ti3X/rnszKZhEQH/ZdWdtP1tA/y3VoHJzcVrO9pjjJCNpigfscAtoUB5ONcd2wNn0A==", + "dependencies": { + "d3": "^7.8.2", + "lodash-es": "^4.17.21" + } + }, + "node_modules/dayjs": { + "version": "1.11.10", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", + "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==" + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decode-named-character-reference": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", + "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-gateway": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "engines": { + "node": ">=10" + } + }, + "node_modules/define-data-property": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", + "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "dependencies": { + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "engines": { + "node": ">=8" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/del": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz", + "integrity": "sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==", + "dependencies": { + "globby": "^11.0.1", + "graceful-fs": "^4.2.4", + "is-glob": "^4.0.1", + "is-path-cwd": "^2.2.0", + "is-path-inside": "^3.0.2", + "p-map": "^4.0.0", + "rimraf": "^3.0.2", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/delaunator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.0.tgz", + "integrity": "sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==", + "dependencies": { + "robust-predicates": "^3.0.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==" + }, + "node_modules/detect-port": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.5.1.tgz", + "integrity": "sha512-aBzdj76lueB6uUst5iAs7+0H/oOjqI5D16XUWxlWMIMROhcM0rfsNVk93zTngq1dDNpoXRr++Sus7ETAExppAQ==", + "dependencies": { + "address": "^1.0.1", + "debug": "4" + }, + "bin": { + "detect": "bin/detect-port.js", + "detect-port": "bin/detect-port.js" + } + }, + "node_modules/detect-port-alt": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz", + "integrity": "sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==", + "dependencies": { + "address": "^1.0.1", + "debug": "^2.6.0" + }, + "bin": { + "detect": "bin/detect-port", + "detect-port": "bin/detect-port" + }, + "engines": { + "node": ">= 4.2.1" + } + }, + "node_modules/detect-port-alt/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/detect-port-alt/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/diff": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", + "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==" + }, + "node_modules/dns-packet": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", + "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", + "dependencies": { + "@leichtgewicht/ip-codec": "^2.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/docusaurus-plugin-sass": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/docusaurus-plugin-sass/-/docusaurus-plugin-sass-0.2.5.tgz", + "integrity": "sha512-Z+D0fLFUKcFpM+bqSUmqKIU+vO+YF1xoEQh5hoFreg2eMf722+siwXDD+sqtwU8E4MvVpuvsQfaHwODNlxJAEg==", + "dependencies": { + "sass-loader": "^10.1.1" + }, + "peerDependencies": { + "@docusaurus/core": "^2.0.0-beta || ^3.0.0-alpha", + "sass": "^1.30.0" + } + }, + "node_modules/dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "dependencies": { + "utila": "~0.4" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/dompurify": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.6.tgz", + "integrity": "sha512-ilkD8YEnnGh1zJ240uJsW7AzE+2qpbOUYjacomn3AvJ6J4JhKGSZ2nh4wUIXPZrEPppaCLx5jFe8T89Rk8tQ7w==" + }, + "node_modules/domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/dot-prop": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", + "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dot-prop/node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/dotenv": { + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", + "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/motdotla/dotenv?sponsor=1" + } + }, + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/electron-to-chromium": { + "version": "1.4.699", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.699.tgz", + "integrity": "sha512-I7q3BbQi6e4tJJN5CRcyvxhK0iJb34TV8eJQcgh+fR2fQ8miMgZcEInckCo1U9exDHbfz7DLDnFn8oqH/VcRKw==" + }, + "node_modules/elkjs": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/elkjs/-/elkjs-0.8.2.tgz", + "integrity": "sha512-L6uRgvZTH+4OF5NE/MBbzQx/WYpru1xCBE9respNj6qznEewGUIfhzmm7horWWxbNO2M0WckQypGctR8lH79xQ==" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + }, + "node_modules/emojilib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/emojilib/-/emojilib-2.4.0.tgz", + "integrity": "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==" + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/emoticon": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/emoticon/-/emoticon-4.0.1.tgz", + "integrity": "sha512-dqx7eA9YaqyvYtUhJwT4rC1HIp82j5ybS1/vQ42ur+jBe17dJMwZE4+gvL1XadSFfxaPFFGt3Xsw+Y8akThDlw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", + "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-module-lexer": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.1.tgz", + "integrity": "sha512-JUFAyicQV9mXc3YRxPnDlrfBKpqt6hUYzz9/boprUJHs4e4KVr3XwOF70doO6gwXUor6EWZJAyWAfKki84t20Q==" + }, + "node_modules/es5-ext": { + "version": "0.10.62", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", + "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", + "hasInstallScript": true, + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "dependencies": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-carriage": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/escape-carriage/-/escape-carriage-1.3.1.tgz", + "integrity": "sha512-GwBr6yViW3ttx1kb7/Oh+gKQ1/TrhYwxKqVmg5gS+BK+Qe2KrOa/Vh7w3HPBvgGf0LfcDGoY9I6NHKoA5Hozhw==" + }, + "node_modules/escape-goat": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-4.0.0.tgz", + "integrity": "sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-util-attach-comments": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-attach-comments/-/estree-util-attach-comments-3.0.0.tgz", + "integrity": "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==", + "dependencies": { + "@types/estree": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-build-jsx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/estree-util-build-jsx/-/estree-util-build-jsx-3.0.1.tgz", + "integrity": "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "estree-walker": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-is-identifier-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-to-js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/estree-util-to-js/-/estree-util-to-js-2.0.0.tgz", + "integrity": "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "astring": "^1.8.0", + "source-map": "^0.7.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-value-to-estree": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/estree-util-value-to-estree/-/estree-util-value-to-estree-3.1.1.tgz", + "integrity": "sha512-5mvUrF2suuv5f5cGDnDphIy4/gW86z82kl5qG6mM9z04SEQI4FB5Apmaw/TGEf3l55nLtMs5s51dmhUzvAHQCA==", + "dependencies": { + "@types/estree": "^1.0.0", + "is-plain-obj": "^4.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/remcohaszing" + } + }, + "node_modules/estree-util-visit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/estree-util-visit/-/estree-util-visit-2.0.0.tgz", + "integrity": "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eta": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/eta/-/eta-2.2.0.tgz", + "integrity": "sha512-UVQ72Rqjy/ZKQalzV5dCCJP80GrmPrMxh6NlNf+erV6ObL0ZFkhCstWRawS85z3smdr3d2wXPsZEY7rDPfGd2g==", + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "url": "https://github.com/eta-dev/eta?sponsor=1" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eval": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/eval/-/eval-0.1.8.tgz", + "integrity": "sha512-EzV94NYKoO09GLXGjXj9JIlXijVck4ONSr5wiCWDvhsvj5jxSrzTmRU/9C1DyB6uToszLs8aifA6NQ7lEQdvFw==", + "dependencies": { + "@types/node": "*", + "require-like": ">= 0.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/express/node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/express/node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "node_modules/express/node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "dependencies": { + "type": "^2.7.2" + } + }, + "node_modules/ext/node_modules/type": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "node_modules/fast-url-parser": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz", + "integrity": "sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ==", + "dependencies": { + "punycode": "^1.3.2" + } + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fault": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fault/-/fault-2.0.1.tgz", + "integrity": "sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==", + "dependencies": { + "format": "^0.2.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/feed": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/feed/-/feed-4.2.2.tgz", + "integrity": "sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ==", + "dependencies": { + "xml-js": "^1.6.11" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/file-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/file-loader/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/file-loader/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/file-loader/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "node_modules/file-loader/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/filesize": { + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", + "integrity": "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/find-cache-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-4.0.0.tgz", + "integrity": "sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==", + "dependencies": { + "common-path-prefix": "^3.0.0", + "pkg-dir": "^7.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/find-up": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", + "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", + "dependencies": { + "locate-path": "^7.1.0", + "path-exists": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "bin": { + "flat": "cli.js" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", + "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/fork-ts-checker-webpack-plugin": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.3.tgz", + "integrity": "sha512-SbH/l9ikmMWycd5puHJKTkZJKddF4iRLyW3DeZ08HTI7NGyLS38MXd/KGgeWumQO7YNQbW2u/NtPT2YowbPaGQ==", + "dependencies": { + "@babel/code-frame": "^7.8.3", + "@types/json-schema": "^7.0.5", + "chalk": "^4.1.0", + "chokidar": "^3.4.2", + "cosmiconfig": "^6.0.0", + "deepmerge": "^4.2.2", + "fs-extra": "^9.0.0", + "glob": "^7.1.6", + "memfs": "^3.1.2", + "minimatch": "^3.0.4", + "schema-utils": "2.7.0", + "semver": "^7.3.2", + "tapable": "^1.0.0" + }, + "engines": { + "node": ">=10", + "yarn": ">=1.0.0" + }, + "peerDependencies": { + "eslint": ">= 6", + "typescript": ">= 2.7", + "vue-template-compiler": "*", + "webpack": ">= 4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + }, + "vue-template-compiler": { + "optional": true + } + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/schema-utils": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", + "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", + "dependencies": { + "@types/json-schema": "^7.0.4", + "ajv": "^6.12.2", + "ajv-keywords": "^3.4.1" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/form-data-encoder": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", + "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==", + "engines": { + "node": ">= 14.17" + } + }, + "node_modules/format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fs-monkey": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.5.tgz", + "integrity": "sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew==" + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", + "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", + "dependencies": { + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-own-enumerable-property-symbols": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==" + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/github-slugger": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.5.0.tgz", + "integrity": "sha512-wIh+gKBI9Nshz2o46B0B3f5k/W+WI9ZAv6y5Dn5WJ5SK1t0TnDimB4WE5rmTD05ZAIn8HALCZVmCsvj0w0v0lw==" + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" + }, + "node_modules/global-dirs": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", + "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==", + "dependencies": { + "ini": "2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/global-dirs/node_modules/ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "dependencies": { + "global-prefix": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dependencies": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/got": { + "version": "12.6.1", + "resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz", + "integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==", + "dependencies": { + "@sindresorhus/is": "^5.2.0", + "@szmarczak/http-timer": "^5.0.1", + "cacheable-lookup": "^7.0.0", + "cacheable-request": "^10.2.8", + "decompress-response": "^6.0.0", + "form-data-encoder": "^2.1.2", + "get-stream": "^6.0.1", + "http2-wrapper": "^2.1.10", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^3.0.0", + "responselike": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/got/node_modules/@sindresorhus/is": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", + "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "node_modules/gray-matter": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", + "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==", + "dependencies": { + "js-yaml": "^3.13.1", + "kind-of": "^6.0.2", + "section-matter": "^1.0.0", + "strip-bom-string": "^1.0.0" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/gray-matter/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/gray-matter/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/gzip-size": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", + "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", + "dependencies": { + "duplexer": "^0.1.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", + "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", + "dependencies": { + "get-intrinsic": "^1.2.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-yarn": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-3.0.0.tgz", + "integrity": "sha512-IrsVwUHhEULx3R8f/aA8AHuEzAorplsab/v8HBzEiIukwq5i/EC+xmOW+HfP1OaDP+2JkgT1yILHN2O3UFIbcA==", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hast-util-from-parse5": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.1.tgz", + "integrity": "sha512-Er/Iixbc7IEa7r/XLtuG52zoqn/b3Xng/w6aZQ0xGVxzhw5xUFxcRqdPzP6yFi/4HBYRaifaI5fQ1RH8n0ZeOQ==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "hastscript": "^8.0.0", + "property-information": "^6.0.0", + "vfile": "^6.0.0", + "vfile-location": "^5.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-parse-selector": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", + "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-raw": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.0.3.tgz", + "integrity": "sha512-ICWvVOF2fq4+7CMmtCPD5CM4QKjPbHpPotE6+8tDooV0ZuyJVUzHsrNX+O5NaRbieTf0F7FfeBOMAwi6Td0+yQ==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "@ungap/structured-clone": "^1.0.0", + "hast-util-from-parse5": "^8.0.0", + "hast-util-to-parse5": "^8.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "parse5": "^7.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-estree": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hast-util-to-estree/-/hast-util-to-estree-3.1.0.tgz", + "integrity": "sha512-lfX5g6hqVh9kjS/B9E2gSkvHH4SZNiQFiqWS0x9fENzEl+8W12RqdRxX6d/Cwxi30tPQs3bIO+aolQJNp1bIyw==", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-attach-comments": "^3.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-object": "^0.4.0", + "unist-util-position": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-jsx-runtime": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.0.tgz", + "integrity": "sha512-H/y0+IWPdsLLS738P8tDnrQ8Z+dj12zQQ6WC11TIM21C8WFVoIxcqWXf2H3hiTVZjF1AWqoimGwrTWecWrnmRQ==", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-object": "^1.0.0", + "unist-util-position": "^5.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/inline-style-parser": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.2.tgz", + "integrity": "sha512-EcKzdTHVe8wFVOGEYXiW9WmJXPjqi1T+234YpJr98RiFYKHV3cdy1+3mkTE+KHTHxFFLH51SfaGOoUdW+v7ViQ==" + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/style-to-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.5.tgz", + "integrity": "sha512-rDRwHtoDD3UMMrmZ6BzOW0naTjMsVZLIjsGleSKS/0Oz+cgCfAPRspaqJuE8rDzpKha/nEvnM0IF4seEAZUTKQ==", + "dependencies": { + "inline-style-parser": "0.2.2" + } + }, + "node_modules/hast-util-to-parse5": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.0.tgz", + "integrity": "sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hastscript": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-8.0.0.tgz", + "integrity": "sha512-dMOtzCEd3ABUeSIISmrETiKuyydk1w0pa+gE/uormcTpSYuaNJPbX1NU3JLyscSLjwAQM8bWMhhIlnCqnRvDTw==", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^4.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "bin": { + "he": "bin/he" + } + }, + "node_modules/heap": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.7.tgz", + "integrity": "sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==" + }, + "node_modules/history": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", + "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==", + "dependencies": { + "@babel/runtime": "^7.1.2", + "loose-envify": "^1.2.0", + "resolve-pathname": "^3.0.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0", + "value-equal": "^1.0.1" + } + }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hoist-non-react-statics/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", + "dependencies": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "node_modules/hpack.js/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/hpack.js/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/hpack.js/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/hpack.js/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/html-entities": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.4.0.tgz", + "integrity": "sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ] + }, + "node_modules/html-minifier-terser": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-7.2.0.tgz", + "integrity": "sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA==", + "dependencies": { + "camel-case": "^4.1.2", + "clean-css": "~5.3.2", + "commander": "^10.0.0", + "entities": "^4.4.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.15.1" + }, + "bin": { + "html-minifier-terser": "cli.js" + }, + "engines": { + "node": "^14.13.1 || >=16.0.0" + } + }, + "node_modules/html-minifier-terser/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "engines": { + "node": ">=14" + } + }, + "node_modules/html-tags": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz", + "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/html-webpack-plugin": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.3.tgz", + "integrity": "sha512-6YrDKTuqaP/TquFH7h4srYWsZx+x6k6+FbsTm0ziCwGHDP78Unr1r9F/H4+sGmMbX08GQcJ+K64x55b+7VM/jg==", + "dependencies": { + "@types/html-minifier-terser": "^6.0.0", + "html-minifier-terser": "^6.0.2", + "lodash": "^4.17.21", + "pretty-error": "^4.0.0", + "tapable": "^2.0.0" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/html-webpack-plugin" + }, + "peerDependencies": { + "webpack": "^5.20.0" + } + }, + "node_modules/html-webpack-plugin/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "engines": { + "node": ">= 12" + } + }, + "node_modules/html-webpack-plugin/node_modules/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", + "dependencies": { + "camel-case": "^4.1.2", + "clean-css": "^5.2.2", + "commander": "^8.3.0", + "he": "^1.2.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.10.0" + }, + "bin": { + "html-minifier-terser": "cli.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==" + }, + "node_modules/http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==" + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==" + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-middleware": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", + "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "dependencies": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@types/express": "^4.17.13" + }, + "peerDependenciesMeta": { + "@types/express": { + "optional": true + } + } + }, + "node_modules/http-proxy-middleware/node_modules/is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/http2-wrapper": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.0.tgz", + "integrity": "sha512-kZB0wxMo0sh1PehyjJUWRFEd99KC5TLjZ2cULC4f9iqJBAmKQQXEICjxl5iPJRwP40dpeHFqqhm7tYCvODpqpQ==", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.2.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/image-size": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.1.1.tgz", + "integrity": "sha512-541xKlUw6jr/6gGuk92F+mYM5zaFAc5ahphvkqvNe2bQ6gVBkd6bfrmVJ2t4KDAfikAYZyIqTnktX3i6/aQDrQ==", + "dependencies": { + "queue": "6.0.2" + }, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=16.x" + } + }, + "node_modules/immer": { + "version": "9.0.21", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", + "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/immutable": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.6.tgz", + "integrity": "sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==" + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-lazy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", + "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/infima": { + "version": "0.2.0-alpha.43", + "resolved": "https://registry.npmjs.org/infima/-/infima-0.2.0-alpha.43.tgz", + "integrity": "sha512-2uw57LvUqW0rK/SWYnd/2rRfxNA5DDNOh33jxF7fy46VWoNhGxiUQyVZHbBMjQ33mQem0cjdDVwgWVAmlRfgyQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, + "node_modules/inline-style-parser": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", + "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==" + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/intersection-observer": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/intersection-observer/-/intersection-observer-0.10.0.tgz", + "integrity": "sha512-fn4bQ0Xq8FTej09YC/jqKZwtijpvARlRp6wxL5WTA6yPe2YWSJ5RJh7Nm79rK2qB0wr6iDQzH60XGq5V/7u8YQ==" + }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/ipaddr.js": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.1.0.tgz", + "integrity": "sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-ci": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", + "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", + "dependencies": { + "ci-info": "^3.2.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-installed-globally": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", + "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", + "dependencies": { + "global-dirs": "^3.0.0", + "is-path-inside": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-npm": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-6.0.0.tgz", + "integrity": "sha512-JEjxbSmtPSt1c8XTkVrlujcXdKV1/tvuQ7GwKcAlyiVLeYFQ2VHat8xfrDJsIkhCdF/tZ7CiIR3sy141c6+gPQ==", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-reference": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz", + "integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-root": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", + "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-yarn-global": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.4.1.tgz", + "integrity": "sha512-/kppl+R+LO5VmhYSEWARUFjodS25D68gvj8W7z0I7OWhUla5xWu8KL6CtB2V0R6yqhnRgbcaREMr4EEM6htLPQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jiti": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", + "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/joi": { + "version": "17.11.0", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.11.0.tgz", + "integrity": "sha512-NgB+lZLNoqISVy1rZocE9PZI36bL/77ie924Ri43yEvi9GUUMPeyVIr8KdFTMUlby1p0PBYMk9spIxEUQYqrJQ==", + "dependencies": { + "@hapi/hoek": "^9.0.0", + "@hapi/topo": "^5.0.0", + "@sideway/address": "^4.1.3", + "@sideway/formula": "^3.0.1", + "@sideway/pinpoint": "^2.0.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/khroma": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz", + "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==" + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "engines": { + "node": ">=6" + } + }, + "node_modules/klona": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", + "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/latest-version": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-7.0.0.tgz", + "integrity": "sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg==", + "dependencies": { + "package-json": "^8.1.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/launch-editor": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.6.1.tgz", + "integrity": "sha512-eB/uXmFVpY4zezmGp5XtU21kwo7GBbKB+EQ+UZeWtGb9yAM5xt/Evk+lYH3eRNAtId+ej4u7TYPFZ07w4s7rRw==", + "dependencies": { + "picocolors": "^1.0.0", + "shell-quote": "^1.8.1" + } + }, + "node_modules/layout-base": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz", + "integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==" + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/lilconfig": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", + "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/locate-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "dependencies": { + "p-locate": "^6.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" + }, + "node_modules/lodash.escape": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-4.0.1.tgz", + "integrity": "sha512-nXEOnb/jK9g0DYMr1/Xvq6l5xMD7GDG55+GSYIYmS0G4tBk/hURD4JR9WCavs04t33WmJx9kCyp9vJ+mr4BOUw==" + }, + "node_modules/lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==" + }, + "node_modules/lodash.invokemap": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.invokemap/-/lodash.invokemap-4.6.0.tgz", + "integrity": "sha512-CfkycNtMqgUlfjfdh2BhKO/ZXrP8ePOX5lEU/g0R3ItJcnuxWDwokMGKx1hWcfOikmyOVx6X9IwWnDGlgKl61w==" + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==" + }, + "node_modules/lodash.pullall": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.pullall/-/lodash.pullall-4.2.0.tgz", + "integrity": "sha512-VhqxBKH0ZxPpLhiu68YD1KnHmbhQJQctcipvmFnqIBDYzcIHzf3Zpu0tpeOKtR4x76p9yohc506eGdOjTmyIBg==" + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==" + }, + "node_modules/lodash.uniqby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz", + "integrity": "sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==" + }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/lowercase-keys": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", + "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "bin": { + "lz-string": "bin/bin.js" + } + }, + "node_modules/markdown-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-2.0.0.tgz", + "integrity": "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/markdown-table": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.3.tgz", + "integrity": "sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/mdast-util-directive": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-directive/-/mdast-util-directive-3.0.0.tgz", + "integrity": "sha512-JUpYOqKI4mM3sZcNxmF/ox04XYFFkNwr0CFlrQIkCwbvH0xzMCqkMqAde9wRd80VAhaUrwFwKm2nxretdT1h7Q==", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-find-and-replace": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.1.tgz", + "integrity": "sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA==", + "dependencies": { + "@types/mdast": "^4.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.0.tgz", + "integrity": "sha512-n7MTOr/z+8NAX/wmhhDji8O3bRvPTV/U0oTCaZJkjhPSKTPhS3xufVhKGF8s1pJ7Ox4QgoIU7KHseh09S+9rTA==", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-from-markdown/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/mdast-util-frontmatter": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-frontmatter/-/mdast-util-frontmatter-2.0.1.tgz", + "integrity": "sha512-LRqI9+wdgC25P0URIJY9vwocIzCcksduHQ9OF2joxQoyTNVduwLAFUzjoopuRJbJAReaKrNQKAZKL3uCMugWJA==", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "escape-string-regexp": "^5.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-extension-frontmatter": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-frontmatter/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mdast-util-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.0.0.tgz", + "integrity": "sha512-dgQEX5Amaq+DuUqf26jJqSK9qgixgd6rYDHAv4aTBuA92cTknZlKpPfa86Z/s8Dj8xsAQpFfBmPUHWJBWqS4Bw==", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.0", + "mdast-util-gfm-strikethrough": "^2.0.0", + "mdast-util-gfm-table": "^2.0.0", + "mdast-util-gfm-task-list-item": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.0.tgz", + "integrity": "sha512-FyzMsduZZHSc3i0Px3PQcBT4WJY/X/RCtEJKuybiC6sjPqLv7h1yqAkmILZtuxMSsUyaLUWNp71+vQH2zqp5cg==", + "dependencies": { + "@types/mdast": "^4.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-find-and-replace": "^3.0.0", + "micromark-util-character": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-autolink-literal/node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-autolink-literal/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/mdast-util-gfm-footnote": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.0.0.tgz", + "integrity": "sha512-5jOT2boTSVkMnQ7LTrd6n/18kqwjmuYqo7JUPe+tRCY6O7dAuTFMtTPauYYrMPpox9hlN0uOx/FL8XvEfG9/mQ==", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-strikethrough": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", + "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", + "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-3.0.0.tgz", + "integrity": "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-expression": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.0.tgz", + "integrity": "sha512-fGCu8eWdKUKNu5mohVGkhBXCXGnOTLuFqOvGMvdikr+J1w7lDJgxThOKpwRWzzbyXAU2hhSwsmssOY4yTokluw==", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.1.1.tgz", + "integrity": "sha512-Di63TQEHbiApe6CFp/qQXCORHMHnmW2JFdr5PYH57LuEIPjijRHicAmL5wQu+B0/Q4p0qJaEOE1EkhiwxiNmAQ==", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-remove-position": "^5.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdxjs-esm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.1.0.tgz", + "integrity": "sha512-/e2l/6+OdGp/FB+ctrJ9Avz71AN/GRH3oi/3KAx/kMnoUsD6q0woXlDT8lLEeViVKE7oZxE7RXzvO3T8kF2/sA==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.0.tgz", + "integrity": "sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ==", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==" + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memfs": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", + "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", + "dependencies": { + "fs-monkey": "^1.0.4" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/mermaid": { + "version": "10.6.1", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-10.6.1.tgz", + "integrity": "sha512-Hky0/RpOw/1il9X8AvzOEChfJtVvmXm+y7JML5C//ePYMy0/9jCEmW1E1g86x9oDfW9+iVEdTV/i+M6KWRNs4A==", + "dependencies": { + "@braintree/sanitize-url": "^6.0.1", + "@types/d3-scale": "^4.0.3", + "@types/d3-scale-chromatic": "^3.0.0", + "cytoscape": "^3.23.0", + "cytoscape-cose-bilkent": "^4.1.0", + "cytoscape-fcose": "^2.1.0", + "d3": "^7.4.0", + "d3-sankey": "^0.12.3", + "dagre-d3-es": "7.0.10", + "dayjs": "^1.11.7", + "dompurify": "^3.0.5", + "elkjs": "^0.8.2", + "khroma": "^2.0.0", + "lodash-es": "^4.17.21", + "mdast-util-from-markdown": "^1.3.0", + "non-layered-tidy-tree-layout": "^2.0.2", + "stylis": "^4.1.3", + "ts-dedent": "^2.2.0", + "uuid": "^9.0.0", + "web-worker": "^1.2.0" + } + }, + "node_modules/mermaid/node_modules/@types/mdast": { + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz", + "integrity": "sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==", + "dependencies": { + "@types/unist": "^2" + } + }, + "node_modules/mermaid/node_modules/@types/unist": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz", + "integrity": "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==" + }, + "node_modules/mermaid/node_modules/mdast-util-from-markdown": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-1.3.1.tgz", + "integrity": "sha512-4xTO/M8c82qBcnQc1tgpNtubGUW/Y1tBQ1B0i5CtSoelOLKFYlElIr3bvgREYYO5iRqbMY1YuqZng0GVOI8Qww==", + "dependencies": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "mdast-util-to-string": "^3.1.0", + "micromark": "^3.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-decode-string": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "unist-util-stringify-position": "^3.0.0", + "uvu": "^0.5.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mermaid/node_modules/mdast-util-to-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.2.0.tgz", + "integrity": "sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==", + "dependencies": { + "@types/mdast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mermaid/node_modules/micromark": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.2.0.tgz", + "integrity": "sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "micromark-core-commonmark": "^1.0.1", + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-combine-extensions": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-sanitize-uri": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "node_modules/mermaid/node_modules/micromark-core-commonmark": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-1.1.0.tgz", + "integrity": "sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-factory-destination": "^1.0.0", + "micromark-factory-label": "^1.0.0", + "micromark-factory-space": "^1.0.0", + "micromark-factory-title": "^1.0.0", + "micromark-factory-whitespace": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-classify-character": "^1.0.0", + "micromark-util-html-tag-name": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "node_modules/mermaid/node_modules/micromark-factory-destination": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-1.1.0.tgz", + "integrity": "sha512-XaNDROBgx9SgSChd69pjiGKbV+nfHGDPVYFs5dOoDd7ZnMAE+Cuu91BCpsY8RT2NP9vo/B8pds2VQNCLiu0zhg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/mermaid/node_modules/micromark-factory-label": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-1.1.0.tgz", + "integrity": "sha512-OLtyez4vZo/1NjxGhcpDSbHQ+m0IIGnT8BoPamh+7jVlzLJBH98zzuCoUeMxvM6WsNeh8wx8cKvqLiPHEACn0w==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "node_modules/mermaid/node_modules/micromark-factory-title": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-1.1.0.tgz", + "integrity": "sha512-J7n9R3vMmgjDOCY8NPw55jiyaQnH5kBdV2/UXCtZIpnHH3P6nHUKaH7XXEYuWwx/xUJcawa8plLBEjMPU24HzQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/mermaid/node_modules/micromark-factory-whitespace": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-1.1.0.tgz", + "integrity": "sha512-v2WlmiymVSp5oMg+1Q0N1Lxmt6pMhIHD457whWM7/GUlEks1hI9xj5w3zbc4uuMKXGisksZk8DzP2UyGbGqNsQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/mermaid/node_modules/micromark-util-chunked": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-1.1.0.tgz", + "integrity": "sha512-Ye01HXpkZPNcV6FiyoW2fGZDUw4Yc7vT0E9Sad83+bEDiCJ1uXu0S3mr8WLpsz3HaG3x2q0HM6CTuPdcZcluFQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/mermaid/node_modules/micromark-util-classify-character": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-1.1.0.tgz", + "integrity": "sha512-SL0wLxtKSnklKSUplok1WQFoGhUdWYKggKUiqhX+Swala+BtptGCu5iPRc+xvzJ4PXE/hwM3FNXsfEVgoZsWbw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/mermaid/node_modules/micromark-util-combine-extensions": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.1.0.tgz", + "integrity": "sha512-Q20sp4mfNf9yEqDL50WwuWZHUrCO4fEyeDCnMGmG5Pr0Cz15Uo7KBs6jq+dq0EgX4DPwwrh9m0X+zPV1ypFvUA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/mermaid/node_modules/micromark-util-decode-numeric-character-reference": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.1.0.tgz", + "integrity": "sha512-m9V0ExGv0jB1OT21mrWcuf4QhP46pH1KkfWy9ZEezqHKAxkj4mPCy3nIH1rkbdMlChLHX531eOrymlwyZIf2iw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/mermaid/node_modules/micromark-util-decode-string": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-1.1.0.tgz", + "integrity": "sha512-YphLGCK8gM1tG1bd54azwyrQRjCFcmgj2S2GoJDNnh4vYtnL38JS8M4gpxzOPNyHdNEpheyWXCTnnTDY3N+NVQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/mermaid/node_modules/micromark-util-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-1.1.0.tgz", + "integrity": "sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/mermaid/node_modules/micromark-util-html-tag-name": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.2.0.tgz", + "integrity": "sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/mermaid/node_modules/micromark-util-normalize-identifier": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.1.0.tgz", + "integrity": "sha512-N+w5vhqrBihhjdpM8+5Xsxy71QWqGn7HYNUvch71iV2PM7+E3uWGox1Qp90loa1ephtCxG2ftRV/Conitc6P2Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/mermaid/node_modules/micromark-util-resolve-all": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-1.1.0.tgz", + "integrity": "sha512-b/G6BTMSg+bX+xVCshPTPyAu2tmA0E4X98NSR7eIbeC6ycCqCeE7wjfDIgzEbkzdEVJXRtOG4FbEm/uGbCRouA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/mermaid/node_modules/micromark-util-sanitize-uri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.2.0.tgz", + "integrity": "sha512-QO4GXv0XZfWey4pYFndLUKEAktKkG5kZTdUNaTAkzbuJxn2tNBOr+QtxR2XpWaMhbImT2dPzyLrPXLlPhph34A==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/mermaid/node_modules/micromark-util-subtokenize": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-1.1.0.tgz", + "integrity": "sha512-kUQHyzRoxvZO2PuLzMt2P/dwVsTiivCK8icYTeR+3WgbuPqfHgPPy7nFKbeqRivBvn/3N3GBiNC+JRTMSxEC7A==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "node_modules/mermaid/node_modules/micromark-util-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz", + "integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/mermaid/node_modules/unist-util-stringify-position": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.3.tgz", + "integrity": "sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==", + "dependencies": { + "@types/unist": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mermaid/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromark": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.0.tgz", + "integrity": "sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.0.tgz", + "integrity": "sha512-jThOz/pVmAYUtkroV3D5c1osFXAMv9e0ypGDOIZuCeAe91/sD6BoE2Sjzt30yuXtwOYUmySOhMas/PVyh02itA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark/node_modules/micromark-factory-space": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", + "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark/node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-extension-directive": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-3.0.0.tgz", + "integrity": "sha512-61OI07qpQrERc+0wEysLHMvoiO3s2R56x5u7glHq2Yqq6EHbH4dW25G9GfDdGCDYqA21KE6DWgNSzxSwHc2hSg==", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "parse-entities": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-directive/node_modules/micromark-factory-space": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", + "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-directive/node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-directive/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-extension-frontmatter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-frontmatter/-/micromark-extension-frontmatter-2.0.0.tgz", + "integrity": "sha512-C4AkuM3dA58cgZha7zVnuVxBhDsbttIMiytjgsM2XbHAB2faRVaHRle40558FBN+DJcrLNCoqG5mlrpdU4cRtg==", + "dependencies": { + "fault": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-frontmatter/node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-frontmatter/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-extension-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", + "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", + "dependencies": { + "micromark-extension-gfm-autolink-literal": "^2.0.0", + "micromark-extension-gfm-footnote": "^2.0.0", + "micromark-extension-gfm-strikethrough": "^2.0.0", + "micromark-extension-gfm-table": "^2.0.0", + "micromark-extension-gfm-tagfilter": "^2.0.0", + "micromark-extension-gfm-task-list-item": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.0.0.tgz", + "integrity": "sha512-rTHfnpt/Q7dEAK1Y5ii0W8bhfJlVJFnJMHIPisfPK3gpVNuOP0VnRl96+YJ3RYWV/P4gFeQoGKNlT3RhuvpqAg==", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal/node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.0.0.tgz", + "integrity": "sha512-6Rzu0CYRKDv3BfLAUnZsSlzx3ak6HAoI85KTiijuKIz5UxZxbUI+pD6oHgw+6UtQuiRwnGRhzMmPRv4smcz0fg==", + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-factory-space": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", + "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha512-c3BR1ClMp5fxxmwP6AoOY2fXO9U8uFMKs4ADD66ahLTNcwzSCyRVU4k7LPV5Nxo/VJiR4TdzxRQY2v3qIUceCw==", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-strikethrough/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.0.0.tgz", + "integrity": "sha512-PoHlhypg1ItIucOaHmKE8fbin3vTLpDOUg8KAr8gRCF1MOZI9Nquq2i/44wFvviM4WuxJzc3demT8Y3dkfvYrw==", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table/node_modules/micromark-factory-space": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", + "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-table/node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-table/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", + "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.0.1.tgz", + "integrity": "sha512-cY5PzGcnULaN5O7T+cOzfMoHjBW7j+T9D2sucA5d/KbsBTPcYdebm9zUd9zzdgJGCwahV+/W78Z3nbulBYVbTw==", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-task-list-item/node_modules/micromark-factory-space": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", + "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-task-list-item/node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-task-list-item/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-extension-mdx-expression": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-3.0.0.tgz", + "integrity": "sha512-sI0nwhUDz97xyzqJAbHQhp5TfaxEvZZZ2JDqUo+7NvyIYG6BZ5CPPqj2ogUoPJlmXHBnyZUzISg9+oUmU6tUjQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-mdx-expression": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-mdx-expression/node_modules/micromark-factory-space": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", + "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-mdx-expression/node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-mdx-expression/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-extension-mdx-jsx": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-3.0.0.tgz", + "integrity": "sha512-uvhhss8OGuzR4/N17L1JwvmJIpPhAd8oByMawEKx6NVdBCbesjH4t+vjEp3ZXft9DwvlKSD07fCeI44/N0Vf2w==", + "dependencies": { + "@types/acorn": "^4.0.0", + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "micromark-factory-mdx-expression": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdx-jsx/node_modules/micromark-factory-space": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", + "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-mdx-jsx/node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-mdx-jsx/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-extension-mdx-md": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-md/-/micromark-extension-mdx-md-2.0.0.tgz", + "integrity": "sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdxjs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs/-/micromark-extension-mdxjs-3.0.0.tgz", + "integrity": "sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==", + "dependencies": { + "acorn": "^8.0.0", + "acorn-jsx": "^5.0.0", + "micromark-extension-mdx-expression": "^3.0.0", + "micromark-extension-mdx-jsx": "^3.0.0", + "micromark-extension-mdx-md": "^2.0.0", + "micromark-extension-mdxjs-esm": "^3.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdxjs-esm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-3.0.0.tgz", + "integrity": "sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdxjs-esm/node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-mdxjs-esm/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.0.tgz", + "integrity": "sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-destination/node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-destination/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-factory-label": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.0.tgz", + "integrity": "sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label/node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-factory-mdx-expression": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-2.0.1.tgz", + "integrity": "sha512-F0ccWIUHRLRrYp5TC9ZYXmZo+p2AM13ggbsW4T0b5CRKP8KHVRB8t4pwtBgTxtjRmwrK0Irwm7vs2JOZabHZfg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + } + }, + "node_modules/micromark-factory-mdx-expression/node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-mdx-expression/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-factory-space": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-1.1.0.tgz", + "integrity": "sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-factory-space/node_modules/micromark-util-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz", + "integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-factory-title": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.0.tgz", + "integrity": "sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title/node_modules/micromark-factory-space": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", + "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title/node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.0.tgz", + "integrity": "sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace/node_modules/micromark-factory-space": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", + "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace/node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-character": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-1.2.0.tgz", + "integrity": "sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-character/node_modules/micromark-util-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz", + "integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.0.tgz", + "integrity": "sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.0.tgz", + "integrity": "sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character/node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.0.tgz", + "integrity": "sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.1.tgz", + "integrity": "sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.0.tgz", + "integrity": "sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string/node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz", + "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-events-to-acorn": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-2.0.2.tgz", + "integrity": "sha512-Fk+xmBrOv9QZnEDguL9OI9/NQQp6Hz4FuQ4YmCb/5V7+9eAh1s6AYSvL20kHkD67YIg7EpE54TiSlcsf3vyZgA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "@types/acorn": "^4.0.0", + "@types/estree": "^1.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "estree-util-visit": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "vfile-message": "^4.0.0" + } + }, + "node_modules/micromark-util-events-to-acorn/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.0.tgz", + "integrity": "sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.0.tgz", + "integrity": "sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-normalize-identifier/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.0.tgz", + "integrity": "sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz", + "integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri/node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.0.tgz", + "integrity": "sha512-vc93L1t+gpR3p8jxeVdaYlbV2jTYteDje19rNSS/H5dlhxUYll5Fy6vJ2cDwP8RnsXi818yGty1ayP55y3W6fg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-symbol": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.1.0.tgz", + "integrity": "sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.0.tgz", + "integrity": "sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark/node_modules/micromark-factory-space": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", + "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark/node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", + "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.18", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", + "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", + "dependencies": { + "mime-db": "~1.33.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-response": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", + "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mini-css-extract-plugin": { + "version": "2.7.6", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.6.tgz", + "integrity": "sha512-Qk7HcgaPkGG6eD77mLvZS1nmxlao3j+9PkrT9Uc7HAE1id3F41+DdBRYRYkbyfNRGzm8/YWtzhw7nVPmwhqTQw==", + "dependencies": { + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/mrmime": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz", + "integrity": "sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/multicast-dns": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", + "dependencies": { + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" + } + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + }, + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" + }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/node-emoji": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.1.3.tgz", + "integrity": "sha512-E2WEOVsgs7O16zsURJ/eH8BqhF029wGpEOnv7Urwdo2wmQanOACwJQh0devF9D9RhoZru0+9JXIS0dBXIAz+lA==", + "dependencies": { + "@sindresorhus/is": "^4.6.0", + "char-regex": "^1.0.2", + "emojilib": "^2.4.0", + "skin-tone": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/node-releases": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==" + }, + "node_modules/non-layered-tidy-tree-layout": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/non-layered-tidy-tree-layout/-/non-layered-tidy-tree-layout-2.0.2.tgz", + "integrity": "sha512-gkXMxRzUH+PB0ax9dUN0yYF0S25BqeAYqhgMaLUFmpXLEk7Fcu8f4emJuOAY0V8kjDICxROIKsTAKsV/v355xw==" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nprogress": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz", + "integrity": "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==" + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/opener": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", + "bin": { + "opener": "bin/opener-bin.js" + } + }, + "node_modules/outvariant": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.0.tgz", + "integrity": "sha512-AlWY719RF02ujitly7Kk/0QlV+pXGFDHrHf9O2OKqyqgBieaPOIeuSkL8sRK6j2WK+/ZAURq2kZsY0d8JapUiw==" + }, + "node_modules/p-cancelable": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", + "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==", + "engines": { + "node": ">=12.20" + } + }, + "node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "dependencies": { + "p-limit": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-8.1.1.tgz", + "integrity": "sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==", + "dependencies": { + "got": "^12.1.0", + "registry-auth-token": "^5.0.1", + "registry-url": "^6.0.0", + "semver": "^7.3.7" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-entities": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.1.tgz", + "integrity": "sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse-entities/node_modules/@types/unist": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz", + "integrity": "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==" + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-numeric-range": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/parse-numeric-range/-/parse-numeric-range-1.3.0.tgz", + "integrity": "sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ==" + }, + "node_modules/parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "dependencies": { + "entities": "^4.4.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", + "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", + "dependencies": { + "domhandler": "^5.0.2", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "dependencies": { + "isarray": "0.0.1" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/periscopic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", + "integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^3.0.0", + "is-reference": "^3.0.0" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pkg-dir": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-7.0.0.tgz", + "integrity": "sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==", + "dependencies": { + "find-up": "^6.3.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-up/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-up/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss": { + "version": "8.4.38", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", + "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-calc": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-9.0.1.tgz", + "integrity": "sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==", + "dependencies": { + "postcss-selector-parser": "^6.0.11", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.2.2" + } + }, + "node_modules/postcss-colormin": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-6.1.0.tgz", + "integrity": "sha512-x9yX7DOxeMAR+BgGVnNSAxmAj98NX/YxEMNFP+SDCEeNLb2r3i6Hh1ksMsnW8Ub5SLCpbescQqn9YEbE9554Sw==", + "dependencies": { + "browserslist": "^4.23.0", + "caniuse-api": "^3.0.0", + "colord": "^2.9.3", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-convert-values": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-6.1.0.tgz", + "integrity": "sha512-zx8IwP/ts9WvUM6NkVSkiU902QZL1bwPhaVaLynPtCsOTqp+ZKbNi+s6XJg3rfqpKGA/oc7Oxk5t8pOQJcwl/w==", + "dependencies": { + "browserslist": "^4.23.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-discard-comments": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-6.0.2.tgz", + "integrity": "sha512-65w/uIqhSBBfQmYnG92FO1mWZjJ4GL5b8atm5Yw2UgrwD7HiNiSSNwJor1eCFGzUgYnN/iIknhNRVqjrrpuglw==", + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-discard-duplicates": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-6.0.3.tgz", + "integrity": "sha512-+JA0DCvc5XvFAxwx6f/e68gQu/7Z9ud584VLmcgto28eB8FqSFZwtrLwB5Kcp70eIoWP/HXqz4wpo8rD8gpsTw==", + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-discard-empty": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-6.0.3.tgz", + "integrity": "sha512-znyno9cHKQsK6PtxL5D19Fj9uwSzC2mB74cpT66fhgOadEUPyXFkbgwm5tvc3bt3NAy8ltE5MrghxovZRVnOjQ==", + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-discard-overridden": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-6.0.2.tgz", + "integrity": "sha512-j87xzI4LUggC5zND7KdjsI25APtyMuynXZSujByMaav2roV6OZX+8AaCUcZSWqckZpjAjRyFDdpqybgjFO0HJQ==", + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-discard-unused": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-6.0.5.tgz", + "integrity": "sha512-wHalBlRHkaNnNwfC8z+ppX57VhvS+HWgjW508esjdaEYr3Mx7Gnn2xA4R/CKf5+Z9S5qsqC+Uzh4ueENWwCVUA==", + "dependencies": { + "postcss-selector-parser": "^6.0.16" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-loader": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.3.3.tgz", + "integrity": "sha512-YgO/yhtevGO/vJePCQmTxiaEwER94LABZN0ZMT4A0vsak9TpO+RvKRs7EmJ8peIlB9xfXCsS7M8LjqncsUZ5HA==", + "dependencies": { + "cosmiconfig": "^8.2.0", + "jiti": "^1.18.2", + "semver": "^7.3.8" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "postcss": "^7.0.0 || ^8.0.1", + "webpack": "^5.0.0" + } + }, + "node_modules/postcss-merge-idents": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-6.0.3.tgz", + "integrity": "sha512-1oIoAsODUs6IHQZkLQGO15uGEbK3EAl5wi9SS8hs45VgsxQfMnxvt+L+zIr7ifZFIH14cfAeVe2uCTa+SPRa3g==", + "dependencies": { + "cssnano-utils": "^4.0.2", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-merge-longhand": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-6.0.5.tgz", + "integrity": "sha512-5LOiordeTfi64QhICp07nzzuTDjNSO8g5Ksdibt44d+uvIIAE1oZdRn8y/W5ZtYgRH/lnLDlvi9F8btZcVzu3w==", + "dependencies": { + "postcss-value-parser": "^4.2.0", + "stylehacks": "^6.1.1" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-merge-rules": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-6.1.1.tgz", + "integrity": "sha512-KOdWF0gju31AQPZiD+2Ar9Qjowz1LTChSjFFbS+e2sFgc4uHOp3ZvVX4sNeTlk0w2O31ecFGgrFzhO0RSWbWwQ==", + "dependencies": { + "browserslist": "^4.23.0", + "caniuse-api": "^3.0.0", + "cssnano-utils": "^4.0.2", + "postcss-selector-parser": "^6.0.16" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-minify-font-values": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-6.1.0.tgz", + "integrity": "sha512-gklfI/n+9rTh8nYaSJXlCo3nOKqMNkxuGpTn/Qm0gstL3ywTr9/WRKznE+oy6fvfolH6dF+QM4nCo8yPLdvGJg==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-minify-gradients": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-6.0.3.tgz", + "integrity": "sha512-4KXAHrYlzF0Rr7uc4VrfwDJ2ajrtNEpNEuLxFgwkhFZ56/7gaE4Nr49nLsQDZyUe+ds+kEhf+YAUolJiYXF8+Q==", + "dependencies": { + "colord": "^2.9.3", + "cssnano-utils": "^4.0.2", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-minify-params": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-6.1.0.tgz", + "integrity": "sha512-bmSKnDtyyE8ujHQK0RQJDIKhQ20Jq1LYiez54WiaOoBtcSuflfK3Nm596LvbtlFcpipMjgClQGyGr7GAs+H1uA==", + "dependencies": { + "browserslist": "^4.23.0", + "cssnano-utils": "^4.0.2", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-minify-selectors": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-6.0.4.tgz", + "integrity": "sha512-L8dZSwNLgK7pjTto9PzWRoMbnLq5vsZSTu8+j1P/2GB8qdtGQfn+K1uSvFgYvgh83cbyxT5m43ZZhUMTJDSClQ==", + "dependencies": { + "postcss-selector-parser": "^6.0.16" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.3.tgz", + "integrity": "sha512-2/u2zraspoACtrbFRnTijMiQtb4GW4BvatjaG/bCjYQo8kLTdevCUlwuBHx2sCnSyrI3x3qj4ZK1j5LQBgzmwA==", + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", + "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-normalize-charset": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-6.0.2.tgz", + "integrity": "sha512-a8N9czmdnrjPHa3DeFlwqst5eaL5W8jYu3EBbTTkI5FHkfMhFZh1EGbku6jhHhIzTA6tquI2P42NtZ59M/H/kQ==", + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-normalize-display-values": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-6.0.2.tgz", + "integrity": "sha512-8H04Mxsb82ON/aAkPeq8kcBbAtI5Q2a64X/mnRRfPXBq7XeogoQvReqxEfc0B4WPq1KimjezNC8flUtC3Qz6jg==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-normalize-positions": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-6.0.2.tgz", + "integrity": "sha512-/JFzI441OAB9O7VnLA+RtSNZvQ0NCFZDOtp6QPFo1iIyawyXg0YI3CYM9HBy1WvwCRHnPep/BvI1+dGPKoXx/Q==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-normalize-repeat-style": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-6.0.2.tgz", + "integrity": "sha512-YdCgsfHkJ2jEXwR4RR3Tm/iOxSfdRt7jplS6XRh9Js9PyCR/aka/FCb6TuHT2U8gQubbm/mPmF6L7FY9d79VwQ==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-normalize-string": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-6.0.2.tgz", + "integrity": "sha512-vQZIivlxlfqqMp4L9PZsFE4YUkWniziKjQWUtsxUiVsSSPelQydwS8Wwcuw0+83ZjPWNTl02oxlIvXsmmG+CiQ==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-normalize-timing-functions": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-6.0.2.tgz", + "integrity": "sha512-a+YrtMox4TBtId/AEwbA03VcJgtyW4dGBizPl7e88cTFULYsprgHWTbfyjSLyHeBcK/Q9JhXkt2ZXiwaVHoMzA==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-normalize-unicode": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-6.1.0.tgz", + "integrity": "sha512-QVC5TQHsVj33otj8/JD869Ndr5Xcc/+fwRh4HAsFsAeygQQXm+0PySrKbr/8tkDKzW+EVT3QkqZMfFrGiossDg==", + "dependencies": { + "browserslist": "^4.23.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-normalize-url": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-6.0.2.tgz", + "integrity": "sha512-kVNcWhCeKAzZ8B4pv/DnrU1wNh458zBNp8dh4y5hhxih5RZQ12QWMuQrDgPRw3LRl8mN9vOVfHl7uhvHYMoXsQ==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-normalize-whitespace": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-6.0.2.tgz", + "integrity": "sha512-sXZ2Nj1icbJOKmdjXVT9pnyHQKiSAyuNQHSgRCUgThn2388Y9cGVDR+E9J9iAYbSbLHI+UUwLVl1Wzco/zgv0Q==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-ordered-values": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-6.0.2.tgz", + "integrity": "sha512-VRZSOB+JU32RsEAQrO94QPkClGPKJEL/Z9PCBImXMhIeK5KAYo6slP/hBYlLgrCjFxyqvn5VC81tycFEDBLG1Q==", + "dependencies": { + "cssnano-utils": "^4.0.2", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-reduce-idents": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-6.0.3.tgz", + "integrity": "sha512-G3yCqZDpsNPoQgbDUy3T0E6hqOQ5xigUtBQyrmq3tn2GxlyiL0yyl7H+T8ulQR6kOcHJ9t7/9H4/R2tv8tJbMA==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-reduce-initial": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-6.1.0.tgz", + "integrity": "sha512-RarLgBK/CrL1qZags04oKbVbrrVK2wcxhvta3GCxrZO4zveibqbRPmm2VI8sSgCXwoUHEliRSbOfpR0b/VIoiw==", + "dependencies": { + "browserslist": "^4.23.0", + "caniuse-api": "^3.0.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-reduce-transforms": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-6.0.2.tgz", + "integrity": "sha512-sB+Ya++3Xj1WaT9+5LOOdirAxP7dJZms3GRcYheSPi1PiTMigsxHAdkrbItHxwYHr4kt1zL7mmcHstgMYT+aiA==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.0.tgz", + "integrity": "sha512-UMz42UD0UY0EApS0ZL9o1XnLhSTtvvvLe5Dc2H2O56fvRZi+KulDyf5ctDhhtYJBGKStV2FL1fy6253cmLgqVQ==", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-sort-media-queries": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/postcss-sort-media-queries/-/postcss-sort-media-queries-5.2.0.tgz", + "integrity": "sha512-AZ5fDMLD8SldlAYlvi8NIqo0+Z8xnXU2ia0jxmuhxAU+Lqt9K+AlmLNJ/zWEnE9x+Zx3qL3+1K20ATgNOr3fAA==", + "dependencies": { + "sort-css-media-queries": "2.2.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.4.23" + } + }, + "node_modules/postcss-svgo": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-6.0.3.tgz", + "integrity": "sha512-dlrahRmxP22bX6iKEjOM+c8/1p+81asjKT+V5lrgOH944ryx/OHpclnIbGsKVd3uWOXFLYJwCVf0eEkJGvO96g==", + "dependencies": { + "postcss-value-parser": "^4.2.0", + "svgo": "^3.2.0" + }, + "engines": { + "node": "^14 || ^16 || >= 18" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-unique-selectors": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-6.0.4.tgz", + "integrity": "sha512-K38OCaIrO8+PzpArzkLKB42dSARtC2tmG6PvD4b1o1Q2E9Os8jzfWFfSy/rixsHwohtsDdFtAWGjFVFUdwYaMg==", + "dependencies": { + "postcss-selector-parser": "^6.0.16" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/postcss-zindex": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-6.0.2.tgz", + "integrity": "sha512-5BxW9l1evPB/4ZIc+2GobEBoKC+h8gPGCMi+jxsYvd2x0mjq7wazk6DrP71pStqxE9Foxh5TVnonbWpFZzXaYg==", + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/pretty-error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", + "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", + "dependencies": { + "lodash": "^4.17.20", + "renderkid": "^3.0.0" + } + }, + "node_modules/pretty-time": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pretty-time/-/pretty-time-1.1.0.tgz", + "integrity": "sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/prism-react-renderer": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-2.3.0.tgz", + "integrity": "sha512-UYRg2TkVIaI6tRVHC5OJ4/BxqPUxJkJvq/odLT/ykpt1zGYXooNperUxQcCvi87LyRnR4nCh81ceOA+e7nrydg==", + "dependencies": { + "@types/prismjs": "^1.26.0", + "clsx": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.0.0" + } + }, + "node_modules/prismjs": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", + "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==", + "engines": { + "node": ">=6" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/property-information": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.4.1.tgz", + "integrity": "sha512-OHYtXfu5aI2sS2LWFSN5rgJjrQ4pCy8i1jubJLe2QvMF8JJ++HXTUIVWFLfXJoaOfvYYjk2SN8J2wFUWIGXT4w==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-addr/node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==" + }, + "node_modules/pupa": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-3.1.0.tgz", + "integrity": "sha512-FLpr4flz5xZTSJxSeaheeMKN/EDzMdK7b8PTOC6a5PYFKTucWbdqjgqaEyH0shFiSJrVB1+Qqi4Tk19ccU6Aug==", + "dependencies": { + "escape-goat": "^4.0.0" + }, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", + "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", + "dependencies": { + "inherits": "~2.0.3" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-loader": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-4.0.2.tgz", + "integrity": "sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA==", + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/raw-loader/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/raw-loader/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/raw-loader/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "node_modules/raw-loader/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dev-utils": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", + "integrity": "sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==", + "dependencies": { + "@babel/code-frame": "^7.16.0", + "address": "^1.1.2", + "browserslist": "^4.18.1", + "chalk": "^4.1.2", + "cross-spawn": "^7.0.3", + "detect-port-alt": "^1.1.6", + "escape-string-regexp": "^4.0.0", + "filesize": "^8.0.6", + "find-up": "^5.0.0", + "fork-ts-checker-webpack-plugin": "^6.5.0", + "global-modules": "^2.0.0", + "globby": "^11.0.4", + "gzip-size": "^6.0.0", + "immer": "^9.0.7", + "is-root": "^2.1.0", + "loader-utils": "^3.2.0", + "open": "^8.4.0", + "pkg-up": "^3.1.0", + "prompts": "^2.4.2", + "react-error-overlay": "^6.0.11", + "recursive-readdir": "^2.2.2", + "shell-quote": "^1.7.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/react-dev-utils/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-dev-utils/node_modules/loader-utils": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz", + "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==", + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/react-dev-utils/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-dev-utils/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-dev-utils/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-dev-utils/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/react-dev-utils/node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-devtools-inline": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/react-devtools-inline/-/react-devtools-inline-4.4.0.tgz", + "integrity": "sha512-ES0GolSrKO8wsKbsEkVeiR/ZAaHQTY4zDh1UW8DImVmm8oaGLl3ijJDvSGe+qDRKPZdPRnDtWWnSvvrgxXdThQ==", + "dependencies": { + "es6-symbol": "^3" + } + }, + "node_modules/react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + }, + "peerDependencies": { + "react": "^18.2.0" + } + }, + "node_modules/react-error-overlay": { + "version": "6.0.11", + "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", + "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" + }, + "node_modules/react-fast-compare": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", + "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==" + }, + "node_modules/react-helmet-async": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-1.3.0.tgz", + "integrity": "sha512-9jZ57/dAn9t3q6hneQS0wukqC2ENOBgMNVEhb/ZG9ZSxUetzVIw4iAmEU38IaVg3QGYauQPhSeUTuIUtFglWpg==", + "dependencies": { + "@babel/runtime": "^7.12.5", + "invariant": "^2.2.4", + "prop-types": "^15.7.2", + "react-fast-compare": "^3.2.0", + "shallowequal": "^1.1.0" + }, + "peerDependencies": { + "react": "^16.6.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.6.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + }, + "node_modules/react-json-view-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/react-json-view-lite/-/react-json-view-lite-1.4.0.tgz", + "integrity": "sha512-wh6F6uJyYAmQ4fK0e8dSQMEWuvTs2Wr3el3sLD9bambX1+pSWUVXIz1RFaoy3TI1mZ0FqdpKq9YgbgTTgyrmXA==", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": "^16.13.1 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-loadable": { + "name": "@docusaurus/react-loadable", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-6.0.0.tgz", + "integrity": "sha512-YMMxTUQV/QFSnbgrP3tjDzLHRg7vsbMn8e9HAa8o/1iXoiomo48b7sk/kkmWEuWNDPJVlKSJRB6Y2fHqdJk+SQ==", + "dependencies": { + "@types/react": "*" + }, + "peerDependencies": { + "react": "*" + } + }, + "node_modules/react-loadable-ssr-addon-v5-slorber": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/react-loadable-ssr-addon-v5-slorber/-/react-loadable-ssr-addon-v5-slorber-1.0.1.tgz", + "integrity": "sha512-lq3Lyw1lGku8zUEJPDxsNm1AfYHBrO9Y1+olAYwpUJ2IGFBskM0DMKok97A6LWUpHm+o7IvQBOWu9MLenp9Z+A==", + "dependencies": { + "@babel/runtime": "^7.10.3" + }, + "engines": { + "node": ">=10.13.0" + }, + "peerDependencies": { + "react-loadable": "*", + "webpack": ">=4.41.1 || 5.x" + } + }, + "node_modules/react-router": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.3.4.tgz", + "integrity": "sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA==", + "dependencies": { + "@babel/runtime": "^7.12.13", + "history": "^4.9.0", + "hoist-non-react-statics": "^3.1.0", + "loose-envify": "^1.3.1", + "path-to-regexp": "^1.7.0", + "prop-types": "^15.6.2", + "react-is": "^16.6.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0" + }, + "peerDependencies": { + "react": ">=15" + } + }, + "node_modules/react-router-config": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/react-router-config/-/react-router-config-5.1.1.tgz", + "integrity": "sha512-DuanZjaD8mQp1ppHjgnnUnyOlqYXZVjnov/JzFhjLEwd3Z4dYjMSnqrEzzGThH47vpCOqPPwJM2FtthLeJ8Pbg==", + "dependencies": { + "@babel/runtime": "^7.1.2" + }, + "peerDependencies": { + "react": ">=15", + "react-router": ">=5" + } + }, + "node_modules/react-router-dom": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.3.4.tgz", + "integrity": "sha512-m4EqFMHv/Ih4kpcBCONHbkT68KoAeHN4p3lAGoNryfHi0dMy0kCzEZakiKRsvg5wHZ/JLrLW8o8KomWiz/qbYQ==", + "dependencies": { + "@babel/runtime": "^7.12.13", + "history": "^4.9.0", + "loose-envify": "^1.3.1", + "prop-types": "^15.6.2", + "react-router": "5.3.4", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0" + }, + "peerDependencies": { + "react": ">=15" + } + }, + "node_modules/react-router/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/reading-time": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/reading-time/-/reading-time-1.5.0.tgz", + "integrity": "sha512-onYyVhBNr4CmAxFsKS7bz+uTLRakypIe4R+5A824vBSkQy/hB3fZepoVEf8OVAxzLvK+H/jm9TzpI3ETSm64Kg==" + }, + "node_modules/rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", + "dependencies": { + "resolve": "^1.1.6" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/recursive-readdir": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", + "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", + "dependencies": { + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz", + "integrity": "sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==", + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", + "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" + }, + "node_modules/regenerator-transform": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", + "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regexpu-core": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", + "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", + "dependencies": { + "@babel/regjsgen": "^0.8.0", + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.1.0", + "regjsparser": "^0.9.1", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/registry-auth-token": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.0.2.tgz", + "integrity": "sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ==", + "dependencies": { + "@pnpm/npm-conf": "^2.1.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/registry-url": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-6.0.1.tgz", + "integrity": "sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==", + "dependencies": { + "rc": "1.2.8" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/regjsparser": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/rehype-raw": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz", + "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-raw": "^9.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/remark-directive": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/remark-directive/-/remark-directive-3.0.0.tgz", + "integrity": "sha512-l1UyWJ6Eg1VPU7Hm/9tt0zKtReJQNOA4+iDMAxTyZNWnJnFlbS/7zhiel/rogTLQ2vMYwDzSJa4BiVNqGlqIMA==", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-directive": "^3.0.0", + "micromark-extension-directive": "^3.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-emoji": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/remark-emoji/-/remark-emoji-4.0.1.tgz", + "integrity": "sha512-fHdvsTR1dHkWKev9eNyhTo4EFwbUvJ8ka9SgeWkMPYFX4WoI7ViVBms3PjlQYgw5TLvNQso3GUB/b/8t3yo+dg==", + "dependencies": { + "@types/mdast": "^4.0.2", + "emoticon": "^4.0.1", + "mdast-util-find-and-replace": "^3.0.1", + "node-emoji": "^2.1.0", + "unified": "^11.0.4" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/remark-frontmatter": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/remark-frontmatter/-/remark-frontmatter-5.0.0.tgz", + "integrity": "sha512-XTFYvNASMe5iPN0719nPrdItC9aU0ssC4v14mH1BCi1u0n1gAocqcujWUrByftZTbLhRtiKRyjYTSIOcr69UVQ==", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-frontmatter": "^2.0.0", + "micromark-extension-frontmatter": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-gfm": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.0.tgz", + "integrity": "sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA==", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-gfm": "^3.0.0", + "micromark-extension-gfm": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-mdx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-3.0.1.tgz", + "integrity": "sha512-3Pz3yPQ5Rht2pM5R+0J2MrGoBSrzf+tJG94N+t/ilfdh8YLyyKYtidAYwTveB20BoHAcwIopOUqhcmh2F7hGYA==", + "dependencies": { + "mdast-util-mdx": "^3.0.0", + "micromark-extension-mdxjs": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.0.tgz", + "integrity": "sha512-z3tJrAs2kIs1AqIIy6pzHmAHlF1hWQ+OdY4/hv+Wxe35EhyLKcajL33iUEn3ScxtFox9nUvRufR/Zre8Q08H/g==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-stringify": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", + "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-to-markdown": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/renderkid": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", + "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", + "dependencies": { + "css-select": "^4.1.3", + "dom-converter": "^0.2.0", + "htmlparser2": "^6.1.0", + "lodash": "^4.17.21", + "strip-ansi": "^6.0.1" + } + }, + "node_modules/renderkid/node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/renderkid/node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/renderkid/node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/renderkid/node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/renderkid/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/renderkid/node_modules/htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-like": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz", + "integrity": "sha512-oyrU88skkMtDdauHDuKVrgR+zuItqr6/c//FXzvmxRGMexSDc6hNvJInGW3LL46n+8b50RykrvwSUIIQH2LQ5A==", + "engines": { + "node": "*" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==" + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-pathname": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz", + "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==" + }, + "node_modules/responselike": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", + "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", + "dependencies": { + "lowercase-keys": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/robust-predicates": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", + "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==" + }, + "node_modules/rtl-detect": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/rtl-detect/-/rtl-detect-1.1.2.tgz", + "integrity": "sha512-PGMBq03+TTG/p/cRB7HCLKJ1MgDIi07+QU1faSjiYRfmY5UsAttV9Hs08jDAHVwcOwmVLcSJkpwyfXszVjWfIQ==" + }, + "node_modules/rtlcss": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-4.1.1.tgz", + "integrity": "sha512-/oVHgBtnPNcggP2aVXQjSy6N1mMAfHg4GSag0QtZBlD5bdDgAHwr4pydqJGd+SUCu9260+Pjqbjwtvu7EMH1KQ==", + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0", + "postcss": "^8.4.21", + "strip-json-comments": "^3.1.1" + }, + "bin": { + "rtlcss": "bin/rtlcss.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" + }, + "node_modules/sade": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "dependencies": { + "mri": "^1.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/sass": { + "version": "1.77.6", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.6.tgz", + "integrity": "sha512-ByXE1oLD79GVq9Ht1PeHWCPMPB8XHpBuz1r85oByKHjZY6qV6rWnQovQzXJXuQ/XyE1Oj3iPk3lo28uzaRA2/Q==", + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-loader": { + "version": "10.5.2", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-10.5.2.tgz", + "integrity": "sha512-vMUoSNOUKJILHpcNCCyD23X34gve1TS7Rjd9uXHeKqhvBG39x6XbswFDtpbTElj6XdMFezoWhkh5vtKudf2cgQ==", + "dependencies": { + "klona": "^2.0.4", + "loader-utils": "^2.0.0", + "neo-async": "^2.6.2", + "schema-utils": "^3.0.0", + "semver": "^7.3.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "fibers": ">= 3.1.0", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0", + "sass": "^1.3.0", + "webpack": "^4.36.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "fibers": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/sass-loader/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/sass-loader/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/sass-loader/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "node_modules/sass-loader/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/sax": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==" + }, + "node_modules/scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/search-insights": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.14.0.tgz", + "integrity": "sha512-OLN6MsPMCghDOqlCtsIsYgtsC0pnwVTyT9Mu6A3ewOj1DxvzZF6COrn2g86E/c05xbktB0XN04m/t1Z+n+fTGw==", + "peer": true + }, + "node_modules/section-matter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", + "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", + "dependencies": { + "extend-shallow": "^2.0.1", + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==" + }, + "node_modules/selfsigned": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", + "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", + "dependencies": { + "@types/node-forge": "^1.3.0", + "node-forge": "^1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-4.0.0.tgz", + "integrity": "sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/send/node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", + "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-handler": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.5.tgz", + "integrity": "sha512-ijPFle6Hwe8zfmBxJdE+5fta53fdIY0lHISJvuikXB3VYFafRjMRpOffSPvCYsbKyBA7pvy9oYr/BT1O3EArlg==", + "dependencies": { + "bytes": "3.0.0", + "content-disposition": "0.5.2", + "fast-url-parser": "1.1.3", + "mime-types": "2.1.18", + "minimatch": "3.1.2", + "path-is-inside": "1.0.2", + "path-to-regexp": "2.2.1", + "range-parser": "1.2.0" + } + }, + "node_modules/serve-handler/node_modules/path-to-regexp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.2.1.tgz", + "integrity": "sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ==" + }, + "node_modules/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "dependencies": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-index/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/serve-index/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" + }, + "node_modules/serve-index/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/serve-index/node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "node_modules/serve-index/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-function-length": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", + "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "dependencies": { + "define-data-property": "^1.1.1", + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", + "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/shelljs": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", + "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", + "dependencies": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + }, + "bin": { + "shjs": "bin/shjs" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "node_modules/sirv": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.3.tgz", + "integrity": "sha512-O9jm9BsID1P+0HOi81VpXPoDxYP374pkOLzACAoyUQ/3OUVndNpsz6wMnY2z+yOxzbllCKZrM+9QrWsv4THnyA==", + "dependencies": { + "@polka/url": "^1.0.0-next.20", + "mrmime": "^1.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" + }, + "node_modules/sitemap": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-7.1.2.tgz", + "integrity": "sha512-ARCqzHJ0p4gWt+j7NlU5eDlIO9+Rkr/JhPFZKKQ1l5GCus7rJH4UdrlVAh0xC/gDS/Qir2UMxqYNHtsKr2rpCw==", + "dependencies": { + "@types/node": "^17.0.5", + "@types/sax": "^1.2.1", + "arg": "^5.0.0", + "sax": "^1.2.4" + }, + "bin": { + "sitemap": "dist/cli.js" + }, + "engines": { + "node": ">=12.0.0", + "npm": ">=5.6.0" + } + }, + "node_modules/sitemap/node_modules/@types/node": { + "version": "17.0.45", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", + "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==" + }, + "node_modules/skin-tone": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/skin-tone/-/skin-tone-2.0.0.tgz", + "integrity": "sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==", + "dependencies": { + "unicode-emoji-modifier-base": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/snake-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "dependencies": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "node_modules/sort-css-media-queries": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/sort-css-media-queries/-/sort-css-media-queries-2.2.0.tgz", + "integrity": "sha512-0xtkGhWCC9MGt/EzgnvbbbKhqWjl1+/rncmhTh5qCpbYguXh6S/qwePfv/JQ8jePXXmqingylxoC49pCkSPIbA==", + "engines": { + "node": ">= 6.3.0" + } + }, + "node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/source-map-js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dependencies": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dependencies": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + }, + "node_modules/srcset": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/srcset/-/srcset-4.0.0.tgz", + "integrity": "sha512-wvLeHgcVHKO8Sc/H/5lkGreJQVeYMm9rlmt8PuR1xE31rIuXhuzznUUqAt8MqLhB3MqJdFzlNAfpcWnxiFUcPw==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/static-browser-server": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/static-browser-server/-/static-browser-server-1.0.3.tgz", + "integrity": "sha512-ZUyfgGDdFRbZGGJQ1YhiM930Yczz5VlbJObrQLlk24+qNHVQx4OlLcYswEUo3bIyNAbQUIUR9Yr5/Hqjzqb4zA==", + "dependencies": { + "@open-draft/deferred-promise": "^2.1.0", + "dotenv": "^16.0.3", + "mime-db": "^1.52.0", + "outvariant": "^1.3.0" + } + }, + "node_modules/static-browser-server/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/std-env": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.4.3.tgz", + "integrity": "sha512-f9aPhy8fYBuMN+sNfakZV18U39PbalgjXG3lLB9WkaYTxijru61wb57V9wxxNthXM5Sd88ETBWi29qLAsHO52Q==" + }, + "node_modules/strict-event-emitter": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.4.6.tgz", + "integrity": "sha512-12KWeb+wixJohmnwNFerbyiBrAlq5qJLwIt38etRtKtmmHyDSoGlIqFE9wx+4IwG0aDjI7GV8tc8ZccjWZZtTg==" + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/stringify-entities": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.3.tgz", + "integrity": "sha512-BP9nNHMhhfcMbiuQKCqMjhDP5yBCAxsPu4pHFFzJ6Alo9dZgY4VLDPutXqIjpRiMoKdp7Av85Gr73Q5uH9k7+g==", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "dependencies": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", + "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/style-mod": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.0.tgz", + "integrity": "sha512-Ca5ib8HrFn+f+0n4N4ScTIA9iTOQ7MaGS1ylHcoVqW9J7w2w8PzN6g9gKmTYgGEBH8e120+RCmhpje6jC5uGWA==" + }, + "node_modules/style-to-object": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.4.4.tgz", + "integrity": "sha512-HYNoHZa2GorYNyqiCaBgsxvcJIn7OHq6inEga+E6Ke3m5JkoqpQbnFssk4jwe+K7AhGa2fcha4wSOf1Kn01dMg==", + "dependencies": { + "inline-style-parser": "0.1.1" + } + }, + "node_modules/stylehacks": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-6.1.1.tgz", + "integrity": "sha512-gSTTEQ670cJNoaeIp9KX6lZmm8LJ3jPB5yJmX8Zq/wQxOsAFXV3qjWzHas3YYk1qesuVIyYWWUpZ0vSE/dTSGg==", + "dependencies": { + "browserslist": "^4.23.0", + "postcss-selector-parser": "^6.0.16" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/stylis": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.0.tgz", + "integrity": "sha512-E87pIogpwUsUwXw7dNyU4QDjdgVMy52m+XEOPEKUn161cCzWjjhPSQhByfd1CcNvrOLnXQ6OnnZDwnJrz/Z4YQ==" + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svg-parser": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", + "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==" + }, + "node_modules/svgo": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz", + "integrity": "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==", + "dependencies": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^5.1.0", + "css-tree": "^2.3.1", + "css-what": "^6.1.0", + "csso": "^5.0.5", + "picocolors": "^1.0.0" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/svgo" + } + }, + "node_modules/svgo/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/terser": { + "version": "5.24.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.24.0.tgz", + "integrity": "sha512-ZpGR4Hy3+wBEzVEnHvstMvqpD/nABNelQn/z2r0fjVWGQsN3bpOLzQlqDxmb4CDZnXq5lpjnQ+mHQLAOpfM5iw==", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.9", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz", + "integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.17", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.1", + "terser": "^5.16.8" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/terser-webpack-plugin/node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terser-webpack-plugin/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" + }, + "node_modules/thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" + }, + "node_modules/tiny-invariant": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", + "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==" + }, + "node_modules/tiny-warning": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/ts-dedent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", + "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==", + "engines": { + "node": ">=6.10" + } + }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" + }, + "node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/type-is/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/type-is/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/typescript": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-emoji-modifier-base": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unicode-emoji-modifier-base/-/unicode-emoji-modifier-base-1.0.0.tgz", + "integrity": "sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", + "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "engines": { + "node": ">=4" + } + }, + "node_modules/unified": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.4.tgz", + "integrity": "sha512-apMPnyLjAX+ty4OrNap7yumyVAMlKx5IWU2wlzzUdYJO9A8f1p9m/gywF/GM2ZDFcjQPrx59Mc90KwmxsoklxQ==", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unique-string": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz", + "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==", + "dependencies": { + "crypto-random-string": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/unist-util-is": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position-from-estree": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position-from-estree/-/unist-util-position-from-estree-2.0.0.tgz", + "integrity": "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-remove-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz", + "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", + "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/update-notifier": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-6.0.2.tgz", + "integrity": "sha512-EDxhTEVPZZRLWYcJ4ZXjGFN0oP7qYvbXWzEgRm/Yql4dHX5wDbvh89YHP6PK1lzZJYrMtXUuZZz8XGK+U6U1og==", + "dependencies": { + "boxen": "^7.0.0", + "chalk": "^5.0.1", + "configstore": "^6.0.0", + "has-yarn": "^3.0.0", + "import-lazy": "^4.0.0", + "is-ci": "^3.0.1", + "is-installed-globally": "^0.4.0", + "is-npm": "^6.0.0", + "is-yarn-global": "^0.4.0", + "latest-version": "^7.0.0", + "pupa": "^3.1.0", + "semver": "^7.3.7", + "semver-diff": "^4.0.0", + "xdg-basedir": "^5.1.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/yeoman/update-notifier?sponsor=1" + } + }, + "node_modules/update-notifier/node_modules/boxen": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.1.1.tgz", + "integrity": "sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==", + "dependencies": { + "ansi-align": "^3.0.1", + "camelcase": "^7.0.1", + "chalk": "^5.2.0", + "cli-boxes": "^3.0.0", + "string-width": "^5.1.2", + "type-fest": "^2.13.0", + "widest-line": "^4.0.1", + "wrap-ansi": "^8.1.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/update-notifier/node_modules/camelcase": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz", + "integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/update-notifier/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/uri-js/node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/url-loader": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz", + "integrity": "sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==", + "dependencies": { + "loader-utils": "^2.0.0", + "mime-types": "^2.1.27", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "file-loader": "*", + "webpack": "^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "file-loader": { + "optional": true + } + } + }, + "node_modules/url-loader/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/url-loader/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/url-loader/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "node_modules/url-loader/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/url-loader/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/url-loader/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==" + }, + "node_modules/utility-types": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz", + "integrity": "sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/uvu": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.6.tgz", + "integrity": "sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==", + "dependencies": { + "dequal": "^2.0.0", + "diff": "^5.0.0", + "kleur": "^4.0.3", + "sade": "^1.7.3" + }, + "bin": { + "uvu": "bin.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/uvu/node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/value-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", + "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==" + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vfile": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.1.tgz", + "integrity": "sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-location": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.2.tgz", + "integrity": "sha512-NXPYyxyBSH7zB5U6+3uDdd6Nybz6o6/od9rk8bp9H8GR3L+cm/fC0uUTbqBmUTnMCUDslAGBOIKNfvvb+gGlDg==", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", + "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==" + }, + "node_modules/watchpack": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dependencies": { + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/web-namespaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", + "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/web-worker": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.2.0.tgz", + "integrity": "sha512-PgF341avzqyx60neE9DD+XS26MMNMoUQRz9NOZwW32nPQrF6p77f1htcnjBSEV8BGMKZ16choqUG4hyI0Hx7mA==" + }, + "node_modules/webpack": { + "version": "5.89.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.89.0.tgz", + "integrity": "sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==", + "dependencies": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^1.0.0", + "@webassemblyjs/ast": "^1.11.5", + "@webassemblyjs/wasm-edit": "^1.11.5", + "@webassemblyjs/wasm-parser": "^1.11.5", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.9.0", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.15.0", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.2.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.7", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-bundle-analyzer": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.9.1.tgz", + "integrity": "sha512-jnd6EoYrf9yMxCyYDPj8eutJvtjQNp8PHmni/e/ulydHBWhT5J3menXt3HEkScsu9YqMAcG4CfFjs3rj5pVU1w==", + "dependencies": { + "@discoveryjs/json-ext": "0.5.7", + "acorn": "^8.0.4", + "acorn-walk": "^8.0.0", + "commander": "^7.2.0", + "escape-string-regexp": "^4.0.0", + "gzip-size": "^6.0.0", + "is-plain-object": "^5.0.0", + "lodash.debounce": "^4.0.8", + "lodash.escape": "^4.0.1", + "lodash.flatten": "^4.4.0", + "lodash.invokemap": "^4.6.0", + "lodash.pullall": "^4.2.0", + "lodash.uniqby": "^4.7.0", + "opener": "^1.5.2", + "picocolors": "^1.0.0", + "sirv": "^2.0.3", + "ws": "^7.3.1" + }, + "bin": { + "webpack-bundle-analyzer": "lib/bin/analyzer.js" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/webpack-bundle-analyzer/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/webpack-dev-middleware": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", + "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==", + "dependencies": { + "colorette": "^2.0.10", + "memfs": "^3.4.3", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/webpack-dev-middleware/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/webpack-dev-middleware/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/webpack-dev-middleware/node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/webpack-dev-server": { + "version": "4.15.1", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.1.tgz", + "integrity": "sha512-5hbAst3h3C3L8w6W4P96L5vaV0PxSmJhxZvWKYIdgxOQm8pNZ5dEOmmSLBVpP85ReeyRt6AS1QJNyo/oFFPeVA==", + "dependencies": { + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/express": "^4.17.13", + "@types/serve-index": "^1.9.1", + "@types/serve-static": "^1.13.10", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.5.5", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.0.11", + "chokidar": "^3.5.3", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^2.0.0", + "default-gateway": "^6.0.3", + "express": "^4.17.3", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.3", + "ipaddr.js": "^2.0.1", + "launch-editor": "^2.6.0", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "rimraf": "^3.0.2", + "schema-utils": "^4.0.0", + "selfsigned": "^2.1.1", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^5.3.1", + "ws": "^8.13.0" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.37.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + }, + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server/node_modules/ws": { + "version": "8.14.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz", + "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/webpack-merge": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", + "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", + "dependencies": { + "clone-deep": "^4.0.1", + "flat": "^5.0.2", + "wildcard": "^2.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/webpack/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "node_modules/webpack/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/webpack/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpackbar": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/webpackbar/-/webpackbar-5.0.2.tgz", + "integrity": "sha512-BmFJo7veBDgQzfWXl/wwYXr/VFus0614qZ8i9znqcl9fnEdiVkdbi0TedLQ6xAK92HZHDJ0QmyQ0fmuZPAgCYQ==", + "dependencies": { + "chalk": "^4.1.0", + "consola": "^2.15.3", + "pretty-time": "^1.1.0", + "std-env": "^3.0.1" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "webpack": "3 || 4 || 5" + } + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/widest-line": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", + "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==", + "dependencies": { + "string-width": "^5.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wildcard": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", + "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==" + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/ws": { + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xdg-basedir": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-5.1.0.tgz", + "integrity": "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/xml-js": { + "version": "1.6.11", + "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz", + "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==", + "dependencies": { + "sax": "^1.2.4" + }, + "bin": { + "xml-js": "bin/cli.js" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/yocto-queue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", + "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + } + }, + "dependencies": { + "@algolia/autocomplete-core": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.9.3.tgz", + "integrity": "sha512-009HdfugtGCdC4JdXUbVJClA0q0zh24yyePn+KUGk3rP7j8FEe/m5Yo/z65gn6nP/cM39PxpzqKrL7A6fP6PPw==", + "requires": { + "@algolia/autocomplete-plugin-algolia-insights": "1.9.3", + "@algolia/autocomplete-shared": "1.9.3" + } + }, + "@algolia/autocomplete-plugin-algolia-insights": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.9.3.tgz", + "integrity": "sha512-a/yTUkcO/Vyy+JffmAnTWbr4/90cLzw+CC3bRbhnULr/EM0fGNvM13oQQ14f2moLMcVDyAx/leczLlAOovhSZg==", + "requires": { + "@algolia/autocomplete-shared": "1.9.3" + } + }, + "@algolia/autocomplete-preset-algolia": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.9.3.tgz", + "integrity": "sha512-d4qlt6YmrLMYy95n5TB52wtNDr6EgAIPH81dvvvW8UmuWRgxEtY0NJiPwl/h95JtG2vmRM804M0DSwMCNZlzRA==", + "requires": { + "@algolia/autocomplete-shared": "1.9.3" + } + }, + "@algolia/autocomplete-shared": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.9.3.tgz", + "integrity": "sha512-Wnm9E4Ye6Rl6sTTqjoymD+l8DjSTHsHboVRYrKgEt8Q7UHm9nYbqhN/i0fhUYA3OAEH7WA8x3jfpnmJm3rKvaQ==", + "requires": {} + }, + "@algolia/cache-browser-local-storage": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.23.3.tgz", + "integrity": "sha512-vRHXYCpPlTDE7i6UOy2xE03zHF2C8MEFjPN2v7fRbqVpcOvAUQK81x3Kc21xyb5aSIpYCjWCZbYZuz8Glyzyyg==", + "requires": { + "@algolia/cache-common": "4.23.3" + } + }, + "@algolia/cache-common": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.23.3.tgz", + "integrity": "sha512-h9XcNI6lxYStaw32pHpB1TMm0RuxphF+Ik4o7tcQiodEdpKK+wKufY6QXtba7t3k8eseirEMVB83uFFF3Nu54A==" + }, + "@algolia/cache-in-memory": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.23.3.tgz", + "integrity": "sha512-yvpbuUXg/+0rbcagxNT7un0eo3czx2Uf0y4eiR4z4SD7SiptwYTpbuS0IHxcLHG3lq22ukx1T6Kjtk/rT+mqNg==", + "requires": { + "@algolia/cache-common": "4.23.3" + } + }, + "@algolia/client-account": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.23.3.tgz", + "integrity": "sha512-hpa6S5d7iQmretHHF40QGq6hz0anWEHGlULcTIT9tbUssWUriN9AUXIFQ8Ei4w9azD0hc1rUok9/DeQQobhQMA==", + "requires": { + "@algolia/client-common": "4.23.3", + "@algolia/client-search": "4.23.3", + "@algolia/transporter": "4.23.3" + } + }, + "@algolia/client-analytics": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.23.3.tgz", + "integrity": "sha512-LBsEARGS9cj8VkTAVEZphjxTjMVCci+zIIiRhpFun9jGDUlS1XmhCW7CTrnaWeIuCQS/2iPyRqSy1nXPjcBLRA==", + "requires": { + "@algolia/client-common": "4.23.3", + "@algolia/client-search": "4.23.3", + "@algolia/requester-common": "4.23.3", + "@algolia/transporter": "4.23.3" + } + }, + "@algolia/client-common": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.23.3.tgz", + "integrity": "sha512-l6EiPxdAlg8CYhroqS5ybfIczsGUIAC47slLPOMDeKSVXYG1n0qGiz4RjAHLw2aD0xzh2EXZ7aRguPfz7UKDKw==", + "requires": { + "@algolia/requester-common": "4.23.3", + "@algolia/transporter": "4.23.3" + } + }, + "@algolia/client-personalization": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.23.3.tgz", + "integrity": "sha512-3E3yF3Ocr1tB/xOZiuC3doHQBQ2zu2MPTYZ0d4lpfWads2WTKG7ZzmGnsHmm63RflvDeLK/UVx7j2b3QuwKQ2g==", + "requires": { + "@algolia/client-common": "4.23.3", + "@algolia/requester-common": "4.23.3", + "@algolia/transporter": "4.23.3" + } + }, + "@algolia/client-search": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.23.3.tgz", + "integrity": "sha512-P4VAKFHqU0wx9O+q29Q8YVuaowaZ5EM77rxfmGnkHUJggh28useXQdopokgwMeYw2XUht49WX5RcTQ40rZIabw==", + "requires": { + "@algolia/client-common": "4.23.3", + "@algolia/requester-common": "4.23.3", + "@algolia/transporter": "4.23.3" + } + }, + "@algolia/events": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@algolia/events/-/events-4.0.1.tgz", + "integrity": "sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ==" + }, + "@algolia/logger-common": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.23.3.tgz", + "integrity": "sha512-y9kBtmJwiZ9ZZ+1Ek66P0M68mHQzKRxkW5kAAXYN/rdzgDN0d2COsViEFufxJ0pb45K4FRcfC7+33YB4BLrZ+g==" + }, + "@algolia/logger-console": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.23.3.tgz", + "integrity": "sha512-8xoiseoWDKuCVnWP8jHthgaeobDLolh00KJAdMe9XPrWPuf1by732jSpgy2BlsLTaT9m32pHI8CRfrOqQzHv3A==", + "requires": { + "@algolia/logger-common": "4.23.3" + } + }, + "@algolia/recommend": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-4.23.3.tgz", + "integrity": "sha512-9fK4nXZF0bFkdcLBRDexsnGzVmu4TSYZqxdpgBW2tEyfuSSY54D4qSRkLmNkrrz4YFvdh2GM1gA8vSsnZPR73w==", + "requires": { + "@algolia/cache-browser-local-storage": "4.23.3", + "@algolia/cache-common": "4.23.3", + "@algolia/cache-in-memory": "4.23.3", + "@algolia/client-common": "4.23.3", + "@algolia/client-search": "4.23.3", + "@algolia/logger-common": "4.23.3", + "@algolia/logger-console": "4.23.3", + "@algolia/requester-browser-xhr": "4.23.3", + "@algolia/requester-common": "4.23.3", + "@algolia/requester-node-http": "4.23.3", + "@algolia/transporter": "4.23.3" + } + }, + "@algolia/requester-browser-xhr": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.23.3.tgz", + "integrity": "sha512-jDWGIQ96BhXbmONAQsasIpTYWslyjkiGu0Quydjlowe+ciqySpiDUrJHERIRfELE5+wFc7hc1Q5hqjGoV7yghw==", + "requires": { + "@algolia/requester-common": "4.23.3" + } + }, + "@algolia/requester-common": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.23.3.tgz", + "integrity": "sha512-xloIdr/bedtYEGcXCiF2muajyvRhwop4cMZo+K2qzNht0CMzlRkm8YsDdj5IaBhshqfgmBb3rTg4sL4/PpvLYw==" + }, + "@algolia/requester-node-http": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.23.3.tgz", + "integrity": "sha512-zgu++8Uj03IWDEJM3fuNl34s746JnZOWn1Uz5taV1dFyJhVM/kTNw9Ik7YJWiUNHJQXcaD8IXD1eCb0nq/aByA==", + "requires": { + "@algolia/requester-common": "4.23.3" + } + }, + "@algolia/transporter": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.23.3.tgz", + "integrity": "sha512-Wjl5gttqnf/gQKJA+dafnD0Y6Yw97yvfY8R9h0dQltX1GXTgNs1zWgvtWW0tHl1EgMdhAyw189uWiZMnL3QebQ==", + "requires": { + "@algolia/cache-common": "4.23.3", + "@algolia/logger-common": "4.23.3", + "@algolia/requester-common": "4.23.3" + } + }, + "@ampproject/remapping": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "requires": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@babel/code-frame": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", + "requires": { + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/compat-data": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.2.tgz", + "integrity": "sha512-0S9TQMmDHlqAZ2ITT95irXKfxN9bncq8ZCoJhun3nHL/lLUxd2NKBJYoNGWH7S0hz6fRQwWlAWn/ILM0C70KZQ==" + }, + "@babel/core": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.5.tgz", + "integrity": "sha512-Cwc2XjUrG4ilcfOw4wBAK+enbdgwAcAJCfGUItPBKR7Mjw4aEfAFYrLxeRp4jWgtNIKn3n2AlBOfwwafl+42/g==", + "requires": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.5", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.23.5", + "@babel/parser": "^7.23.5", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.5", + "@babel/types": "^7.23.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } + } + }, + "@babel/generator": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.5.tgz", + "integrity": "sha512-BPssCHrBD+0YrxviOa3QzpqwhNIXKEtOa2jQrm4FlmkC2apYgRnQcmPWiGZDlGxiNtltnUFolMe8497Esry+jA==", + "requires": { + "@babel/types": "^7.23.5", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", + "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz", + "integrity": "sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==", + "requires": { + "@babel/types": "^7.22.15" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", + "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", + "requires": { + "@babel/compat-data": "^7.22.9", + "@babel/helper-validator-option": "^7.22.15", + "browserslist": "^4.21.9", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } + } + }, + "@babel/helper-create-class-features-plugin": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.15.tgz", + "integrity": "sha512-jKkwA59IXcvSaiK2UN45kKwSC9o+KuoXsBDvHvU/7BecYIp8GQ2UwrVvFgJASUT+hBnwJx6MhvMCuMzwZZ7jlg==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-member-expression-to-functions": "^7.22.15", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "semver": "^6.3.1" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } + } + }, + "@babel/helper-create-regexp-features-plugin": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz", + "integrity": "sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "regexpu-core": "^5.3.1", + "semver": "^6.3.1" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } + } + }, + "@babel/helper-define-polyfill-provider": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.3.tgz", + "integrity": "sha512-WBrLmuPP47n7PNwsZ57pqam6G/RGo1vw/87b0Blc53tZNGZ4x7YvZ6HgQe2vo1W/FR20OgjeZuGXzudPiXHFug==", + "requires": { + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2" + } + }, + "@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==" + }, + "@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "requires": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz", + "integrity": "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==", + "requires": { + "@babel/types": "^7.23.0" + } + }, + "@babel/helper-module-imports": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", + "requires": { + "@babel/types": "^7.22.15" + } + }, + "@babel/helper-module-transforms": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", + "requires": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.20" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", + "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.7.tgz", + "integrity": "sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==" + }, + "@babel/helper-remap-async-to-generator": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz", + "integrity": "sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-wrap-function": "^7.22.20" + } + }, + "@babel/helper-replace-supers": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz", + "integrity": "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==", + "requires": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-member-expression-to-functions": "^7.22.15", + "@babel/helper-optimise-call-expression": "^7.22.5" + } + }, + "@babel/helper-simple-access": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", + "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-string-parser": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", + "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==" + }, + "@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==" + }, + "@babel/helper-validator-option": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", + "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==" + }, + "@babel/helper-wrap-function": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz", + "integrity": "sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==", + "requires": { + "@babel/helper-function-name": "^7.22.5", + "@babel/template": "^7.22.15", + "@babel/types": "^7.22.19" + } + }, + "@babel/helpers": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.5.tgz", + "integrity": "sha512-oO7us8FzTEsG3U6ag9MfdF1iA/7Z6dz+MtFhifZk8C8o453rGJFFWUP1t+ULM9TUIAzC9uxXEiXjOiVMyd7QPg==", + "requires": { + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.5", + "@babel/types": "^7.23.5" + } + }, + "@babel/highlight": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", + "requires": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/parser": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.5.tgz", + "integrity": "sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ==" + }, + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.15.tgz", + "integrity": "sha512-FB9iYlz7rURmRJyXRKEnalYPPdn87H5no108cyuQQyMwlpJ2SJtpIUBI27kdTin956pz+LPypkPVPUTlxOmrsg==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.15.tgz", + "integrity": "sha512-Hyph9LseGvAeeXzikV88bczhsrLrIZqDPxO+sSmAunMPaGrBGhfMWzCPYTtiW9t+HzSE2wtV8e5cc5P6r1xMDQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-transform-optional-chaining": "^7.22.15" + } + }, + "@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "requires": {} + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-syntax-import-assertions": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.22.5.tgz", + "integrity": "sha512-rdV97N7KqsRzeNGoWUOK6yUsWarLjE5Su/Snk9IYPU9CwkWHs4t+rTGOvffTR8XGkJMTAdLfO0xVnXm8wugIJg==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-syntax-import-attributes": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.22.5.tgz", + "integrity": "sha512-KwvoWDeNKPETmozyFE0P2rOLqh39EoQHNjqizrI5B8Vt0ZNS7M56s7dAiAqbYfiAYOuIzIh96z3iR2ktgu3tEg==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-jsx": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", + "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-typescript": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", + "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-arrow-functions": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.22.5.tgz", + "integrity": "sha512-26lTNXoVRdAnsaDXPpvCNUq+OVWEVC6bx7Vvz9rC53F2bagUWW4u4ii2+h8Fejfh7RYqPxn+libeFBBck9muEw==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-async-generator-functions": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.2.tgz", + "integrity": "sha512-BBYVGxbDVHfoeXbOwcagAkOQAm9NxoTdMGfTqghu1GrvadSaw6iW3Je6IcL5PNOw8VwjxqBECXy50/iCQSY/lQ==", + "requires": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-remap-async-to-generator": "^7.22.20", + "@babel/plugin-syntax-async-generators": "^7.8.4" + } + }, + "@babel/plugin-transform-async-to-generator": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.22.5.tgz", + "integrity": "sha512-b1A8D8ZzE/VhNDoV1MSJTnpKkCG5bJo+19R4o4oy03zM7ws8yEMK755j61Dc3EyvdysbqH5BOOTquJ7ZX9C6vQ==", + "requires": { + "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-remap-async-to-generator": "^7.22.5" + } + }, + "@babel/plugin-transform-block-scoped-functions": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.22.5.tgz", + "integrity": "sha512-tdXZ2UdknEKQWKJP1KMNmuF5Lx3MymtMN/pvA+p/VEkhK8jVcQ1fzSy8KM9qRYhAf2/lV33hoMPKI/xaI9sADA==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-block-scoping": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.0.tgz", + "integrity": "sha512-cOsrbmIOXmf+5YbL99/S49Y3j46k/T16b9ml8bm9lP6N9US5iQ2yBK7gpui1pg0V/WMcXdkfKbTb7HXq9u+v4g==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-class-properties": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.22.5.tgz", + "integrity": "sha512-nDkQ0NfkOhPTq8YCLiWNxp1+f9fCobEjCb0n8WdbNUBc4IB5V7P1QnX9IjpSoquKrXF5SKojHleVNs2vGeHCHQ==", + "requires": { + "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-class-static-block": { + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.11.tgz", + "integrity": "sha512-GMM8gGmqI7guS/llMFk1bJDkKfn3v3C4KHK9Yg1ey5qcHcOlKb0QvcMrgzvxo+T03/4szNh5lghY+fEC98Kq9g==", + "requires": { + "@babel/helper-create-class-features-plugin": "^7.22.11", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + } + }, + "@babel/plugin-transform-classes": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.15.tgz", + "integrity": "sha512-VbbC3PGjBdE0wAWDdHM9G8Gm977pnYI0XpqMd6LrKISj8/DJXEsWqgRuTYaNE9Bv0JGhTZUzHDlMk18IpOuoqw==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.9", + "@babel/helper-split-export-declaration": "^7.22.6", + "globals": "^11.1.0" + } + }, + "@babel/plugin-transform-computed-properties": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.22.5.tgz", + "integrity": "sha512-4GHWBgRf0krxPX+AaPtgBAlTgTeZmqDynokHOX7aqqAB4tHs3U2Y02zH6ETFdLZGcg9UQSD1WCmkVrE9ErHeOg==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/template": "^7.22.5" + } + }, + "@babel/plugin-transform-destructuring": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.0.tgz", + "integrity": "sha512-vaMdgNXFkYrB+8lbgniSYWHsgqK5gjaMNcc84bMIOMRLH0L9AqYq3hwMdvnyqj1OPqea8UtjPEuS/DCenah1wg==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-dotall-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.22.5.tgz", + "integrity": "sha512-5/Yk9QxCQCl+sOIB1WelKnVRxTJDSAIxtJLL2/pqL14ZVlbH0fUQUZa/T5/UnQtBNgghR7mfB8ERBKyKPCi7Vw==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-duplicate-keys": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.22.5.tgz", + "integrity": "sha512-dEnYD+9BBgld5VBXHnF/DbYGp3fqGMsyxKbtD1mDyIA7AkTSpKXFhCVuj/oQVOoALfBs77DudA0BE4d5mcpmqw==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-dynamic-import": { + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.11.tgz", + "integrity": "sha512-g/21plo58sfteWjaO0ZNVb+uEOkJNjAaHhbejrnBmu011l/eNDScmkbjCC3l4FKb10ViaGU4aOkFznSu2zRHgA==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + } + }, + "@babel/plugin-transform-exponentiation-operator": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.22.5.tgz", + "integrity": "sha512-vIpJFNM/FjZ4rh1myqIya9jXwrwwgFRHPjT3DkUA9ZLHuzox8jiXkOLvwm1H+PQIP3CqfC++WPKeuDi0Sjdj1g==", + "requires": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-export-namespace-from": { + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.11.tgz", + "integrity": "sha512-xa7aad7q7OiT8oNZ1mU7NrISjlSkVdMbNxn9IuLZyL9AJEhs1Apba3I+u5riX1dIkdptP5EKDG5XDPByWxtehw==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + } + }, + "@babel/plugin-transform-for-of": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.15.tgz", + "integrity": "sha512-me6VGeHsx30+xh9fbDLLPi0J1HzmeIIyenoOQHuw2D4m2SAU3NrspX5XxJLBpqn5yrLzrlw2Iy3RA//Bx27iOA==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-function-name": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.22.5.tgz", + "integrity": "sha512-UIzQNMS0p0HHiQm3oelztj+ECwFnj+ZRV4KnguvlsD2of1whUeM6o7wGNj6oLwcDoAXQ8gEqfgC24D+VdIcevg==", + "requires": { + "@babel/helper-compilation-targets": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-json-strings": { + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.11.tgz", + "integrity": "sha512-CxT5tCqpA9/jXFlme9xIBCc5RPtdDq3JpkkhgHQqtDdiTnTI0jtZ0QzXhr5DILeYifDPp2wvY2ad+7+hLMW5Pw==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-json-strings": "^7.8.3" + } + }, + "@babel/plugin-transform-literals": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.22.5.tgz", + "integrity": "sha512-fTLj4D79M+mepcw3dgFBTIDYpbcB9Sm0bpm4ppXPaO+U+PKFFyV9MGRvS0gvGw62sd10kT5lRMKXAADb9pWy8g==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-logical-assignment-operators": { + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.11.tgz", + "integrity": "sha512-qQwRTP4+6xFCDV5k7gZBF3C31K34ut0tbEcTKxlX/0KXxm9GLcO14p570aWxFvVzx6QAfPgq7gaeIHXJC8LswQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + } + }, + "@babel/plugin-transform-member-expression-literals": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.22.5.tgz", + "integrity": "sha512-RZEdkNtzzYCFl9SE9ATaUMTj2hqMb4StarOJLrZRbqqU4HSBE7UlBw9WBWQiDzrJZJdUWiMTVDI6Gv/8DPvfew==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-modules-amd": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.23.0.tgz", + "integrity": "sha512-xWT5gefv2HGSm4QHtgc1sYPbseOyf+FFDo2JbpE25GWl5BqTGO9IMwTYJRoIdjsF85GE+VegHxSCUt5EvoYTAw==", + "requires": { + "@babel/helper-module-transforms": "^7.23.0", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-modules-commonjs": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.0.tgz", + "integrity": "sha512-32Xzss14/UVc7k9g775yMIvkVK8xwKE0DPdP5JTapr3+Z9w4tzeOuLNY6BXDQR6BdnzIlXnCGAzsk/ICHBLVWQ==", + "requires": { + "@babel/helper-module-transforms": "^7.23.0", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-simple-access": "^7.22.5" + } + }, + "@babel/plugin-transform-modules-systemjs": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.0.tgz", + "integrity": "sha512-qBej6ctXZD2f+DhlOC9yO47yEYgUh5CZNz/aBoH4j/3NOlRfJXJbY7xDQCqQVf9KbrqGzIWER1f23doHGrIHFg==", + "requires": { + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-module-transforms": "^7.23.0", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20" + } + }, + "@babel/plugin-transform-modules-umd": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.22.5.tgz", + "integrity": "sha512-+S6kzefN/E1vkSsKx8kmQuqeQsvCKCd1fraCM7zXm4SFoggI099Tr4G8U81+5gtMdUeMQ4ipdQffbKLX0/7dBQ==", + "requires": { + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz", + "integrity": "sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-new-target": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.22.5.tgz", + "integrity": "sha512-AsF7K0Fx/cNKVyk3a+DW0JLo+Ua598/NxMRvxDnkpCIGFh43+h/v2xyhRUYf6oD8gE4QtL83C7zZVghMjHd+iw==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.11.tgz", + "integrity": "sha512-YZWOw4HxXrotb5xsjMJUDlLgcDXSfO9eCmdl1bgW4+/lAGdkjaEvOnQ4p5WKKdUgSzO39dgPl0pTnfxm0OAXcg==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + } + }, + "@babel/plugin-transform-numeric-separator": { + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.11.tgz", + "integrity": "sha512-3dzU4QGPsILdJbASKhF/V2TVP+gJya1PsueQCxIPCEcerqF21oEcrob4mzjsp2Py/1nLfF5m+xYNMDpmA8vffg==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + } + }, + "@babel/plugin-transform-object-rest-spread": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.15.tgz", + "integrity": "sha512-fEB+I1+gAmfAyxZcX1+ZUwLeAuuf8VIg67CTznZE0MqVFumWkh8xWtn58I4dxdVf080wn7gzWoF8vndOViJe9Q==", + "requires": { + "@babel/compat-data": "^7.22.9", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.22.15" + } + }, + "@babel/plugin-transform-object-super": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.22.5.tgz", + "integrity": "sha512-klXqyaT9trSjIUrcsYIfETAzmOEZL3cBYqOYLJxBHfMFFggmXOv+NYSX/Jbs9mzMVESw/WycLFPRx8ba/b2Ipw==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.5" + } + }, + "@babel/plugin-transform-optional-catch-binding": { + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.11.tgz", + "integrity": "sha512-rli0WxesXUeCJnMYhzAglEjLWVDF6ahb45HuprcmQuLidBJFWjNnOzssk2kuc6e33FlLaiZhG/kUIzUMWdBKaQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + } + }, + "@babel/plugin-transform-optional-chaining": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.0.tgz", + "integrity": "sha512-sBBGXbLJjxTzLBF5rFWaikMnOGOk/BmK6vVByIdEggZ7Vn6CvWXZyRkkLFK6WE0IF8jSliyOkUN6SScFgzCM0g==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + } + }, + "@babel/plugin-transform-parameters": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.15.tgz", + "integrity": "sha512-hjk7qKIqhyzhhUvRT683TYQOFa/4cQKwQy7ALvTpODswN40MljzNDa0YldevS6tGbxwaEKVn502JmY0dP7qEtQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-private-methods": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.22.5.tgz", + "integrity": "sha512-PPjh4gyrQnGe97JTalgRGMuU4icsZFnWkzicB/fUtzlKUqvsWBKEpPPfr5a2JiyirZkHxnAqkQMO5Z5B2kK3fA==", + "requires": { + "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-private-property-in-object": { + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.11.tgz", + "integrity": "sha512-sSCbqZDBKHetvjSwpyWzhuHkmW5RummxJBVbYLkGkaiTOWGxml7SXt0iWa03bzxFIx7wOj3g/ILRd0RcJKBeSQ==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.22.11", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + } + }, + "@babel/plugin-transform-property-literals": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.22.5.tgz", + "integrity": "sha512-TiOArgddK3mK/x1Qwf5hay2pxI6wCZnvQqrFSqbtg1GLl2JcNMitVH/YnqjP+M31pLUeTfzY1HAXFDnUBV30rQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-react-constant-elements": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.24.7.tgz", + "integrity": "sha512-7LidzZfUXyfZ8/buRW6qIIHBY8wAZ1OrY9c/wTr8YhZ6vMPo+Uc/CVFLYY1spZrEQlD4w5u8wjqk5NQ3OVqQKA==", + "requires": { + "@babel/helper-plugin-utils": "^7.24.7" + } + }, + "@babel/plugin-transform-react-display-name": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.22.5.tgz", + "integrity": "sha512-PVk3WPYudRF5z4GKMEYUrLjPl38fJSKNaEOkFuoprioowGuWN6w2RKznuFNSlJx7pzzXXStPUnNSOEO0jL5EVw==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-react-jsx": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.22.15.tgz", + "integrity": "sha512-oKckg2eZFa8771O/5vi7XeTvmM6+O9cxZu+kanTU7tD4sin5nO/G8jGJhq8Hvt2Z0kUoEDRayuZLaUlYl8QuGA==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-jsx": "^7.22.5", + "@babel/types": "^7.22.15" + } + }, + "@babel/plugin-transform-react-jsx-development": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.22.5.tgz", + "integrity": "sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A==", + "requires": { + "@babel/plugin-transform-react-jsx": "^7.22.5" + } + }, + "@babel/plugin-transform-react-pure-annotations": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.22.5.tgz", + "integrity": "sha512-gP4k85wx09q+brArVinTXhWiyzLl9UpmGva0+mWyKxk6JZequ05x3eUcIUE+FyttPKJFRRVtAvQaJ6YF9h1ZpA==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-regenerator": { + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.10.tgz", + "integrity": "sha512-F28b1mDt8KcT5bUyJc/U9nwzw6cV+UmTeRlXYIl2TNqMMJif0Jeey9/RQ3C4NOd2zp0/TRsDns9ttj2L523rsw==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "regenerator-transform": "^0.15.2" + } + }, + "@babel/plugin-transform-reserved-words": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.22.5.tgz", + "integrity": "sha512-DTtGKFRQUDm8svigJzZHzb/2xatPc6TzNvAIJ5GqOKDsGFYgAskjRulbR/vGsPKq3OPqtexnz327qYpP57RFyA==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-runtime": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.23.2.tgz", + "integrity": "sha512-XOntj6icgzMS58jPVtQpiuF6ZFWxQiJavISGx5KGjRj+3gqZr8+N6Kx+N9BApWzgS+DOjIZfXXj0ZesenOWDyA==", + "requires": { + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "babel-plugin-polyfill-corejs2": "^0.4.6", + "babel-plugin-polyfill-corejs3": "^0.8.5", + "babel-plugin-polyfill-regenerator": "^0.5.3", + "semver": "^6.3.1" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } + } + }, + "@babel/plugin-transform-shorthand-properties": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.22.5.tgz", + "integrity": "sha512-vM4fq9IXHscXVKzDv5itkO1X52SmdFBFcMIBZ2FRn2nqVYqw6dBexUgMvAjHW+KXpPPViD/Yo3GrDEBaRC0QYA==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-spread": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.22.5.tgz", + "integrity": "sha512-5ZzDQIGyvN4w8+dMmpohL6MBo+l2G7tfC/O2Dg7/hjpgeWvUx8FzfeOKxGog9IimPa4YekaQ9PlDqTLOljkcxg==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" + } + }, + "@babel/plugin-transform-sticky-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.22.5.tgz", + "integrity": "sha512-zf7LuNpHG0iEeiyCNwX4j3gDg1jgt1k3ZdXBKbZSoA3BbGQGvMiSvfbZRR3Dr3aeJe3ooWFZxOOG3IRStYp2Bw==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-template-literals": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.22.5.tgz", + "integrity": "sha512-5ciOehRNf+EyUeewo8NkbQiUs4d6ZxiHo6BcBcnFlgiJfu16q0bQUw9Jvo0b0gBKFG1SMhDSjeKXSYuJLeFSMA==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-typeof-symbol": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.22.5.tgz", + "integrity": "sha512-bYkI5lMzL4kPii4HHEEChkD0rkc+nvnlR6+o/qdqR6zrm0Sv/nodmyLhlq2DO0YKLUNd2VePmPRjJXSBh9OIdA==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-typescript": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.15.tgz", + "integrity": "sha512-1uirS0TnijxvQLnlv5wQBwOX3E1wCFX7ITv+9pBV2wKEk4K+M5tqDaoNXnTH8tjEIYHLO98MwiTWO04Ggz4XuA==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-typescript": "^7.22.5" + } + }, + "@babel/plugin-transform-unicode-escapes": { + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.10.tgz", + "integrity": "sha512-lRfaRKGZCBqDlRU3UIFovdp9c9mEvlylmpod0/OatICsSfuQ9YFthRo1tpTkGsklEefZdqlEFdY4A2dwTb6ohg==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-unicode-property-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.22.5.tgz", + "integrity": "sha512-HCCIb+CbJIAE6sXn5CjFQXMwkCClcOfPCzTlilJ8cUatfzwHlWQkbtV0zD338u9dZskwvuOYTuuaMaA8J5EI5A==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-unicode-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.22.5.tgz", + "integrity": "sha512-028laaOKptN5vHJf9/Arr/HiJekMd41hOEZYvNsrsXqJ7YPYuX2bQxh31fkZzGmq3YqHRJzYFFAVYvKfMPKqyg==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-unicode-sets-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.22.5.tgz", + "integrity": "sha512-lhMfi4FC15j13eKrh3DnYHjpGj6UKQHtNKTbtc1igvAhRy4+kLhV07OpLcsN0VgDEw/MjAvJO4BdMJsHwMhzCg==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/preset-env": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.23.2.tgz", + "integrity": "sha512-BW3gsuDD+rvHL2VO2SjAUNTBe5YrjsTiDyqamPDWY723na3/yPQ65X5oQkFVJZ0o50/2d+svm1rkPoJeR1KxVQ==", + "requires": { + "@babel/compat-data": "^7.23.2", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-option": "^7.22.15", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.22.15", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.22.15", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-import-assertions": "^7.22.5", + "@babel/plugin-syntax-import-attributes": "^7.22.5", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.22.5", + "@babel/plugin-transform-async-generator-functions": "^7.23.2", + "@babel/plugin-transform-async-to-generator": "^7.22.5", + "@babel/plugin-transform-block-scoped-functions": "^7.22.5", + "@babel/plugin-transform-block-scoping": "^7.23.0", + "@babel/plugin-transform-class-properties": "^7.22.5", + "@babel/plugin-transform-class-static-block": "^7.22.11", + "@babel/plugin-transform-classes": "^7.22.15", + "@babel/plugin-transform-computed-properties": "^7.22.5", + "@babel/plugin-transform-destructuring": "^7.23.0", + "@babel/plugin-transform-dotall-regex": "^7.22.5", + "@babel/plugin-transform-duplicate-keys": "^7.22.5", + "@babel/plugin-transform-dynamic-import": "^7.22.11", + "@babel/plugin-transform-exponentiation-operator": "^7.22.5", + "@babel/plugin-transform-export-namespace-from": "^7.22.11", + "@babel/plugin-transform-for-of": "^7.22.15", + "@babel/plugin-transform-function-name": "^7.22.5", + "@babel/plugin-transform-json-strings": "^7.22.11", + "@babel/plugin-transform-literals": "^7.22.5", + "@babel/plugin-transform-logical-assignment-operators": "^7.22.11", + "@babel/plugin-transform-member-expression-literals": "^7.22.5", + "@babel/plugin-transform-modules-amd": "^7.23.0", + "@babel/plugin-transform-modules-commonjs": "^7.23.0", + "@babel/plugin-transform-modules-systemjs": "^7.23.0", + "@babel/plugin-transform-modules-umd": "^7.22.5", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", + "@babel/plugin-transform-new-target": "^7.22.5", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.11", + "@babel/plugin-transform-numeric-separator": "^7.22.11", + "@babel/plugin-transform-object-rest-spread": "^7.22.15", + "@babel/plugin-transform-object-super": "^7.22.5", + "@babel/plugin-transform-optional-catch-binding": "^7.22.11", + "@babel/plugin-transform-optional-chaining": "^7.23.0", + "@babel/plugin-transform-parameters": "^7.22.15", + "@babel/plugin-transform-private-methods": "^7.22.5", + "@babel/plugin-transform-private-property-in-object": "^7.22.11", + "@babel/plugin-transform-property-literals": "^7.22.5", + "@babel/plugin-transform-regenerator": "^7.22.10", + "@babel/plugin-transform-reserved-words": "^7.22.5", + "@babel/plugin-transform-shorthand-properties": "^7.22.5", + "@babel/plugin-transform-spread": "^7.22.5", + "@babel/plugin-transform-sticky-regex": "^7.22.5", + "@babel/plugin-transform-template-literals": "^7.22.5", + "@babel/plugin-transform-typeof-symbol": "^7.22.5", + "@babel/plugin-transform-unicode-escapes": "^7.22.10", + "@babel/plugin-transform-unicode-property-regex": "^7.22.5", + "@babel/plugin-transform-unicode-regex": "^7.22.5", + "@babel/plugin-transform-unicode-sets-regex": "^7.22.5", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "@babel/types": "^7.23.0", + "babel-plugin-polyfill-corejs2": "^0.4.6", + "babel-plugin-polyfill-corejs3": "^0.8.5", + "babel-plugin-polyfill-regenerator": "^0.5.3", + "core-js-compat": "^3.31.0", + "semver": "^6.3.1" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } + } + }, + "@babel/preset-modules": { + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + } + }, + "@babel/preset-react": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.22.15.tgz", + "integrity": "sha512-Csy1IJ2uEh/PecCBXXoZGAZBeCATTuePzCSB7dLYWS0vOEj6CNpjxIhW4duWwZodBNueH7QO14WbGn8YyeuN9w==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-option": "^7.22.15", + "@babel/plugin-transform-react-display-name": "^7.22.5", + "@babel/plugin-transform-react-jsx": "^7.22.15", + "@babel/plugin-transform-react-jsx-development": "^7.22.5", + "@babel/plugin-transform-react-pure-annotations": "^7.22.5" + } + }, + "@babel/preset-typescript": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.23.2.tgz", + "integrity": "sha512-u4UJc1XsS1GhIGteM8rnGiIvf9rJpiVgMEeCnwlLA7WJPC+jcXWJAGxYmeqs5hOZD8BbAfnV5ezBOxQbb4OUxA==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-option": "^7.22.15", + "@babel/plugin-syntax-jsx": "^7.22.5", + "@babel/plugin-transform-modules-commonjs": "^7.23.0", + "@babel/plugin-transform-typescript": "^7.22.15" + } + }, + "@babel/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" + }, + "@babel/runtime": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz", + "integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==", + "requires": { + "regenerator-runtime": "^0.14.0" + } + }, + "@babel/runtime-corejs3": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.23.2.tgz", + "integrity": "sha512-54cIh74Z1rp4oIjsHjqN+WM4fMyCBYe+LpZ9jWm51CZ1fbH3SkAzQD/3XLoNkjbJ7YEmjobLXyvQrFypRHOrXw==", + "requires": { + "core-js-pure": "^3.30.2", + "regenerator-runtime": "^0.14.0" + } + }, + "@babel/template": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "requires": { + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" + } + }, + "@babel/traverse": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.5.tgz", + "integrity": "sha512-czx7Xy5a6sapWWRx61m1Ke1Ra4vczu1mCTtJam5zRTBOonfdJ+S/B6HYmGYu3fJtr8GGET3si6IhgWVBhJ/m8w==", + "requires": { + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.5", + "@babel/types": "^7.23.5", + "debug": "^4.1.0", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.5.tgz", + "integrity": "sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w==", + "requires": { + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + } + }, + "@braintree/sanitize-url": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-6.0.4.tgz", + "integrity": "sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A==" + }, + "@codemirror/autocomplete": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.10.2.tgz", + "integrity": "sha512-3dCL7b0j2GdtZzWN5j7HDpRAJ26ip07R4NGYz7QYthIYMiX8I4E4TNrYcdTayPJGeVQtd/xe7lWU4XL7THFb/w==", + "requires": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0" + } + }, + "@codemirror/commands": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.3.0.tgz", + "integrity": "sha512-tFfcxRIlOWiQDFhjBSWJ10MxcvbCIsRr6V64SgrcaY0MwNk32cUOcCuNlWo8VjV4qRQCgNgUAnIeo0svkk4R5Q==", + "requires": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.2.0", + "@codemirror/view": "^6.0.0", + "@lezer/common": "^1.1.0" + } + }, + "@codemirror/lang-css": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.2.1.tgz", + "integrity": "sha512-/UNWDNV5Viwi/1lpr/dIXJNWiwDxpw13I4pTUAsNxZdg6E0mI2kTQb0P2iHczg1Tu+H4EBgJR+hYhKiHKko7qg==", + "requires": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.0.2", + "@lezer/css": "^1.0.0" + } + }, + "@codemirror/lang-html": { + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.6.tgz", + "integrity": "sha512-E4C8CVupBksXvgLSme/zv31x91g06eZHSph7NczVxZW+/K+3XgJGWNT//2WLzaKSBoxpAjaOi5ZnPU1SHhjh3A==", + "requires": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/lang-css": "^6.0.0", + "@codemirror/lang-javascript": "^6.0.0", + "@codemirror/language": "^6.4.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0", + "@lezer/css": "^1.1.0", + "@lezer/html": "^1.3.0" + } + }, + "@codemirror/lang-javascript": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.1.tgz", + "integrity": "sha512-jlFOXTejVyiQCW3EQwvKH0m99bUYIw40oPmFjSX2VS78yzfe0HELZ+NEo9Yfo1MkGRpGlj3Gnu4rdxV1EnAs5A==", + "requires": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.6.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0", + "@lezer/javascript": "^1.0.0" + } + }, + "@codemirror/language": { + "version": "6.9.2", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.9.2.tgz", + "integrity": "sha512-QGTQXSpAKDIzaSE96zNK1UfIUhPgkT1CLjh1N5qVzZuxgsEOhz5RqaN8QCIdyOQklGLx3MgHd9YrE3X3+Pl1ow==", + "requires": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@lezer/common": "^1.1.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0", + "style-mod": "^4.0.0" + } + }, + "@codemirror/lint": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.4.2.tgz", + "integrity": "sha512-wzRkluWb1ptPKdzlsrbwwjYCPLgzU6N88YBAmlZi8WFyuiEduSd05MnJYNogzyc8rPK7pj6m95ptUApc8sHKVA==", + "requires": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "crelt": "^1.0.5" + } + }, + "@codemirror/state": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.3.1.tgz", + "integrity": "sha512-88e4HhMtKJyw6fKprGaN/yZfiaoGYOi2nM45YCUC6R/kex9sxFWBDGatS1vk4lMgnWmdIIB9tk8Gj1LmL8YfvA==" + }, + "@codemirror/view": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.22.0.tgz", + "integrity": "sha512-6zLj4YIoIpfTGKrDMTbeZRpa8ih4EymMCKmddEDcJWrCdp/N1D46B38YEz4creTb4T177AVS9EyXkLeC/HL2jA==", + "requires": { + "@codemirror/state": "^6.1.4", + "style-mod": "^4.1.0", + "w3c-keyname": "^2.2.4" + } + }, + "@codesandbox/nodebox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/@codesandbox/nodebox/-/nodebox-0.1.8.tgz", + "integrity": "sha512-2VRS6JDSk+M+pg56GA6CryyUSGPjBEe8Pnae0QL3jJF1mJZJVMDKr93gJRtBbLkfZN6LD/DwMtf+2L0bpWrjqg==", + "requires": { + "outvariant": "^1.4.0", + "strict-event-emitter": "^0.4.3" + } + }, + "@codesandbox/sandpack-client": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@codesandbox/sandpack-client/-/sandpack-client-2.9.0.tgz", + "integrity": "sha512-KkG/YusBsL0RnI3P079cckHrRleLaGQVM5Plzn6xWOPM04Dce52MMkr6XIU77ZMjZm/zZ5pmgXLW7M6HoyIy/A==", + "requires": { + "@codesandbox/nodebox": "0.1.8", + "buffer": "^6.0.3", + "dequal": "^2.0.2", + "outvariant": "1.4.0", + "static-browser-server": "1.0.3" + } + }, + "@codesandbox/sandpack-react": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@codesandbox/sandpack-react/-/sandpack-react-2.9.0.tgz", + "integrity": "sha512-A0quGugVyWbRktpdq2Nc6W1+XV0QnHq1lbqDCHAs2ijfWxvhhNaqMr6lWDaG/NTUGRNLUNETbipcNaBeC0dxhw==", + "requires": { + "@codemirror/autocomplete": "^6.4.0", + "@codemirror/commands": "^6.1.3", + "@codemirror/lang-css": "^6.0.1", + "@codemirror/lang-html": "^6.4.0", + "@codemirror/lang-javascript": "^6.1.2", + "@codemirror/language": "^6.3.2", + "@codemirror/state": "^6.2.0", + "@codemirror/view": "^6.7.1", + "@codesandbox/sandpack-client": "^2.9.0", + "@lezer/highlight": "^1.1.3", + "@react-hook/intersection-observer": "^3.1.1", + "@stitches/core": "^1.2.6", + "anser": "^2.1.1", + "clean-set": "^1.1.2", + "codesandbox-import-util-types": "^2.2.3", + "dequal": "^2.0.2", + "escape-carriage": "^1.3.1", + "lz-string": "^1.4.4", + "react-devtools-inline": "4.4.0", + "react-is": "^17.0.2" + } + }, + "@codesandbox/sandpack-themes": { + "version": "2.0.21", + "resolved": "https://registry.npmjs.org/@codesandbox/sandpack-themes/-/sandpack-themes-2.0.21.tgz", + "integrity": "sha512-CMH/MO/dh6foPYb/3eSn2Cu/J3+1+/81Fsaj7VggICkCrmRk0qG5dmgjGAearPTnRkOGORIPHuRqwNXgw0E6YQ==" + }, + "@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "optional": true + }, + "@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==" + }, + "@docsearch/css": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.6.0.tgz", + "integrity": "sha512-+sbxb71sWre+PwDK7X2T8+bhS6clcVMLwBPznX45Qu6opJcgRjAp7gYSDzVFp187J+feSj5dNBN1mJoi6ckkUQ==" + }, + "@docsearch/react": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.6.0.tgz", + "integrity": "sha512-HUFut4ztcVNmqy9gp/wxNbC7pTOHhgVVkHVGCACTuLhUKUhKAF9KYHJtMiLUJxEqiFLQiuri1fWF8zqwM/cu1w==", + "requires": { + "@algolia/autocomplete-core": "1.9.3", + "@algolia/autocomplete-preset-algolia": "1.9.3", + "@docsearch/css": "3.6.0", + "algoliasearch": "^4.19.1" + } + }, + "@docusaurus/core": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.4.0.tgz", + "integrity": "sha512-g+0wwmN2UJsBqy2fQRQ6fhXruoEa62JDeEa5d8IdTJlMoaDaEDfHh7WjwGRn4opuTQWpjAwP/fbcgyHKlE+64w==", + "requires": { + "@babel/core": "^7.23.3", + "@babel/generator": "^7.23.3", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-transform-runtime": "^7.22.9", + "@babel/preset-env": "^7.22.9", + "@babel/preset-react": "^7.22.5", + "@babel/preset-typescript": "^7.22.5", + "@babel/runtime": "^7.22.6", + "@babel/runtime-corejs3": "^7.22.6", + "@babel/traverse": "^7.22.8", + "@docusaurus/cssnano-preset": "3.4.0", + "@docusaurus/logger": "3.4.0", + "@docusaurus/mdx-loader": "3.4.0", + "@docusaurus/utils": "3.4.0", + "@docusaurus/utils-common": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", + "autoprefixer": "^10.4.14", + "babel-loader": "^9.1.3", + "babel-plugin-dynamic-import-node": "^2.3.3", + "boxen": "^6.2.1", + "chalk": "^4.1.2", + "chokidar": "^3.5.3", + "clean-css": "^5.3.2", + "cli-table3": "^0.6.3", + "combine-promises": "^1.1.0", + "commander": "^5.1.0", + "copy-webpack-plugin": "^11.0.0", + "core-js": "^3.31.1", + "css-loader": "^6.8.1", + "css-minimizer-webpack-plugin": "^5.0.1", + "cssnano": "^6.1.2", + "del": "^6.1.1", + "detect-port": "^1.5.1", + "escape-html": "^1.0.3", + "eta": "^2.2.0", + "eval": "^0.1.8", + "file-loader": "^6.2.0", + "fs-extra": "^11.1.1", + "html-minifier-terser": "^7.2.0", + "html-tags": "^3.3.1", + "html-webpack-plugin": "^5.5.3", + "leven": "^3.1.0", + "lodash": "^4.17.21", + "mini-css-extract-plugin": "^2.7.6", + "p-map": "^4.0.0", + "postcss": "^8.4.26", + "postcss-loader": "^7.3.3", + "prompts": "^2.4.2", + "react-dev-utils": "^12.0.1", + "react-helmet-async": "^1.3.0", + "react-loadable": "npm:@docusaurus/react-loadable@6.0.0", + "react-loadable-ssr-addon-v5-slorber": "^1.0.1", + "react-router": "^5.3.4", + "react-router-config": "^5.1.1", + "react-router-dom": "^5.3.4", + "rtl-detect": "^1.0.4", + "semver": "^7.5.4", + "serve-handler": "^6.1.5", + "shelljs": "^0.8.5", + "terser-webpack-plugin": "^5.3.9", + "tslib": "^2.6.0", + "update-notifier": "^6.0.2", + "url-loader": "^4.1.1", + "webpack": "^5.88.1", + "webpack-bundle-analyzer": "^4.9.0", + "webpack-dev-server": "^4.15.1", + "webpack-merge": "^5.9.0", + "webpackbar": "^5.0.2" + } + }, + "@docusaurus/cssnano-preset": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-3.4.0.tgz", + "integrity": "sha512-qwLFSz6v/pZHy/UP32IrprmH5ORce86BGtN0eBtG75PpzQJAzp9gefspox+s8IEOr0oZKuQ/nhzZ3xwyc3jYJQ==", + "requires": { + "cssnano-preset-advanced": "^6.1.2", + "postcss": "^8.4.38", + "postcss-sort-media-queries": "^5.2.0", + "tslib": "^2.6.0" + } + }, + "@docusaurus/logger": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-3.4.0.tgz", + "integrity": "sha512-bZwkX+9SJ8lB9kVRkXw+xvHYSMGG4bpYHKGXeXFvyVc79NMeeBSGgzd4TQLHH+DYeOJoCdl8flrFJVxlZ0wo/Q==", + "requires": { + "chalk": "^4.1.2", + "tslib": "^2.6.0" + } + }, + "@docusaurus/mdx-loader": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.4.0.tgz", + "integrity": "sha512-kSSbrrk4nTjf4d+wtBA9H+FGauf2gCax89kV8SUSJu3qaTdSIKdWERlngsiHaCFgZ7laTJ8a67UFf+xlFPtuTw==", + "requires": { + "@docusaurus/logger": "3.4.0", + "@docusaurus/utils": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", + "@mdx-js/mdx": "^3.0.0", + "@slorber/remark-comment": "^1.0.0", + "escape-html": "^1.0.3", + "estree-util-value-to-estree": "^3.0.1", + "file-loader": "^6.2.0", + "fs-extra": "^11.1.1", + "image-size": "^1.0.2", + "mdast-util-mdx": "^3.0.0", + "mdast-util-to-string": "^4.0.0", + "rehype-raw": "^7.0.0", + "remark-directive": "^3.0.0", + "remark-emoji": "^4.0.0", + "remark-frontmatter": "^5.0.0", + "remark-gfm": "^4.0.0", + "stringify-object": "^3.3.0", + "tslib": "^2.6.0", + "unified": "^11.0.3", + "unist-util-visit": "^5.0.0", + "url-loader": "^4.1.1", + "vfile": "^6.0.1", + "webpack": "^5.88.1" + } + }, + "@docusaurus/module-type-aliases": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.4.0.tgz", + "integrity": "sha512-A1AyS8WF5Bkjnb8s+guTDuYmUiwJzNrtchebBHpc0gz0PyHJNMaybUlSrmJjHVcGrya0LKI4YcR3lBDQfXRYLw==", + "requires": { + "@docusaurus/types": "3.4.0", + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router-config": "*", + "@types/react-router-dom": "*", + "react-helmet-async": "*", + "react-loadable": "npm:@docusaurus/react-loadable@6.0.0" + } + }, + "@docusaurus/plugin-content-blog": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.4.0.tgz", + "integrity": "sha512-vv6ZAj78ibR5Jh7XBUT4ndIjmlAxkijM3Sx5MAAzC1gyv0vupDQNhzuFg1USQmQVj3P5I6bquk12etPV3LJ+Xw==", + "requires": { + "@docusaurus/core": "3.4.0", + "@docusaurus/logger": "3.4.0", + "@docusaurus/mdx-loader": "3.4.0", + "@docusaurus/types": "3.4.0", + "@docusaurus/utils": "3.4.0", + "@docusaurus/utils-common": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", + "cheerio": "^1.0.0-rc.12", + "feed": "^4.2.2", + "fs-extra": "^11.1.1", + "lodash": "^4.17.21", + "reading-time": "^1.5.0", + "srcset": "^4.0.0", + "tslib": "^2.6.0", + "unist-util-visit": "^5.0.0", + "utility-types": "^3.10.0", + "webpack": "^5.88.1" + } + }, + "@docusaurus/plugin-content-docs": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.4.0.tgz", + "integrity": "sha512-HkUCZffhBo7ocYheD9oZvMcDloRnGhBMOZRyVcAQRFmZPmNqSyISlXA1tQCIxW+r478fty97XXAGjNYzBjpCsg==", + "requires": { + "@docusaurus/core": "3.4.0", + "@docusaurus/logger": "3.4.0", + "@docusaurus/mdx-loader": "3.4.0", + "@docusaurus/module-type-aliases": "3.4.0", + "@docusaurus/types": "3.4.0", + "@docusaurus/utils": "3.4.0", + "@docusaurus/utils-common": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", + "@types/react-router-config": "^5.0.7", + "combine-promises": "^1.1.0", + "fs-extra": "^11.1.1", + "js-yaml": "^4.1.0", + "lodash": "^4.17.21", + "tslib": "^2.6.0", + "utility-types": "^3.10.0", + "webpack": "^5.88.1" + } + }, + "@docusaurus/plugin-content-pages": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.4.0.tgz", + "integrity": "sha512-h2+VN/0JjpR8fIkDEAoadNjfR3oLzB+v1qSXbIAKjQ46JAHx3X22n9nqS+BWSQnTnp1AjkjSvZyJMekmcwxzxg==", + "requires": { + "@docusaurus/core": "3.4.0", + "@docusaurus/mdx-loader": "3.4.0", + "@docusaurus/types": "3.4.0", + "@docusaurus/utils": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", + "fs-extra": "^11.1.1", + "tslib": "^2.6.0", + "webpack": "^5.88.1" + } + }, + "@docusaurus/plugin-debug": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-3.4.0.tgz", + "integrity": "sha512-uV7FDUNXGyDSD3PwUaf5YijX91T5/H9SX4ErEcshzwgzWwBtK37nUWPU3ZLJfeTavX3fycTOqk9TglpOLaWkCg==", + "requires": { + "@docusaurus/core": "3.4.0", + "@docusaurus/types": "3.4.0", + "@docusaurus/utils": "3.4.0", + "fs-extra": "^11.1.1", + "react-json-view-lite": "^1.2.0", + "tslib": "^2.6.0" + } + }, + "@docusaurus/plugin-google-analytics": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.4.0.tgz", + "integrity": "sha512-mCArluxEGi3cmYHqsgpGGt3IyLCrFBxPsxNZ56Mpur0xSlInnIHoeLDH7FvVVcPJRPSQ9/MfRqLsainRw+BojA==", + "requires": { + "@docusaurus/core": "3.4.0", + "@docusaurus/types": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", + "tslib": "^2.6.0" + } + }, + "@docusaurus/plugin-google-gtag": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.4.0.tgz", + "integrity": "sha512-Dsgg6PLAqzZw5wZ4QjUYc8Z2KqJqXxHxq3vIoyoBWiLEEfigIs7wHR+oiWUQy3Zk9MIk6JTYj7tMoQU0Jm3nqA==", + "requires": { + "@docusaurus/core": "3.4.0", + "@docusaurus/types": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", + "@types/gtag.js": "^0.0.12", + "tslib": "^2.6.0" + } + }, + "@docusaurus/plugin-google-tag-manager": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.4.0.tgz", + "integrity": "sha512-O9tX1BTwxIhgXpOLpFDueYA9DWk69WCbDRrjYoMQtFHSkTyE7RhNgyjSPREUWJb9i+YUg3OrsvrBYRl64FCPCQ==", + "requires": { + "@docusaurus/core": "3.4.0", + "@docusaurus/types": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", + "tslib": "^2.6.0" + } + }, + "@docusaurus/plugin-sitemap": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.4.0.tgz", + "integrity": "sha512-+0VDvx9SmNrFNgwPoeoCha+tRoAjopwT0+pYO1xAbyLcewXSemq+eLxEa46Q1/aoOaJQ0qqHELuQM7iS2gp33Q==", + "requires": { + "@docusaurus/core": "3.4.0", + "@docusaurus/logger": "3.4.0", + "@docusaurus/types": "3.4.0", + "@docusaurus/utils": "3.4.0", + "@docusaurus/utils-common": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", + "fs-extra": "^11.1.1", + "sitemap": "^7.1.1", + "tslib": "^2.6.0" + } + }, + "@docusaurus/preset-classic": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-3.4.0.tgz", + "integrity": "sha512-Ohj6KB7siKqZaQhNJVMBBUzT3Nnp6eTKqO+FXO3qu/n1hJl3YLwVKTWBg28LF7MWrKu46UuYavwMRxud0VyqHg==", + "requires": { + "@docusaurus/core": "3.4.0", + "@docusaurus/plugin-content-blog": "3.4.0", + "@docusaurus/plugin-content-docs": "3.4.0", + "@docusaurus/plugin-content-pages": "3.4.0", + "@docusaurus/plugin-debug": "3.4.0", + "@docusaurus/plugin-google-analytics": "3.4.0", + "@docusaurus/plugin-google-gtag": "3.4.0", + "@docusaurus/plugin-google-tag-manager": "3.4.0", + "@docusaurus/plugin-sitemap": "3.4.0", + "@docusaurus/theme-classic": "3.4.0", + "@docusaurus/theme-common": "3.4.0", + "@docusaurus/theme-search-algolia": "3.4.0", + "@docusaurus/types": "3.4.0" + } + }, + "@docusaurus/theme-classic": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-3.4.0.tgz", + "integrity": "sha512-0IPtmxsBYv2adr1GnZRdMkEQt1YW6tpzrUPj02YxNpvJ5+ju4E13J5tB4nfdaen/tfR1hmpSPlTFPvTf4kwy8Q==", + "requires": { + "@docusaurus/core": "3.4.0", + "@docusaurus/mdx-loader": "3.4.0", + "@docusaurus/module-type-aliases": "3.4.0", + "@docusaurus/plugin-content-blog": "3.4.0", + "@docusaurus/plugin-content-docs": "3.4.0", + "@docusaurus/plugin-content-pages": "3.4.0", + "@docusaurus/theme-common": "3.4.0", + "@docusaurus/theme-translations": "3.4.0", + "@docusaurus/types": "3.4.0", + "@docusaurus/utils": "3.4.0", + "@docusaurus/utils-common": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", + "@mdx-js/react": "^3.0.0", + "clsx": "^2.0.0", + "copy-text-to-clipboard": "^3.2.0", + "infima": "0.2.0-alpha.43", + "lodash": "^4.17.21", + "nprogress": "^0.2.0", + "postcss": "^8.4.26", + "prism-react-renderer": "^2.3.0", + "prismjs": "^1.29.0", + "react-router-dom": "^5.3.4", + "rtlcss": "^4.1.0", + "tslib": "^2.6.0", + "utility-types": "^3.10.0" + } + }, + "@docusaurus/theme-common": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.4.0.tgz", + "integrity": "sha512-0A27alXuv7ZdCg28oPE8nH/Iz73/IUejVaCazqu9elS4ypjiLhK3KfzdSQBnL/g7YfHSlymZKdiOHEo8fJ0qMA==", + "requires": { + "@docusaurus/mdx-loader": "3.4.0", + "@docusaurus/module-type-aliases": "3.4.0", + "@docusaurus/plugin-content-blog": "3.4.0", + "@docusaurus/plugin-content-docs": "3.4.0", + "@docusaurus/plugin-content-pages": "3.4.0", + "@docusaurus/utils": "3.4.0", + "@docusaurus/utils-common": "3.4.0", + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router-config": "*", + "clsx": "^2.0.0", + "parse-numeric-range": "^1.3.0", + "prism-react-renderer": "^2.3.0", + "tslib": "^2.6.0", + "utility-types": "^3.10.0" + } + }, + "@docusaurus/theme-mermaid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-mermaid/-/theme-mermaid-3.4.0.tgz", + "integrity": "sha512-3w5QW0HEZ2O6x2w6lU3ZvOe1gNXP2HIoKDMJBil1VmLBc9PmpAG17VmfhI/p3L2etNmOiVs5GgniUqvn8AFEGQ==", + "requires": { + "@docusaurus/core": "3.4.0", + "@docusaurus/module-type-aliases": "3.4.0", + "@docusaurus/theme-common": "3.4.0", + "@docusaurus/types": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", + "mermaid": "^10.4.0", + "tslib": "^2.6.0" + } + }, + "@docusaurus/theme-search-algolia": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.4.0.tgz", + "integrity": "sha512-aiHFx7OCw4Wck1z6IoShVdUWIjntC8FHCw9c5dR8r3q4Ynh+zkS8y2eFFunN/DL6RXPzpnvKCg3vhLQYJDmT9Q==", + "requires": { + "@docsearch/react": "^3.5.2", + "@docusaurus/core": "3.4.0", + "@docusaurus/logger": "3.4.0", + "@docusaurus/plugin-content-docs": "3.4.0", + "@docusaurus/theme-common": "3.4.0", + "@docusaurus/theme-translations": "3.4.0", + "@docusaurus/utils": "3.4.0", + "@docusaurus/utils-validation": "3.4.0", + "algoliasearch": "^4.18.0", + "algoliasearch-helper": "^3.13.3", + "clsx": "^2.0.0", + "eta": "^2.2.0", + "fs-extra": "^11.1.1", + "lodash": "^4.17.21", + "tslib": "^2.6.0", + "utility-types": "^3.10.0" + } + }, + "@docusaurus/theme-translations": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.4.0.tgz", + "integrity": "sha512-zSxCSpmQCCdQU5Q4CnX/ID8CSUUI3fvmq4hU/GNP/XoAWtXo9SAVnM3TzpU8Gb//H3WCsT8mJcTfyOk3d9ftNg==", + "requires": { + "fs-extra": "^11.1.1", + "tslib": "^2.6.0" + } + }, + "@docusaurus/tsconfig": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/tsconfig/-/tsconfig-3.4.0.tgz", + "integrity": "sha512-0qENiJ+TRaeTzcg4olrnh0BQ7eCxTgbYWBnWUeQDc84UYkt/T3pDNnm3SiQkqPb+YQ1qtYFlC0RriAElclo8Dg==" + }, + "@docusaurus/types": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.4.0.tgz", + "integrity": "sha512-4jcDO8kXi5Cf9TcyikB/yKmz14f2RZ2qTRerbHAsS+5InE9ZgSLBNLsewtFTcTOXSVcbU3FoGOzcNWAmU1TR0A==", + "requires": { + "@mdx-js/mdx": "^3.0.0", + "@types/history": "^4.7.11", + "@types/react": "*", + "commander": "^5.1.0", + "joi": "^17.9.2", + "react-helmet-async": "^1.3.0", + "utility-types": "^3.10.0", + "webpack": "^5.88.1", + "webpack-merge": "^5.9.0" + } + }, + "@docusaurus/utils": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.4.0.tgz", + "integrity": "sha512-fRwnu3L3nnWaXOgs88BVBmG1yGjcQqZNHG+vInhEa2Sz2oQB+ZjbEMO5Rh9ePFpZ0YDiDUhpaVjwmS+AU2F14g==", + "requires": { + "@docusaurus/logger": "3.4.0", + "@docusaurus/utils-common": "3.4.0", + "@svgr/webpack": "^8.1.0", + "escape-string-regexp": "^4.0.0", + "file-loader": "^6.2.0", + "fs-extra": "^11.1.1", + "github-slugger": "^1.5.0", + "globby": "^11.1.0", + "gray-matter": "^4.0.3", + "jiti": "^1.20.0", + "js-yaml": "^4.1.0", + "lodash": "^4.17.21", + "micromatch": "^4.0.5", + "prompts": "^2.4.2", + "resolve-pathname": "^3.0.0", + "shelljs": "^0.8.5", + "tslib": "^2.6.0", + "url-loader": "^4.1.1", + "utility-types": "^3.10.0", + "webpack": "^5.88.1" + } + }, + "@docusaurus/utils-common": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.4.0.tgz", + "integrity": "sha512-NVx54Wr4rCEKsjOH5QEVvxIqVvm+9kh7q8aYTU5WzUU9/Hctd6aTrcZ3G0Id4zYJ+AeaG5K5qHA4CY5Kcm2iyQ==", + "requires": { + "tslib": "^2.6.0" + } + }, + "@docusaurus/utils-validation": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.4.0.tgz", + "integrity": "sha512-hYQ9fM+AXYVTWxJOT1EuNaRnrR2WGpRdLDQG07O8UOpsvCPWUVOeo26Rbm0JWY2sGLfzAb+tvJ62yF+8F+TV0g==", + "requires": { + "@docusaurus/logger": "3.4.0", + "@docusaurus/utils": "3.4.0", + "@docusaurus/utils-common": "3.4.0", + "fs-extra": "^11.2.0", + "joi": "^17.9.2", + "js-yaml": "^4.1.0", + "lodash": "^4.17.21", + "tslib": "^2.6.0" + } + }, + "@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" + }, + "@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "requires": { + "@hapi/hoek": "^9.0.0" + } + }, + "@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "requires": { + "@sinclair/typebox": "^0.27.8" + } + }, + "@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "requires": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + } + }, + "@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==" + }, + "@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==" + }, + "@jridgewell/source-map": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", + "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", + "requires": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + }, + "@jridgewell/trace-mapping": { + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", + "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", + "requires": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "@leichtgewicht/ip-codec": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", + "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" + }, + "@lezer/common": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.1.0.tgz", + "integrity": "sha512-XPIN3cYDXsoJI/oDWoR2tD++juVrhgIago9xyKhZ7IhGlzdDM9QgC8D8saKNCz5pindGcznFr2HBSsEQSWnSjw==" + }, + "@lezer/css": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.1.3.tgz", + "integrity": "sha512-SjSM4pkQnQdJDVc80LYzEaMiNy9txsFbI7HsMgeVF28NdLaAdHNtQ+kB/QqDUzRBV/75NTXjJ/R5IdC8QQGxMg==", + "requires": { + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "@lezer/highlight": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.1.6.tgz", + "integrity": "sha512-cmSJYa2us+r3SePpRCjN5ymCqCPv+zyXmDl0ciWtVaNiORT/MxM7ZgOMQZADD0o51qOaOg24qc/zBViOIwAjJg==", + "requires": { + "@lezer/common": "^1.0.0" + } + }, + "@lezer/html": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.6.tgz", + "integrity": "sha512-Kk9HJARZTc0bAnMQUqbtuhFVsB4AnteR2BFUWfZV7L/x1H0aAKz6YabrfJ2gk/BEgjh9L3hg5O4y2IDZRBdzuQ==", + "requires": { + "@lezer/common": "^1.0.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "@lezer/javascript": { + "version": "1.4.9", + "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.4.9.tgz", + "integrity": "sha512-7Uv8mBBE6l44spgWEZvEMdDqGV+FIuY7kJ1o5TFm+jxIuxydO3PcKJYiINij09igd1D/9P7l2KDqpkN8c3bM6A==", + "requires": { + "@lezer/highlight": "^1.1.3", + "@lezer/lr": "^1.3.0" + } + }, + "@lezer/lr": { + "version": "1.3.14", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.3.14.tgz", + "integrity": "sha512-z5mY4LStlA3yL7aHT/rqgG614cfcvklS+8oFRFBYrs4YaWLJyKKM4+nN6KopToX0o9Hj6zmH6M5kinOYuy06ug==", + "requires": { + "@lezer/common": "^1.0.0" + } + }, + "@mdx-js/mdx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-3.0.1.tgz", + "integrity": "sha512-eIQ4QTrOWyL3LWEe/bu6Taqzq2HQvHcyTMaOrI95P2/LmJE7AsfPfgJGuFLPVqBUE1BC1rik3VIhU+s9u72arA==", + "requires": { + "@types/estree": "^1.0.0", + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdx": "^2.0.0", + "collapse-white-space": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-build-jsx": "^3.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "estree-util-to-js": "^2.0.0", + "estree-walker": "^3.0.0", + "hast-util-to-estree": "^3.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "markdown-extensions": "^2.0.0", + "periscopic": "^3.0.0", + "remark-mdx": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "source-map": "^0.7.0", + "unified": "^11.0.0", + "unist-util-position-from-estree": "^2.0.0", + "unist-util-stringify-position": "^4.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + } + }, + "@mdx-js/react": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.0.0.tgz", + "integrity": "sha512-nDctevR9KyYFyV+m+/+S4cpzCWHqj+iHDHq3QrsWezcC+B17uZdIWgCguESUkwFhM3n/56KxWVE3V6EokrmONQ==", + "requires": { + "@types/mdx": "^2.0.0" + } + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@open-draft/deferred-promise": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz", + "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==" + }, + "@pnpm/config.env-replace": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", + "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==" + }, + "@pnpm/network.ca-file": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz", + "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==", + "requires": { + "graceful-fs": "4.2.10" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" + } + } + }, + "@pnpm/npm-conf": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-2.2.2.tgz", + "integrity": "sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==", + "requires": { + "@pnpm/config.env-replace": "^1.1.0", + "@pnpm/network.ca-file": "^1.0.1", + "config-chain": "^1.1.11" + } + }, + "@polka/url": { + "version": "1.0.0-next.23", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.23.tgz", + "integrity": "sha512-C16M+IYz0rgRhWZdCmK+h58JMv8vijAA61gmz2rspCSwKwzBebpdcsiUmwrtJRdphuY30i6BSLEOP8ppbNLyLg==" + }, + "@react-hook/intersection-observer": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@react-hook/intersection-observer/-/intersection-observer-3.1.1.tgz", + "integrity": "sha512-OTDx8/wFaRvzFtKl1dEUEXSOqK2zVJHporiTTdC2xO++0e9FEx9wIrPis5q3lqtXeZH9zYGLbk+aB75qNFbbuw==", + "requires": { + "@react-hook/passive-layout-effect": "^1.2.0", + "intersection-observer": "^0.10.0" + } + }, + "@react-hook/passive-layout-effect": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@react-hook/passive-layout-effect/-/passive-layout-effect-1.2.1.tgz", + "integrity": "sha512-IwEphTD75liO8g+6taS+4oqz+nnroocNfWVHWz7j+N+ZO2vYrc6PV1q7GQhuahL0IOR7JccFTsFKQ/mb6iZWAg==", + "requires": {} + }, + "@sideway/address": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz", + "integrity": "sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==", + "requires": { + "@hapi/hoek": "^9.0.0" + } + }, + "@sideway/formula": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==" + }, + "@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" + }, + "@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==" + }, + "@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==" + }, + "@slorber/remark-comment": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@slorber/remark-comment/-/remark-comment-1.0.0.tgz", + "integrity": "sha512-RCE24n7jsOj1M0UPvIQCHTe7fI0sFL4S2nwKVWwHyVr/wI/H8GosgsJGyhnsZoGFnD/P2hLf1mSbrrgSLN93NA==", + "requires": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.1.0", + "micromark-util-symbol": "^1.0.1" + } + }, + "@stitches/core": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@stitches/core/-/core-1.2.8.tgz", + "integrity": "sha512-Gfkvwk9o9kE9r9XNBmJRfV8zONvXThnm1tcuojL04Uy5uRyqg93DC83lDebl0rocZCfKSjUv+fWYtMQmEDJldg==" + }, + "@svgr/babel-plugin-add-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==", + "requires": {} + }, + "@svgr/babel-plugin-remove-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==", + "requires": {} + }, + "@svgr/babel-plugin-remove-jsx-empty-expression": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz", + "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==", + "requires": {} + }, + "@svgr/babel-plugin-replace-jsx-attribute-value": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz", + "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==", + "requires": {} + }, + "@svgr/babel-plugin-svg-dynamic-title": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz", + "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==", + "requires": {} + }, + "@svgr/babel-plugin-svg-em-dimensions": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz", + "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==", + "requires": {} + }, + "@svgr/babel-plugin-transform-react-native-svg": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz", + "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==", + "requires": {} + }, + "@svgr/babel-plugin-transform-svg-component": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz", + "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==", + "requires": {} + }, + "@svgr/babel-preset": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz", + "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==", + "requires": { + "@svgr/babel-plugin-add-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0", + "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0", + "@svgr/babel-plugin-svg-dynamic-title": "8.0.0", + "@svgr/babel-plugin-svg-em-dimensions": "8.0.0", + "@svgr/babel-plugin-transform-react-native-svg": "8.1.0", + "@svgr/babel-plugin-transform-svg-component": "8.0.0" + } + }, + "@svgr/core": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", + "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", + "requires": { + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "camelcase": "^6.2.0", + "cosmiconfig": "^8.1.3", + "snake-case": "^3.0.4" + } + }, + "@svgr/hast-util-to-babel-ast": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz", + "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==", + "requires": { + "@babel/types": "^7.21.3", + "entities": "^4.4.0" + } + }, + "@svgr/plugin-jsx": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz", + "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==", + "requires": { + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "@svgr/hast-util-to-babel-ast": "8.0.0", + "svg-parser": "^2.0.4" + } + }, + "@svgr/plugin-svgo": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-8.1.0.tgz", + "integrity": "sha512-Ywtl837OGO9pTLIN/onoWLmDQ4zFUycI1g76vuKGEz6evR/ZTJlJuz3G/fIkb6OVBJ2g0o6CGJzaEjfmEo3AHA==", + "requires": { + "cosmiconfig": "^8.1.3", + "deepmerge": "^4.3.1", + "svgo": "^3.0.2" + } + }, + "@svgr/webpack": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-8.1.0.tgz", + "integrity": "sha512-LnhVjMWyMQV9ZmeEy26maJk+8HTIbd59cH4F2MJ439k9DqejRisfFNGAPvRYlKETuh9LrImlS8aKsBgKjMA8WA==", + "requires": { + "@babel/core": "^7.21.3", + "@babel/plugin-transform-react-constant-elements": "^7.21.3", + "@babel/preset-env": "^7.20.2", + "@babel/preset-react": "^7.18.6", + "@babel/preset-typescript": "^7.21.0", + "@svgr/core": "8.1.0", + "@svgr/plugin-jsx": "8.1.0", + "@svgr/plugin-svgo": "8.1.0" + } + }, + "@szmarczak/http-timer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", + "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", + "requires": { + "defer-to-connect": "^2.0.1" + } + }, + "@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==" + }, + "@types/acorn": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@types/acorn/-/acorn-4.0.6.tgz", + "integrity": "sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ==", + "requires": { + "@types/estree": "*" + } + }, + "@types/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@types/bonjour": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz", + "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", + "requires": { + "@types/node": "*" + } + }, + "@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "requires": { + "@types/node": "*" + } + }, + "@types/connect-history-api-fallback": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.3.tgz", + "integrity": "sha512-6mfQ6iNvhSKCZJoY6sIG3m0pKkdUcweVNOLuBBKvoWGzl2yRxOJcYOTRyLKt3nxXvBLJWa6QkW//tgbIwJehmA==", + "requires": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "@types/d3-scale": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz", + "integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==", + "requires": { + "@types/d3-time": "*" + } + }, + "@types/d3-scale-chromatic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.0.3.tgz", + "integrity": "sha512-laXM4+1o5ImZv3RpFAsTRn3TEkzqkytiOY0Dz0sq5cnd1dtNlk6sHLon4OvqaiJb28T0S/TdsBI3Sjsy+keJrw==" + }, + "@types/d3-time": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.3.tgz", + "integrity": "sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==" + }, + "@types/debug": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.11.tgz", + "integrity": "sha512-R2qflTjHDs4CL6D/6TkqBeIHr54WzZfIxN729xvCNlYIVp2LknlnCro5Yo3frNaX2E5gO9pZ3/QAPVdGmu+q9w==", + "requires": { + "@types/ms": "*" + } + }, + "@types/eslint": { + "version": "8.44.7", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.7.tgz", + "integrity": "sha512-f5ORu2hcBbKei97U73mf+l9t4zTGl74IqZ0GQk4oVea/VS8tQZYkUveSYojk+frraAVYId0V2WC9O4PTNru2FQ==", + "requires": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "requires": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" + }, + "@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "requires": { + "@types/estree": "*" + } + }, + "@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.17.41", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.41.tgz", + "integrity": "sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==", + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "@types/gtag.js": { + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/@types/gtag.js/-/gtag.js-0.0.12.tgz", + "integrity": "sha512-YQV9bUsemkzG81Ea295/nF/5GijnD2Af7QhEofh7xu+kvCN6RdodgNwwGWXB5GMI3NoyvQo0odNctoH/qLMIpg==" + }, + "@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "requires": { + "@types/unist": "*" + } + }, + "@types/history": { + "version": "4.7.11", + "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.11.tgz", + "integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==" + }, + "@types/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==" + }, + "@types/http-cache-semantics": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==" + }, + "@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==" + }, + "@types/http-proxy": { + "version": "1.17.14", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.14.tgz", + "integrity": "sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w==", + "requires": { + "@types/node": "*" + } + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==" + }, + "@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" + }, + "@types/mdast": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.3.tgz", + "integrity": "sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==", + "requires": { + "@types/unist": "*" + } + }, + "@types/mdx": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.10.tgz", + "integrity": "sha512-Rllzc5KHk0Al5/WANwgSPl1/CwjqCy+AZrGd78zuK+jO9aDM6ffblZ+zIjgPNAaEBmlO0RYDvLNh7wD0zKVgEg==" + }, + "@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==" + }, + "@types/ms": { + "version": "0.7.34", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", + "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==" + }, + "@types/node": { + "version": "20.9.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.0.tgz", + "integrity": "sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw==", + "requires": { + "undici-types": "~5.26.4" + } + }, + "@types/node-forge": { + "version": "1.3.9", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.9.tgz", + "integrity": "sha512-meK88cx/sTalPSLSoCzkiUB4VPIFHmxtXm5FaaqRDqBX2i/Sy8bJ4odsan0b20RBjPh06dAQ+OTTdnyQyhJZyQ==", + "requires": { + "@types/node": "*" + } + }, + "@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==" + }, + "@types/prismjs": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.3.tgz", + "integrity": "sha512-A0D0aTXvjlqJ5ZILMz3rNfDBOx9hHxLZYv2by47Sm/pqW35zzjusrZTryatjN/Rf8Us2gZrJD+KeHbUSTux1Cw==" + }, + "@types/prop-types": { + "version": "15.7.10", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.10.tgz", + "integrity": "sha512-mxSnDQxPqsZxmeShFH+uwQ4kO4gcJcGahjjMFeLbKE95IAZiiZyiEepGZjtXJ7hN/yfu0bu9xN2ajcU0JcxX6A==" + }, + "@types/qs": { + "version": "6.9.10", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.10.tgz", + "integrity": "sha512-3Gnx08Ns1sEoCrWssEgTSJs/rsT2vhGP+Ja9cnnk9k4ALxinORlQneLXFeFKOTJMOeZUFD1s7w+w2AphTpvzZw==" + }, + "@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==" + }, + "@types/react": { + "version": "18.2.37", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.37.tgz", + "integrity": "sha512-RGAYMi2bhRgEXT3f4B92WTohopH6bIXw05FuGlmJEnv/omEn190+QYEIYxIAuIBdKgboYYdVved2p1AxZVQnaw==", + "requires": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "@types/react-router": { + "version": "5.1.20", + "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.20.tgz", + "integrity": "sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==", + "requires": { + "@types/history": "^4.7.11", + "@types/react": "*" + } + }, + "@types/react-router-config": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/@types/react-router-config/-/react-router-config-5.0.10.tgz", + "integrity": "sha512-Wn6c/tXdEgi9adCMtDwx8Q2vGty6TsPTc/wCQQ9kAlye8UqFxj0vGFWWuhywNfkwqth+SOgJxQTLTZukrqDQmQ==", + "requires": { + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router": "^5.1.0" + } + }, + "@types/react-router-dom": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.3.3.tgz", + "integrity": "sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==", + "requires": { + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router": "*" + } + }, + "@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" + }, + "@types/sax": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz", + "integrity": "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==", + "requires": { + "@types/node": "*" + } + }, + "@types/scheduler": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.6.tgz", + "integrity": "sha512-Vlktnchmkylvc9SnwwwozTv04L/e1NykF5vgoQ0XTmI8DD+wxfjQuHuvHS3p0r2jz2x2ghPs2h1FVeDirIteWA==" + }, + "@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "requires": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "@types/serve-index": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz", + "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", + "requires": { + "@types/express": "*" + } + }, + "@types/serve-static": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz", + "integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==", + "requires": { + "@types/http-errors": "*", + "@types/mime": "*", + "@types/node": "*" + } + }, + "@types/sockjs": { + "version": "0.3.36", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", + "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", + "requires": { + "@types/node": "*" + } + }, + "@types/unist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", + "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==" + }, + "@types/ws": { + "version": "8.5.9", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.9.tgz", + "integrity": "sha512-jbdrY0a8lxfdTp/+r7Z4CkycbOFN8WX+IOchLJr3juT/xzbJ8URyTVSJ/hvNdadTgM1mnedb47n+Y31GsFnQlg==", + "requires": { + "@types/node": "*" + } + }, + "@types/yargs": { + "version": "17.0.32", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", + "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==" + }, + "@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==" + }, + "@webassemblyjs/ast": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", + "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", + "requires": { + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==" + }, + "@webassemblyjs/helper-api-error": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==" + }, + "@webassemblyjs/helper-buffer": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", + "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==" + }, + "@webassemblyjs/helper-numbers": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", + "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", + "requires": { + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==" + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", + "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", + "requires": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==" + }, + "@webassemblyjs/wasm-edit": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", + "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", + "requires": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-opt": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6", + "@webassemblyjs/wast-printer": "1.11.6" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", + "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", + "requires": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", + "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", + "requires": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", + "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", + "requires": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", + "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", + "requires": { + "@webassemblyjs/ast": "1.11.6", + "@xtuc/long": "4.2.2" + } + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" + }, + "accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "requires": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "dependencies": { + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + } + } + }, + "acorn": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", + "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==" + }, + "acorn-import-assertions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", + "requires": {} + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "requires": {} + }, + "acorn-walk": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.0.tgz", + "integrity": "sha512-FS7hV565M5l1R08MXqo8odwMTB02C2UqzB17RVgu9EyuYFBqJZ3/ZY97sQD5FewVu1UyDFc1yztUDrAwT0EypA==" + }, + "address": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/address/-/address-1.2.2.tgz", + "integrity": "sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==" + }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "requires": { + "ajv": "^8.0.0" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "algoliasearch": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.23.3.tgz", + "integrity": "sha512-Le/3YgNvjW9zxIQMRhUHuhiUjAlKY/zsdZpfq4dlLqg6mEm0nL6yk+7f2hDOtLpxsgE4jSzDmvHL7nXdBp5feg==", + "requires": { + "@algolia/cache-browser-local-storage": "4.23.3", + "@algolia/cache-common": "4.23.3", + "@algolia/cache-in-memory": "4.23.3", + "@algolia/client-account": "4.23.3", + "@algolia/client-analytics": "4.23.3", + "@algolia/client-common": "4.23.3", + "@algolia/client-personalization": "4.23.3", + "@algolia/client-search": "4.23.3", + "@algolia/logger-common": "4.23.3", + "@algolia/logger-console": "4.23.3", + "@algolia/recommend": "4.23.3", + "@algolia/requester-browser-xhr": "4.23.3", + "@algolia/requester-common": "4.23.3", + "@algolia/requester-node-http": "4.23.3", + "@algolia/transporter": "4.23.3" + } + }, + "algoliasearch-helper": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.21.0.tgz", + "integrity": "sha512-hjVOrL15I3Y3K8xG0icwG1/tWE+MocqBrhW6uVBWpU+/kVEMK0BnM2xdssj6mZM61eJ4iRxHR0djEI3ENOpR8w==", + "requires": { + "@algolia/events": "^4.0.1" + } + }, + "anser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/anser/-/anser-2.1.1.tgz", + "integrity": "sha512-nqLm4HxOTpeLOxcmB3QWmV5TcDFhW9y/fyQ+hivtDFcK4OQ+pQ5fzPnXHM1Mfcm0VkLtvVi1TCPr++Qy0Q/3EQ==" + }, + "ansi-align": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", + "requires": { + "string-width": "^4.1.0" + }, + "dependencies": { + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + } + } + }, + "ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==" + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==" + }, + "astring": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/astring/-/astring-1.8.6.tgz", + "integrity": "sha512-ISvCdHdlTDlH5IpxQJIex7BWBywFWgjJSVdwst+/iQCoEYnyOaQ95+X1JGshuBjGp6nxKUy1jMgE3zPqN7fQdg==" + }, + "at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" + }, + "autoprefixer": { + "version": "10.4.19", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz", + "integrity": "sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==", + "requires": { + "browserslist": "^4.23.0", + "caniuse-lite": "^1.0.30001599", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + } + }, + "babel-loader": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.1.3.tgz", + "integrity": "sha512-xG3ST4DglodGf8qSwv0MdeWLhrDsw/32QMdTO5T1ZIp9gQur0HkCyFs7Awskr10JKXFXwpAhiCuYX5oGXnRGbw==", + "requires": { + "find-cache-dir": "^4.0.0", + "schema-utils": "^4.0.0" + } + }, + "babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "requires": { + "object.assign": "^4.1.0" + } + }, + "babel-plugin-polyfill-corejs2": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.6.tgz", + "integrity": "sha512-jhHiWVZIlnPbEUKSSNb9YoWcQGdlTLq7z1GHL4AjFxaoOUMuuEVJ+Y4pAaQUGOGk93YsVCKPbqbfw3m0SM6H8Q==", + "requires": { + "@babel/compat-data": "^7.22.6", + "@babel/helper-define-polyfill-provider": "^0.4.3", + "semver": "^6.3.1" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } + } + }, + "babel-plugin-polyfill-corejs3": { + "version": "0.8.6", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.6.tgz", + "integrity": "sha512-leDIc4l4tUgU7str5BWLS2h8q2N4Nf6lGZP6UrNDxdtfF2g69eJ5L0H7S8A5Ln/arfFAfHor5InAdZuIOwZdgQ==", + "requires": { + "@babel/helper-define-polyfill-provider": "^0.4.3", + "core-js-compat": "^3.33.1" + } + }, + "babel-plugin-polyfill-regenerator": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.3.tgz", + "integrity": "sha512-8sHeDOmXC8csczMrYEOf0UTNa4yE2SxV5JGeT/LP1n0OYVDUUFPxG9vdk2AlDlIit4t+Kf0xCtpgXPBwnn/9pw==", + "requires": { + "@babel/helper-define-polyfill-provider": "^0.4.3" + } + }, + "bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==" + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==" + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==" + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" + }, + "body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "requires": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "dependencies": { + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } + } + }, + "bonjour-service": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.1.1.tgz", + "integrity": "sha512-Z/5lQRMOG9k7W+FkeGTNjh7htqn/2LMnfOvBZ8pynNZCM9MwkQkI3zeI4oz09uWdcgmgHugVvBqxGg4VQJ5PCg==", + "requires": { + "array-flatten": "^2.1.2", + "dns-equal": "^1.0.0", + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" + } + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" + }, + "boxen": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-6.2.1.tgz", + "integrity": "sha512-H4PEsJXfFI/Pt8sjDWbHlQPx4zL/bvSQjcilJmaulGt5mLDorHOHpmdXAJcBcmru7PhYSp/cDMWRko4ZUMFkSw==", + "requires": { + "ansi-align": "^3.0.1", + "camelcase": "^6.2.0", + "chalk": "^4.1.2", + "cli-boxes": "^3.0.0", + "string-width": "^5.0.1", + "type-fest": "^2.5.0", + "widest-line": "^4.0.1", + "wrap-ansi": "^8.0.1" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "requires": { + "fill-range": "^7.0.1" + } + }, + "browserslist": { + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", + "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "requires": { + "caniuse-lite": "^1.0.30001587", + "electron-to-chromium": "^1.4.668", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" + } + }, + "buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==" + }, + "cacheable-lookup": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", + "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==" + }, + "cacheable-request": { + "version": "10.2.14", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz", + "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==", + "requires": { + "@types/http-cache-semantics": "^4.0.2", + "get-stream": "^6.0.1", + "http-cache-semantics": "^4.1.1", + "keyv": "^4.5.3", + "mimic-response": "^4.0.0", + "normalize-url": "^8.0.0", + "responselike": "^3.0.0" + }, + "dependencies": { + "normalize-url": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.0.tgz", + "integrity": "sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw==" + } + } + }, + "call-bind": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", + "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", + "requires": { + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.1", + "set-function-length": "^1.1.1" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" + }, + "camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "requires": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==" + }, + "caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "requires": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "caniuse-lite": { + "version": "1.0.30001632", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001632.tgz", + "integrity": "sha512-udx3o7yHJfUxMLkGohMlVHCvFvWmirKh9JAH/d7WOLPetlH+LTL5cocMZ0t7oZx/mdlOWXti97xLZWc8uURRHg==" + }, + "ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==" + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==" + }, + "character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==" + }, + "character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==" + }, + "character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==" + }, + "character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==" + }, + "cheerio": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", + "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", + "requires": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "htmlparser2": "^8.0.1", + "parse5": "^7.0.0", + "parse5-htmlparser2-tree-adapter": "^7.0.0" + } + }, + "cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "requires": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + } + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==" + }, + "ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==" + }, + "clean-css": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.2.tgz", + "integrity": "sha512-JVJbM+f3d3Q704rF4bqQ5UUyTtuJ0JRKNbTKVEeujCCBoMdkEi+V+e8oktO9qGQNSvHrFTM6JZRXrUvGR1czww==", + "requires": { + "source-map": "~0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "clean-set": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/clean-set/-/clean-set-1.1.2.tgz", + "integrity": "sha512-cA8uCj0qSoG9e0kevyOWXwPaELRPVg5Pxp6WskLMwerx257Zfnh8Nl0JBH59d7wQzij2CK7qEfJQK3RjuKKIug==" + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==" + }, + "cli-boxes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", + "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==" + }, + "cli-table3": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz", + "integrity": "sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==", + "requires": { + "@colors/colors": "1.5.0", + "string-width": "^4.2.0" + }, + "dependencies": { + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + } + } + }, + "clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "requires": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "dependencies": { + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "requires": { + "isobject": "^3.0.1" + } + } + } + }, + "clsx": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz", + "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==" + }, + "codesandbox-import-util-types": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/codesandbox-import-util-types/-/codesandbox-import-util-types-2.2.3.tgz", + "integrity": "sha512-Qj00p60oNExthP2oR3vvXmUGjukij+rxJGuiaKM6tyUmSyimdZsqHI/TUvFFClAffk9s7hxGnQgWQ8KCce27qQ==" + }, + "collapse-white-space": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-2.1.0.tgz", + "integrity": "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==" + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "colord": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==" + }, + "colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==" + }, + "combine-promises": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/combine-promises/-/combine-promises-1.2.0.tgz", + "integrity": "sha512-VcQB1ziGD0NXrhKxiwyNbCDmRzs/OShMs2GqW2DlU2A/Sd0nQxE1oWDAE5O0ygSx5mgQOn9eIFh7yKPgFRVkPQ==" + }, + "comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==" + }, + "commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==" + }, + "common-path-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", + "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==" + }, + "compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "requires": { + "mime-db": ">= 1.43.0 < 2" + }, + "dependencies": { + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + } + } + }, + "compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "requires": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "requires": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "configstore": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-6.0.0.tgz", + "integrity": "sha512-cD31W1v3GqUlQvbBCGcXmd2Nj9SvLDOP1oQ0YFuLETufzSPaKp11rYBsSOm7rCsW3OnIRAFM3OxRhceaXNYHkA==", + "requires": { + "dot-prop": "^6.0.1", + "graceful-fs": "^4.2.6", + "unique-string": "^3.0.0", + "write-file-atomic": "^3.0.3", + "xdg-basedir": "^5.0.1" + } + }, + "connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==" + }, + "consola": { + "version": "2.15.3", + "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", + "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==" + }, + "content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==" + }, + "content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" + }, + "convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" + }, + "cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "copy-text-to-clipboard": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/copy-text-to-clipboard/-/copy-text-to-clipboard-3.2.0.tgz", + "integrity": "sha512-RnJFp1XR/LOBDckxTib5Qjr/PMfkatD0MUCQgdpqS8MdKiNUzBjAQBEN6oUy+jW7LI93BBG3DtMB2KOOKpGs2Q==" + }, + "copy-webpack-plugin": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz", + "integrity": "sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==", + "requires": { + "fast-glob": "^3.2.11", + "glob-parent": "^6.0.1", + "globby": "^13.1.1", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0" + }, + "dependencies": { + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "requires": { + "is-glob": "^4.0.3" + } + }, + "globby": { + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", + "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", + "requires": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.3.0", + "ignore": "^5.2.4", + "merge2": "^1.4.1", + "slash": "^4.0.0" + } + }, + "slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==" + } + } + }, + "core-js": { + "version": "3.33.2", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.33.2.tgz", + "integrity": "sha512-XeBzWI6QL3nJQiHmdzbAOiMYqjrb7hwU7A39Qhvd/POSa/t9E1AeZyEZx3fNvp/vtM8zXwhoL0FsiS0hD0pruQ==" + }, + "core-js-compat": { + "version": "3.33.2", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.33.2.tgz", + "integrity": "sha512-axfo+wxFVxnqf8RvxTzoAlzW4gRoacrHeoFlc9n0x50+7BEyZL/Rt3hicaED1/CEd7I6tPCPVUYcJwCMO5XUYw==", + "requires": { + "browserslist": "^4.22.1" + } + }, + "core-js-pure": { + "version": "3.33.2", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.33.2.tgz", + "integrity": "sha512-a8zeCdyVk7uF2elKIGz67AjcXOxjRbwOLz8SbklEso1V+2DoW4OkAMZN9S9GBgvZIaqQi/OemFX4OiSoQEmg1Q==" + }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "cose-base": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz", + "integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==", + "requires": { + "layout-base": "^1.0.0" + } + }, + "cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "requires": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + } + }, + "crelt": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==" + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "crypto-random-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz", + "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==", + "requires": { + "type-fest": "^1.0.1" + }, + "dependencies": { + "type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==" + } + } + }, + "css-declaration-sorter": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.2.0.tgz", + "integrity": "sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow==", + "requires": {} + }, + "css-loader": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.8.1.tgz", + "integrity": "sha512-xDAXtEVGlD0gJ07iclwWVkLoZOpEvAWaSyf6W18S2pOC//K8+qUDIx8IIT3D+HjnmkJPQeesOPv5aiUaJsCM2g==", + "requires": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.21", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.3", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.3.8" + } + }, + "css-minimizer-webpack-plugin": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-5.0.1.tgz", + "integrity": "sha512-3caImjKFQkS+ws1TGcFn0V1HyDJFq1Euy589JlD6/3rV2kj+w7r5G9WDMgSHvpvXHNZ2calVypZWuEDQd9wfLg==", + "requires": { + "@jridgewell/trace-mapping": "^0.3.18", + "cssnano": "^6.0.1", + "jest-worker": "^29.4.3", + "postcss": "^8.4.24", + "schema-utils": "^4.0.1", + "serialize-javascript": "^6.0.1" + } + }, + "css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "requires": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + } + }, + "css-tree": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "requires": { + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" + } + }, + "css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==" + }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" + }, + "cssnano": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-6.1.2.tgz", + "integrity": "sha512-rYk5UeX7VAM/u0lNqewCdasdtPK81CgX8wJFLEIXHbV2oldWRgJAsZrdhRXkV1NJzA2g850KiFm9mMU2HxNxMA==", + "requires": { + "cssnano-preset-default": "^6.1.2", + "lilconfig": "^3.1.1" + } + }, + "cssnano-preset-advanced": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/cssnano-preset-advanced/-/cssnano-preset-advanced-6.1.2.tgz", + "integrity": "sha512-Nhao7eD8ph2DoHolEzQs5CfRpiEP0xa1HBdnFZ82kvqdmbwVBUr2r1QuQ4t1pi+D1ZpqpcO4T+wy/7RxzJ/WPQ==", + "requires": { + "autoprefixer": "^10.4.19", + "browserslist": "^4.23.0", + "cssnano-preset-default": "^6.1.2", + "postcss-discard-unused": "^6.0.5", + "postcss-merge-idents": "^6.0.3", + "postcss-reduce-idents": "^6.0.3", + "postcss-zindex": "^6.0.2" + } + }, + "cssnano-preset-default": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-6.1.2.tgz", + "integrity": "sha512-1C0C+eNaeN8OcHQa193aRgYexyJtU8XwbdieEjClw+J9d94E41LwT6ivKH0WT+fYwYWB0Zp3I3IZ7tI/BbUbrg==", + "requires": { + "browserslist": "^4.23.0", + "css-declaration-sorter": "^7.2.0", + "cssnano-utils": "^4.0.2", + "postcss-calc": "^9.0.1", + "postcss-colormin": "^6.1.0", + "postcss-convert-values": "^6.1.0", + "postcss-discard-comments": "^6.0.2", + "postcss-discard-duplicates": "^6.0.3", + "postcss-discard-empty": "^6.0.3", + "postcss-discard-overridden": "^6.0.2", + "postcss-merge-longhand": "^6.0.5", + "postcss-merge-rules": "^6.1.1", + "postcss-minify-font-values": "^6.1.0", + "postcss-minify-gradients": "^6.0.3", + "postcss-minify-params": "^6.1.0", + "postcss-minify-selectors": "^6.0.4", + "postcss-normalize-charset": "^6.0.2", + "postcss-normalize-display-values": "^6.0.2", + "postcss-normalize-positions": "^6.0.2", + "postcss-normalize-repeat-style": "^6.0.2", + "postcss-normalize-string": "^6.0.2", + "postcss-normalize-timing-functions": "^6.0.2", + "postcss-normalize-unicode": "^6.1.0", + "postcss-normalize-url": "^6.0.2", + "postcss-normalize-whitespace": "^6.0.2", + "postcss-ordered-values": "^6.0.2", + "postcss-reduce-initial": "^6.1.0", + "postcss-reduce-transforms": "^6.0.2", + "postcss-svgo": "^6.0.3", + "postcss-unique-selectors": "^6.0.4" + } + }, + "cssnano-utils": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-4.0.2.tgz", + "integrity": "sha512-ZR1jHg+wZ8o4c3zqf1SIUSTIvm/9mU343FMR6Obe/unskbvpGhZOo1J6d/r8D1pzkRQYuwbcH3hToOuoA2G7oQ==", + "requires": {} + }, + "csso": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", + "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", + "requires": { + "css-tree": "~2.2.0" + }, + "dependencies": { + "css-tree": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", + "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", + "requires": { + "mdn-data": "2.0.28", + "source-map-js": "^1.0.1" + } + }, + "mdn-data": { + "version": "2.0.28", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", + "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==" + } + } + }, + "csstype": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" + }, + "cytoscape": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.27.0.tgz", + "integrity": "sha512-pPZJilfX9BxESwujODz5pydeGi+FBrXq1rcaB1mfhFXXFJ9GjE6CNndAk+8jPzoXGD+16LtSS4xlYEIUiW4Abg==", + "requires": { + "heap": "^0.2.6", + "lodash": "^4.17.21" + } + }, + "cytoscape-cose-bilkent": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz", + "integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==", + "requires": { + "cose-base": "^1.0.0" + } + }, + "cytoscape-fcose": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz", + "integrity": "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==", + "requires": { + "cose-base": "^2.2.0" + }, + "dependencies": { + "cose-base": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-2.2.0.tgz", + "integrity": "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==", + "requires": { + "layout-base": "^2.0.0" + } + }, + "layout-base": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-2.0.1.tgz", + "integrity": "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==" + } + } + }, + "d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "requires": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "d3": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.8.5.tgz", + "integrity": "sha512-JgoahDG51ncUfJu6wX/1vWQEqOflgXyl4MaHqlcSruTez7yhaRKR9i8VjjcQGeS2en/jnFivXuaIMnseMMt0XA==", + "requires": { + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" + } + }, + "d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "requires": { + "internmap": "1 - 2" + } + }, + "d3-axis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==" + }, + "d3-brush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", + "requires": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + } + }, + "d3-chord": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", + "requires": { + "d3-path": "1 - 3" + } + }, + "d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==" + }, + "d3-contour": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", + "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", + "requires": { + "d3-array": "^3.2.0" + } + }, + "d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", + "requires": { + "delaunator": "5" + } + }, + "d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==" + }, + "d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "requires": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + } + }, + "d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "requires": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "dependencies": { + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==" + }, + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + } + } + }, + "d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==" + }, + "d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "requires": { + "d3-dsv": "1 - 3" + } + }, + "d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "requires": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + } + }, + "d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==" + }, + "d3-geo": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.0.tgz", + "integrity": "sha512-JEo5HxXDdDYXCaWdwLRt79y7giK8SbhZJbFWXqbRTolCHFI5jRqteLzCsq51NKbUoX0PjBVSohxrx+NoOUujYA==", + "requires": { + "d3-array": "2.5.0 - 3" + } + }, + "d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==" + }, + "d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "requires": { + "d3-color": "1 - 3" + } + }, + "d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==" + }, + "d3-polygon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==" + }, + "d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==" + }, + "d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==" + }, + "d3-sankey": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz", + "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==", + "requires": { + "d3-array": "1 - 2", + "d3-shape": "^1.2.0" + }, + "dependencies": { + "d3-array": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", + "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", + "requires": { + "internmap": "^1.0.0" + } + }, + "d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==" + }, + "d3-shape": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", + "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", + "requires": { + "d3-path": "1" + } + }, + "internmap": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", + "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==" + } + } + }, + "d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "requires": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + } + }, + "d3-scale-chromatic": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz", + "integrity": "sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==", + "requires": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + } + }, + "d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==" + }, + "d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "requires": { + "d3-path": "^3.1.0" + } + }, + "d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "requires": { + "d3-array": "2 - 3" + } + }, + "d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "requires": { + "d3-time": "1 - 3" + } + }, + "d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==" + }, + "d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "requires": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + } + }, + "d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "requires": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + } + }, + "dagre-d3-es": { + "version": "7.0.10", + "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.10.tgz", + "integrity": "sha512-qTCQmEhcynucuaZgY5/+ti3X/rnszKZhEQH/ZdWdtP1tA/y3VoHJzcVrO9pjjJCNpigfscAtoUB5ONcd2wNn0A==", + "requires": { + "d3": "^7.8.2", + "lodash-es": "^4.17.21" + } + }, + "dayjs": { + "version": "1.11.10", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", + "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==" + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "decode-named-character-reference": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", + "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", + "requires": { + "character-entities": "^2.0.0" + } + }, + "decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "requires": { + "mimic-response": "^3.1.0" + }, + "dependencies": { + "mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==" + } + } + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" + }, + "deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==" + }, + "default-gateway": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "requires": { + "execa": "^5.0.0" + } + }, + "defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==" + }, + "define-data-property": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", + "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "requires": { + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + } + }, + "define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==" + }, + "define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "requires": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + } + }, + "del": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz", + "integrity": "sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==", + "requires": { + "globby": "^11.0.1", + "graceful-fs": "^4.2.4", + "is-glob": "^4.0.1", + "is-path-cwd": "^2.2.0", + "is-path-inside": "^3.0.2", + "p-map": "^4.0.0", + "rimraf": "^3.0.2", + "slash": "^3.0.0" + } + }, + "delaunator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.0.tgz", + "integrity": "sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==", + "requires": { + "robust-predicates": "^3.0.0" + } + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==" + }, + "destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" + }, + "detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==" + }, + "detect-port": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.5.1.tgz", + "integrity": "sha512-aBzdj76lueB6uUst5iAs7+0H/oOjqI5D16XUWxlWMIMROhcM0rfsNVk93zTngq1dDNpoXRr++Sus7ETAExppAQ==", + "requires": { + "address": "^1.0.1", + "debug": "4" + } + }, + "detect-port-alt": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz", + "integrity": "sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==", + "requires": { + "address": "^1.0.1", + "debug": "^2.6.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } + } + }, + "devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "requires": { + "dequal": "^2.0.0" + } + }, + "diff": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", + "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==" + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "requires": { + "path-type": "^4.0.0" + } + }, + "dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==" + }, + "dns-packet": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", + "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", + "requires": { + "@leichtgewicht/ip-codec": "^2.0.1" + } + }, + "docusaurus-plugin-sass": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/docusaurus-plugin-sass/-/docusaurus-plugin-sass-0.2.5.tgz", + "integrity": "sha512-Z+D0fLFUKcFpM+bqSUmqKIU+vO+YF1xoEQh5hoFreg2eMf722+siwXDD+sqtwU8E4MvVpuvsQfaHwODNlxJAEg==", + "requires": { + "sass-loader": "^10.1.1" + } + }, + "dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "requires": { + "utila": "~0.4" + } + }, + "dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "requires": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + } + }, + "domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==" + }, + "domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "requires": { + "domelementtype": "^2.3.0" + } + }, + "dompurify": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.6.tgz", + "integrity": "sha512-ilkD8YEnnGh1zJ240uJsW7AzE+2qpbOUYjacomn3AvJ6J4JhKGSZ2nh4wUIXPZrEPppaCLx5jFe8T89Rk8tQ7w==" + }, + "domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "requires": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + } + }, + "dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "dot-prop": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", + "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", + "requires": { + "is-obj": "^2.0.0" + }, + "dependencies": { + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==" + } + } + }, + "dotenv": { + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", + "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==" + }, + "duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" + }, + "eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "electron-to-chromium": { + "version": "1.4.699", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.699.tgz", + "integrity": "sha512-I7q3BbQi6e4tJJN5CRcyvxhK0iJb34TV8eJQcgh+fR2fQ8miMgZcEInckCo1U9exDHbfz7DLDnFn8oqH/VcRKw==" + }, + "elkjs": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/elkjs/-/elkjs-0.8.2.tgz", + "integrity": "sha512-L6uRgvZTH+4OF5NE/MBbzQx/WYpru1xCBE9respNj6qznEewGUIfhzmm7horWWxbNO2M0WckQypGctR8lH79xQ==" + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + }, + "emojilib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/emojilib/-/emojilib-2.4.0.tgz", + "integrity": "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==" + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==" + }, + "emoticon": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/emoticon/-/emoticon-4.0.1.tgz", + "integrity": "sha512-dqx7eA9YaqyvYtUhJwT4rC1HIp82j5ybS1/vQ42ur+jBe17dJMwZE4+gvL1XadSFfxaPFFGt3Xsw+Y8akThDlw==" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" + }, + "enhanced-resolve": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", + "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", + "requires": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + } + }, + "entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==" + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-module-lexer": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.1.tgz", + "integrity": "sha512-JUFAyicQV9mXc3YRxPnDlrfBKpqt6hUYzz9/boprUJHs4e4KVr3XwOF70doO6gwXUor6EWZJAyWAfKki84t20Q==" + }, + "es5-ext": { + "version": "0.10.62", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", + "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", + "requires": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "next-tick": "^1.1.0" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "requires": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" + }, + "escape-carriage": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/escape-carriage/-/escape-carriage-1.3.1.tgz", + "integrity": "sha512-GwBr6yViW3ttx1kb7/Oh+gKQ1/TrhYwxKqVmg5gS+BK+Qe2KrOa/Vh7w3HPBvgGf0LfcDGoY9I6NHKoA5Hozhw==" + }, + "escape-goat": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-4.0.0.tgz", + "integrity": "sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" + }, + "estree-util-attach-comments": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-attach-comments/-/estree-util-attach-comments-3.0.0.tgz", + "integrity": "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==", + "requires": { + "@types/estree": "^1.0.0" + } + }, + "estree-util-build-jsx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/estree-util-build-jsx/-/estree-util-build-jsx-3.0.1.tgz", + "integrity": "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==", + "requires": { + "@types/estree-jsx": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "estree-walker": "^3.0.0" + } + }, + "estree-util-is-identifier-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==" + }, + "estree-util-to-js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/estree-util-to-js/-/estree-util-to-js-2.0.0.tgz", + "integrity": "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==", + "requires": { + "@types/estree-jsx": "^1.0.0", + "astring": "^1.8.0", + "source-map": "^0.7.0" + } + }, + "estree-util-value-to-estree": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/estree-util-value-to-estree/-/estree-util-value-to-estree-3.1.1.tgz", + "integrity": "sha512-5mvUrF2suuv5f5cGDnDphIy4/gW86z82kl5qG6mM9z04SEQI4FB5Apmaw/TGEf3l55nLtMs5s51dmhUzvAHQCA==", + "requires": { + "@types/estree": "^1.0.0", + "is-plain-obj": "^4.0.0" + } + }, + "estree-util-visit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/estree-util-visit/-/estree-util-visit-2.0.0.tgz", + "integrity": "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==", + "requires": { + "@types/estree-jsx": "^1.0.0", + "@types/unist": "^3.0.0" + } + }, + "estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "requires": { + "@types/estree": "^1.0.0" + } + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + }, + "eta": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/eta/-/eta-2.2.0.tgz", + "integrity": "sha512-UVQ72Rqjy/ZKQalzV5dCCJP80GrmPrMxh6NlNf+erV6ObL0ZFkhCstWRawS85z3smdr3d2wXPsZEY7rDPfGd2g==" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" + }, + "eval": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/eval/-/eval-0.1.8.tgz", + "integrity": "sha512-EzV94NYKoO09GLXGjXj9JIlXijVck4ONSr5wiCWDvhsvj5jxSrzTmRU/9C1DyB6uToszLs8aifA6NQ7lEQdvFw==", + "requires": { + "@types/node": "*", + "require-like": ">= 0.1.1" + } + }, + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" + }, + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "requires": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "requires": { + "safe-buffer": "5.2.1" + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + } + } + }, + "ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "requires": { + "type": "^2.7.2" + }, + "dependencies": { + "type": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "fast-url-parser": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz", + "integrity": "sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ==", + "requires": { + "punycode": "^1.3.2" + } + }, + "fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "requires": { + "reusify": "^1.0.4" + } + }, + "fault": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fault/-/fault-2.0.1.tgz", + "integrity": "sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==", + "requires": { + "format": "^0.2.0" + } + }, + "faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "feed": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/feed/-/feed-4.2.2.tgz", + "integrity": "sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ==", + "requires": { + "xml-js": "^1.6.11" + } + }, + "file-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", + "requires": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "requires": {} + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "filesize": { + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", + "integrity": "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==" + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } + } + }, + "find-cache-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-4.0.0.tgz", + "integrity": "sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==", + "requires": { + "common-path-prefix": "^3.0.0", + "pkg-dir": "^7.0.0" + } + }, + "find-up": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", + "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", + "requires": { + "locate-path": "^7.1.0", + "path-exists": "^5.0.0" + } + }, + "flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==" + }, + "follow-redirects": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", + "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==" + }, + "fork-ts-checker-webpack-plugin": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.3.tgz", + "integrity": "sha512-SbH/l9ikmMWycd5puHJKTkZJKddF4iRLyW3DeZ08HTI7NGyLS38MXd/KGgeWumQO7YNQbW2u/NtPT2YowbPaGQ==", + "requires": { + "@babel/code-frame": "^7.8.3", + "@types/json-schema": "^7.0.5", + "chalk": "^4.1.0", + "chokidar": "^3.4.2", + "cosmiconfig": "^6.0.0", + "deepmerge": "^4.2.2", + "fs-extra": "^9.0.0", + "glob": "^7.1.6", + "memfs": "^3.1.2", + "minimatch": "^3.0.4", + "schema-utils": "2.7.0", + "semver": "^7.3.2", + "tapable": "^1.0.0" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "requires": {} + }, + "cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + } + }, + "fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "schema-utils": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", + "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", + "requires": { + "@types/json-schema": "^7.0.4", + "ajv": "^6.12.2", + "ajv-keywords": "^3.4.1" + } + }, + "tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==" + } + } + }, + "form-data-encoder": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", + "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==" + }, + "format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==" + }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" + }, + "fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" + }, + "fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "fs-monkey": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.5.tgz", + "integrity": "sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew==" + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "optional": true + }, + "function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" + }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==" + }, + "get-intrinsic": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", + "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", + "requires": { + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + } + }, + "get-own-enumerable-property-symbols": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==" + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==" + }, + "github-slugger": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.5.0.tgz", + "integrity": "sha512-wIh+gKBI9Nshz2o46B0B3f5k/W+WI9ZAv6y5Dn5WJ5SK1t0TnDimB4WE5rmTD05ZAIn8HALCZVmCsvj0w0v0lw==" + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "requires": { + "is-glob": "^4.0.1" + } + }, + "glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" + }, + "global-dirs": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", + "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==", + "requires": { + "ini": "2.0.0" + }, + "dependencies": { + "ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==" + } + } + }, + "global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "requires": { + "global-prefix": "^3.0.0" + } + }, + "global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "requires": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "dependencies": { + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, + "gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "requires": { + "get-intrinsic": "^1.1.3" + } + }, + "got": { + "version": "12.6.1", + "resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz", + "integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==", + "requires": { + "@sindresorhus/is": "^5.2.0", + "@szmarczak/http-timer": "^5.0.1", + "cacheable-lookup": "^7.0.0", + "cacheable-request": "^10.2.8", + "decompress-response": "^6.0.0", + "form-data-encoder": "^2.1.2", + "get-stream": "^6.0.1", + "http2-wrapper": "^2.1.10", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^3.0.0", + "responselike": "^3.0.0" + }, + "dependencies": { + "@sindresorhus/is": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", + "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==" + } + } + }, + "graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "gray-matter": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", + "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==", + "requires": { + "js-yaml": "^3.13.1", + "kind-of": "^6.0.2", + "section-matter": "^1.0.0", + "strip-bom-string": "^1.0.0" + }, + "dependencies": { + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + } + } + }, + "gzip-size": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", + "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", + "requires": { + "duplexer": "^0.1.2" + } + }, + "handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "has-property-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", + "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", + "requires": { + "get-intrinsic": "^1.2.2" + } + }, + "has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==" + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + }, + "has-yarn": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-3.0.0.tgz", + "integrity": "sha512-IrsVwUHhEULx3R8f/aA8AHuEzAorplsab/v8HBzEiIukwq5i/EC+xmOW+HfP1OaDP+2JkgT1yILHN2O3UFIbcA==" + }, + "hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "requires": { + "function-bind": "^1.1.2" + } + }, + "hast-util-from-parse5": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.1.tgz", + "integrity": "sha512-Er/Iixbc7IEa7r/XLtuG52zoqn/b3Xng/w6aZQ0xGVxzhw5xUFxcRqdPzP6yFi/4HBYRaifaI5fQ1RH8n0ZeOQ==", + "requires": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "hastscript": "^8.0.0", + "property-information": "^6.0.0", + "vfile": "^6.0.0", + "vfile-location": "^5.0.0", + "web-namespaces": "^2.0.0" + } + }, + "hast-util-parse-selector": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", + "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", + "requires": { + "@types/hast": "^3.0.0" + } + }, + "hast-util-raw": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.0.3.tgz", + "integrity": "sha512-ICWvVOF2fq4+7CMmtCPD5CM4QKjPbHpPotE6+8tDooV0ZuyJVUzHsrNX+O5NaRbieTf0F7FfeBOMAwi6Td0+yQ==", + "requires": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "@ungap/structured-clone": "^1.0.0", + "hast-util-from-parse5": "^8.0.0", + "hast-util-to-parse5": "^8.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "parse5": "^7.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + } + }, + "hast-util-to-estree": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hast-util-to-estree/-/hast-util-to-estree-3.1.0.tgz", + "integrity": "sha512-lfX5g6hqVh9kjS/B9E2gSkvHH4SZNiQFiqWS0x9fENzEl+8W12RqdRxX6d/Cwxi30tPQs3bIO+aolQJNp1bIyw==", + "requires": { + "@types/estree": "^1.0.0", + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-attach-comments": "^3.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-object": "^0.4.0", + "unist-util-position": "^5.0.0", + "zwitch": "^2.0.0" + } + }, + "hast-util-to-jsx-runtime": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.0.tgz", + "integrity": "sha512-H/y0+IWPdsLLS738P8tDnrQ8Z+dj12zQQ6WC11TIM21C8WFVoIxcqWXf2H3hiTVZjF1AWqoimGwrTWecWrnmRQ==", + "requires": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-object": "^1.0.0", + "unist-util-position": "^5.0.0", + "vfile-message": "^4.0.0" + }, + "dependencies": { + "inline-style-parser": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.2.tgz", + "integrity": "sha512-EcKzdTHVe8wFVOGEYXiW9WmJXPjqi1T+234YpJr98RiFYKHV3cdy1+3mkTE+KHTHxFFLH51SfaGOoUdW+v7ViQ==" + }, + "style-to-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.5.tgz", + "integrity": "sha512-rDRwHtoDD3UMMrmZ6BzOW0naTjMsVZLIjsGleSKS/0Oz+cgCfAPRspaqJuE8rDzpKha/nEvnM0IF4seEAZUTKQ==", + "requires": { + "inline-style-parser": "0.2.2" + } + } + } + }, + "hast-util-to-parse5": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.0.tgz", + "integrity": "sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==", + "requires": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + } + }, + "hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "requires": { + "@types/hast": "^3.0.0" + } + }, + "hastscript": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-8.0.0.tgz", + "integrity": "sha512-dMOtzCEd3ABUeSIISmrETiKuyydk1w0pa+gE/uormcTpSYuaNJPbX1NU3JLyscSLjwAQM8bWMhhIlnCqnRvDTw==", + "requires": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^4.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0" + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" + }, + "heap": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.7.tgz", + "integrity": "sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==" + }, + "history": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", + "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==", + "requires": { + "@babel/runtime": "^7.1.2", + "loose-envify": "^1.2.0", + "resolve-pathname": "^3.0.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0", + "value-equal": "^1.0.1" + } + }, + "hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "requires": { + "react-is": "^16.7.0" + }, + "dependencies": { + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + } + } + }, + "hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", + "requires": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "html-entities": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.4.0.tgz", + "integrity": "sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ==" + }, + "html-minifier-terser": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-7.2.0.tgz", + "integrity": "sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA==", + "requires": { + "camel-case": "^4.1.2", + "clean-css": "~5.3.2", + "commander": "^10.0.0", + "entities": "^4.4.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.15.1" + }, + "dependencies": { + "commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==" + } + } + }, + "html-tags": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz", + "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==" + }, + "html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==" + }, + "html-webpack-plugin": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.3.tgz", + "integrity": "sha512-6YrDKTuqaP/TquFH7h4srYWsZx+x6k6+FbsTm0ziCwGHDP78Unr1r9F/H4+sGmMbX08GQcJ+K64x55b+7VM/jg==", + "requires": { + "@types/html-minifier-terser": "^6.0.0", + "html-minifier-terser": "^6.0.2", + "lodash": "^4.17.21", + "pretty-error": "^4.0.0", + "tapable": "^2.0.0" + }, + "dependencies": { + "commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==" + }, + "html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", + "requires": { + "camel-case": "^4.1.2", + "clean-css": "^5.2.2", + "commander": "^8.3.0", + "he": "^1.2.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.10.0" + } + } + } + }, + "htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "requires": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, + "http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==" + }, + "http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==" + }, + "http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "requires": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + } + }, + "http-parser-js": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==" + }, + "http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "requires": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-proxy-middleware": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", + "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "requires": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "dependencies": { + "is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==" + } + } + }, + "http2-wrapper": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.0.tgz", + "integrity": "sha512-kZB0wxMo0sh1PehyjJUWRFEd99KC5TLjZ2cULC4f9iqJBAmKQQXEICjxl5iPJRwP40dpeHFqqhm7tYCvODpqpQ==", + "requires": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.2.0" + } + }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==" + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "requires": {} + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, + "ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==" + }, + "image-size": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.1.1.tgz", + "integrity": "sha512-541xKlUw6jr/6gGuk92F+mYM5zaFAc5ahphvkqvNe2bQ6gVBkd6bfrmVJ2t4KDAfikAYZyIqTnktX3i6/aQDrQ==", + "requires": { + "queue": "6.0.2" + } + }, + "immer": { + "version": "9.0.21", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", + "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==" + }, + "immutable": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.6.tgz", + "integrity": "sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==" + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "import-lazy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", + "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==" + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==" + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==" + }, + "infima": { + "version": "0.2.0-alpha.43", + "resolved": "https://registry.npmjs.org/infima/-/infima-0.2.0-alpha.43.tgz", + "integrity": "sha512-2uw57LvUqW0rK/SWYnd/2rRfxNA5DDNOh33jxF7fy46VWoNhGxiUQyVZHbBMjQ33mQem0cjdDVwgWVAmlRfgyQ==" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, + "inline-style-parser": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", + "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==" + }, + "internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==" + }, + "interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==" + }, + "intersection-observer": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/intersection-observer/-/intersection-observer-0.10.0.tgz", + "integrity": "sha512-fn4bQ0Xq8FTej09YC/jqKZwtijpvARlRp6wxL5WTA6yPe2YWSJ5RJh7Nm79rK2qB0wr6iDQzH60XGq5V/7u8YQ==" + }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "requires": { + "loose-envify": "^1.0.0" + } + }, + "ipaddr.js": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.1.0.tgz", + "integrity": "sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ==" + }, + "is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==" + }, + "is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "requires": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-ci": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", + "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", + "requires": { + "ci-info": "^3.2.0" + } + }, + "is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "requires": { + "hasown": "^2.0.0" + } + }, + "is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==" + }, + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==" + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==" + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==" + }, + "is-installed-globally": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", + "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", + "requires": { + "global-dirs": "^3.0.0", + "is-path-inside": "^3.0.2" + } + }, + "is-npm": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-6.0.0.tgz", + "integrity": "sha512-JEjxbSmtPSt1c8XTkVrlujcXdKV1/tvuQ7GwKcAlyiVLeYFQ2VHat8xfrDJsIkhCdF/tZ7CiIR3sy141c6+gPQ==" + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, + "is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==" + }, + "is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==" + }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==" + }, + "is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==" + }, + "is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==" + }, + "is-reference": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz", + "integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==", + "requires": { + "@types/estree": "*" + } + }, + "is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==" + }, + "is-root": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", + "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==" + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "requires": { + "is-docker": "^2.0.0" + } + }, + "is-yarn-global": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.4.1.tgz", + "integrity": "sha512-/kppl+R+LO5VmhYSEWARUFjodS25D68gvj8W7z0I7OWhUla5xWu8KL6CtB2V0R6yqhnRgbcaREMr4EEM6htLPQ==" + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==" + }, + "jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "requires": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + } + }, + "jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "requires": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "dependencies": { + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jiti": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", + "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==" + }, + "joi": { + "version": "17.11.0", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.11.0.tgz", + "integrity": "sha512-NgB+lZLNoqISVy1rZocE9PZI36bL/77ie924Ri43yEvi9GUUMPeyVIr8KdFTMUlby1p0PBYMk9spIxEUQYqrJQ==", + "requires": { + "@hapi/hoek": "^9.0.0", + "@hapi/topo": "^5.0.0", + "@sideway/address": "^4.1.3", + "@sideway/formula": "^3.0.1", + "@sideway/pinpoint": "^2.0.0" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "requires": { + "argparse": "^2.0.1" + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" + }, + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==" + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "requires": { + "json-buffer": "3.0.1" + } + }, + "khroma": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz", + "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==" + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" + }, + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==" + }, + "klona": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", + "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==" + }, + "latest-version": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-7.0.0.tgz", + "integrity": "sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg==", + "requires": { + "package-json": "^8.1.0" + } + }, + "launch-editor": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.6.1.tgz", + "integrity": "sha512-eB/uXmFVpY4zezmGp5XtU21kwo7GBbKB+EQ+UZeWtGb9yAM5xt/Evk+lYH3eRNAtId+ej4u7TYPFZ07w4s7rRw==", + "requires": { + "picocolors": "^1.0.0", + "shell-quote": "^1.8.1" + } + }, + "layout-base": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz", + "integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==" + }, + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==" + }, + "lilconfig": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", + "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==" + }, + "lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + }, + "loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==" + }, + "loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "locate-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "requires": { + "p-locate": "^6.0.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, + "lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" + }, + "lodash.escape": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-4.0.1.tgz", + "integrity": "sha512-nXEOnb/jK9g0DYMr1/Xvq6l5xMD7GDG55+GSYIYmS0G4tBk/hURD4JR9WCavs04t33WmJx9kCyp9vJ+mr4BOUw==" + }, + "lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==" + }, + "lodash.invokemap": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.invokemap/-/lodash.invokemap-4.6.0.tgz", + "integrity": "sha512-CfkycNtMqgUlfjfdh2BhKO/ZXrP8ePOX5lEU/g0R3ItJcnuxWDwokMGKx1hWcfOikmyOVx6X9IwWnDGlgKl61w==" + }, + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==" + }, + "lodash.pullall": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.pullall/-/lodash.pullall-4.2.0.tgz", + "integrity": "sha512-VhqxBKH0ZxPpLhiu68YD1KnHmbhQJQctcipvmFnqIBDYzcIHzf3Zpu0tpeOKtR4x76p9yohc506eGdOjTmyIBg==" + }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==" + }, + "lodash.uniqby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz", + "integrity": "sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==" + }, + "longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==" + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "requires": { + "tslib": "^2.0.3" + } + }, + "lowercase-keys": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", + "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==" + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "requires": { + "yallist": "^3.0.2" + } + }, + "lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==" + }, + "markdown-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-2.0.0.tgz", + "integrity": "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==" + }, + "markdown-table": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.3.tgz", + "integrity": "sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==" + }, + "mdast-util-directive": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-directive/-/mdast-util-directive-3.0.0.tgz", + "integrity": "sha512-JUpYOqKI4mM3sZcNxmF/ox04XYFFkNwr0CFlrQIkCwbvH0xzMCqkMqAde9wRd80VAhaUrwFwKm2nxretdT1h7Q==", + "requires": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-visit-parents": "^6.0.0" + } + }, + "mdast-util-find-and-replace": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.1.tgz", + "integrity": "sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA==", + "requires": { + "@types/mdast": "^4.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==" + } + } + }, + "mdast-util-from-markdown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.0.tgz", + "integrity": "sha512-n7MTOr/z+8NAX/wmhhDji8O3bRvPTV/U0oTCaZJkjhPSKTPhS3xufVhKGF8s1pJ7Ox4QgoIU7KHseh09S+9rTA==", + "requires": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "dependencies": { + "micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" + } + } + }, + "mdast-util-frontmatter": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-frontmatter/-/mdast-util-frontmatter-2.0.1.tgz", + "integrity": "sha512-LRqI9+wdgC25P0URIJY9vwocIzCcksduHQ9OF2joxQoyTNVduwLAFUzjoopuRJbJAReaKrNQKAZKL3uCMugWJA==", + "requires": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "escape-string-regexp": "^5.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-extension-frontmatter": "^2.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==" + } + } + }, + "mdast-util-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.0.0.tgz", + "integrity": "sha512-dgQEX5Amaq+DuUqf26jJqSK9qgixgd6rYDHAv4aTBuA92cTknZlKpPfa86Z/s8Dj8xsAQpFfBmPUHWJBWqS4Bw==", + "requires": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.0", + "mdast-util-gfm-strikethrough": "^2.0.0", + "mdast-util-gfm-table": "^2.0.0", + "mdast-util-gfm-task-list-item": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + } + }, + "mdast-util-gfm-autolink-literal": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.0.tgz", + "integrity": "sha512-FyzMsduZZHSc3i0Px3PQcBT4WJY/X/RCtEJKuybiC6sjPqLv7h1yqAkmILZtuxMSsUyaLUWNp71+vQH2zqp5cg==", + "requires": { + "@types/mdast": "^4.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-find-and-replace": "^3.0.0", + "micromark-util-character": "^2.0.0" + }, + "dependencies": { + "micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "requires": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" + } + } + }, + "mdast-util-gfm-footnote": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.0.0.tgz", + "integrity": "sha512-5jOT2boTSVkMnQ7LTrd6n/18kqwjmuYqo7JUPe+tRCY6O7dAuTFMtTPauYYrMPpox9hlN0uOx/FL8XvEfG9/mQ==", + "requires": { + "@types/mdast": "^4.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0" + } + }, + "mdast-util-gfm-strikethrough": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", + "requires": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + } + }, + "mdast-util-gfm-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", + "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", + "requires": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + } + }, + "mdast-util-gfm-task-list-item": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", + "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", + "requires": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + } + }, + "mdast-util-mdx": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-3.0.0.tgz", + "integrity": "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==", + "requires": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + } + }, + "mdast-util-mdx-expression": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.0.tgz", + "integrity": "sha512-fGCu8eWdKUKNu5mohVGkhBXCXGnOTLuFqOvGMvdikr+J1w7lDJgxThOKpwRWzzbyXAU2hhSwsmssOY4yTokluw==", + "requires": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + } + }, + "mdast-util-mdx-jsx": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.1.1.tgz", + "integrity": "sha512-Di63TQEHbiApe6CFp/qQXCORHMHnmW2JFdr5PYH57LuEIPjijRHicAmL5wQu+B0/Q4p0qJaEOE1EkhiwxiNmAQ==", + "requires": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-remove-position": "^5.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + } + }, + "mdast-util-mdxjs-esm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", + "requires": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + } + }, + "mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "requires": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + } + }, + "mdast-util-to-hast": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.1.0.tgz", + "integrity": "sha512-/e2l/6+OdGp/FB+ctrJ9Avz71AN/GRH3oi/3KAx/kMnoUsD6q0woXlDT8lLEeViVKE7oZxE7RXzvO3T8kF2/sA==", + "requires": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + } + }, + "mdast-util-to-markdown": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.0.tgz", + "integrity": "sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ==", + "requires": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + } + }, + "mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "requires": { + "@types/mdast": "^4.0.0" + } + }, + "mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==" + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" + }, + "memfs": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", + "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", + "requires": { + "fs-monkey": "^1.0.4" + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" + }, + "mermaid": { + "version": "10.6.1", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-10.6.1.tgz", + "integrity": "sha512-Hky0/RpOw/1il9X8AvzOEChfJtVvmXm+y7JML5C//ePYMy0/9jCEmW1E1g86x9oDfW9+iVEdTV/i+M6KWRNs4A==", + "requires": { + "@braintree/sanitize-url": "^6.0.1", + "@types/d3-scale": "^4.0.3", + "@types/d3-scale-chromatic": "^3.0.0", + "cytoscape": "^3.23.0", + "cytoscape-cose-bilkent": "^4.1.0", + "cytoscape-fcose": "^2.1.0", + "d3": "^7.4.0", + "d3-sankey": "^0.12.3", + "dagre-d3-es": "7.0.10", + "dayjs": "^1.11.7", + "dompurify": "^3.0.5", + "elkjs": "^0.8.2", + "khroma": "^2.0.0", + "lodash-es": "^4.17.21", + "mdast-util-from-markdown": "^1.3.0", + "non-layered-tidy-tree-layout": "^2.0.2", + "stylis": "^4.1.3", + "ts-dedent": "^2.2.0", + "uuid": "^9.0.0", + "web-worker": "^1.2.0" + }, + "dependencies": { + "@types/mdast": { + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz", + "integrity": "sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==", + "requires": { + "@types/unist": "^2" + } + }, + "@types/unist": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz", + "integrity": "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==" + }, + "mdast-util-from-markdown": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-1.3.1.tgz", + "integrity": "sha512-4xTO/M8c82qBcnQc1tgpNtubGUW/Y1tBQ1B0i5CtSoelOLKFYlElIr3bvgREYYO5iRqbMY1YuqZng0GVOI8Qww==", + "requires": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "mdast-util-to-string": "^3.1.0", + "micromark": "^3.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-decode-string": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "unist-util-stringify-position": "^3.0.0", + "uvu": "^0.5.0" + } + }, + "mdast-util-to-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.2.0.tgz", + "integrity": "sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==", + "requires": { + "@types/mdast": "^3.0.0" + } + }, + "micromark": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.2.0.tgz", + "integrity": "sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA==", + "requires": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "micromark-core-commonmark": "^1.0.1", + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-combine-extensions": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-sanitize-uri": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "micromark-core-commonmark": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-1.1.0.tgz", + "integrity": "sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw==", + "requires": { + "decode-named-character-reference": "^1.0.0", + "micromark-factory-destination": "^1.0.0", + "micromark-factory-label": "^1.0.0", + "micromark-factory-space": "^1.0.0", + "micromark-factory-title": "^1.0.0", + "micromark-factory-whitespace": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-classify-character": "^1.0.0", + "micromark-util-html-tag-name": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "micromark-factory-destination": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-1.1.0.tgz", + "integrity": "sha512-XaNDROBgx9SgSChd69pjiGKbV+nfHGDPVYFs5dOoDd7ZnMAE+Cuu91BCpsY8RT2NP9vo/B8pds2VQNCLiu0zhg==", + "requires": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-factory-label": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-1.1.0.tgz", + "integrity": "sha512-OLtyez4vZo/1NjxGhcpDSbHQ+m0IIGnT8BoPamh+7jVlzLJBH98zzuCoUeMxvM6WsNeh8wx8cKvqLiPHEACn0w==", + "requires": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "micromark-factory-title": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-1.1.0.tgz", + "integrity": "sha512-J7n9R3vMmgjDOCY8NPw55jiyaQnH5kBdV2/UXCtZIpnHH3P6nHUKaH7XXEYuWwx/xUJcawa8plLBEjMPU24HzQ==", + "requires": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-factory-whitespace": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-1.1.0.tgz", + "integrity": "sha512-v2WlmiymVSp5oMg+1Q0N1Lxmt6pMhIHD457whWM7/GUlEks1hI9xj5w3zbc4uuMKXGisksZk8DzP2UyGbGqNsQ==", + "requires": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-util-chunked": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-1.1.0.tgz", + "integrity": "sha512-Ye01HXpkZPNcV6FiyoW2fGZDUw4Yc7vT0E9Sad83+bEDiCJ1uXu0S3mr8WLpsz3HaG3x2q0HM6CTuPdcZcluFQ==", + "requires": { + "micromark-util-symbol": "^1.0.0" + } + }, + "micromark-util-classify-character": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-1.1.0.tgz", + "integrity": "sha512-SL0wLxtKSnklKSUplok1WQFoGhUdWYKggKUiqhX+Swala+BtptGCu5iPRc+xvzJ4PXE/hwM3FNXsfEVgoZsWbw==", + "requires": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-util-combine-extensions": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.1.0.tgz", + "integrity": "sha512-Q20sp4mfNf9yEqDL50WwuWZHUrCO4fEyeDCnMGmG5Pr0Cz15Uo7KBs6jq+dq0EgX4DPwwrh9m0X+zPV1ypFvUA==", + "requires": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-util-decode-numeric-character-reference": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.1.0.tgz", + "integrity": "sha512-m9V0ExGv0jB1OT21mrWcuf4QhP46pH1KkfWy9ZEezqHKAxkj4mPCy3nIH1rkbdMlChLHX531eOrymlwyZIf2iw==", + "requires": { + "micromark-util-symbol": "^1.0.0" + } + }, + "micromark-util-decode-string": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-1.1.0.tgz", + "integrity": "sha512-YphLGCK8gM1tG1bd54azwyrQRjCFcmgj2S2GoJDNnh4vYtnL38JS8M4gpxzOPNyHdNEpheyWXCTnnTDY3N+NVQ==", + "requires": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + } + }, + "micromark-util-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-1.1.0.tgz", + "integrity": "sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw==" + }, + "micromark-util-html-tag-name": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.2.0.tgz", + "integrity": "sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q==" + }, + "micromark-util-normalize-identifier": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.1.0.tgz", + "integrity": "sha512-N+w5vhqrBihhjdpM8+5Xsxy71QWqGn7HYNUvch71iV2PM7+E3uWGox1Qp90loa1ephtCxG2ftRV/Conitc6P2Q==", + "requires": { + "micromark-util-symbol": "^1.0.0" + } + }, + "micromark-util-resolve-all": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-1.1.0.tgz", + "integrity": "sha512-b/G6BTMSg+bX+xVCshPTPyAu2tmA0E4X98NSR7eIbeC6ycCqCeE7wjfDIgzEbkzdEVJXRtOG4FbEm/uGbCRouA==", + "requires": { + "micromark-util-types": "^1.0.0" + } + }, + "micromark-util-sanitize-uri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.2.0.tgz", + "integrity": "sha512-QO4GXv0XZfWey4pYFndLUKEAktKkG5kZTdUNaTAkzbuJxn2tNBOr+QtxR2XpWaMhbImT2dPzyLrPXLlPhph34A==", + "requires": { + "micromark-util-character": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + } + }, + "micromark-util-subtokenize": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-1.1.0.tgz", + "integrity": "sha512-kUQHyzRoxvZO2PuLzMt2P/dwVsTiivCK8icYTeR+3WgbuPqfHgPPy7nFKbeqRivBvn/3N3GBiNC+JRTMSxEC7A==", + "requires": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "micromark-util-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz", + "integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==" + }, + "unist-util-stringify-position": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.3.tgz", + "integrity": "sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==", + "requires": { + "@types/unist": "^2.0.0" + } + }, + "uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==" + } + } + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" + }, + "micromark": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.0.tgz", + "integrity": "sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==", + "requires": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "dependencies": { + "micromark-factory-space": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", + "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", + "requires": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "requires": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" + } + } + }, + "micromark-core-commonmark": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.0.tgz", + "integrity": "sha512-jThOz/pVmAYUtkroV3D5c1osFXAMv9e0ypGDOIZuCeAe91/sD6BoE2Sjzt30yuXtwOYUmySOhMas/PVyh02itA==", + "requires": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "dependencies": { + "micromark-factory-space": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", + "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", + "requires": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "requires": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" + } + } + }, + "micromark-extension-directive": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-3.0.0.tgz", + "integrity": "sha512-61OI07qpQrERc+0wEysLHMvoiO3s2R56x5u7glHq2Yqq6EHbH4dW25G9GfDdGCDYqA21KE6DWgNSzxSwHc2hSg==", + "requires": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "parse-entities": "^4.0.0" + }, + "dependencies": { + "micromark-factory-space": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", + "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", + "requires": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "requires": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" + } + } + }, + "micromark-extension-frontmatter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-frontmatter/-/micromark-extension-frontmatter-2.0.0.tgz", + "integrity": "sha512-C4AkuM3dA58cgZha7zVnuVxBhDsbttIMiytjgsM2XbHAB2faRVaHRle40558FBN+DJcrLNCoqG5mlrpdU4cRtg==", + "requires": { + "fault": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "dependencies": { + "micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "requires": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" + } + } + }, + "micromark-extension-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", + "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", + "requires": { + "micromark-extension-gfm-autolink-literal": "^2.0.0", + "micromark-extension-gfm-footnote": "^2.0.0", + "micromark-extension-gfm-strikethrough": "^2.0.0", + "micromark-extension-gfm-table": "^2.0.0", + "micromark-extension-gfm-tagfilter": "^2.0.0", + "micromark-extension-gfm-task-list-item": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-extension-gfm-autolink-literal": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.0.0.tgz", + "integrity": "sha512-rTHfnpt/Q7dEAK1Y5ii0W8bhfJlVJFnJMHIPisfPK3gpVNuOP0VnRl96+YJ3RYWV/P4gFeQoGKNlT3RhuvpqAg==", + "requires": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "dependencies": { + "micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "requires": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" + } + } + }, + "micromark-extension-gfm-footnote": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.0.0.tgz", + "integrity": "sha512-6Rzu0CYRKDv3BfLAUnZsSlzx3ak6HAoI85KTiijuKIz5UxZxbUI+pD6oHgw+6UtQuiRwnGRhzMmPRv4smcz0fg==", + "requires": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "dependencies": { + "micromark-factory-space": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", + "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", + "requires": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "requires": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" + } + } + }, + "micromark-extension-gfm-strikethrough": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha512-c3BR1ClMp5fxxmwP6AoOY2fXO9U8uFMKs4ADD66ahLTNcwzSCyRVU4k7LPV5Nxo/VJiR4TdzxRQY2v3qIUceCw==", + "requires": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "dependencies": { + "micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" + } + } + }, + "micromark-extension-gfm-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.0.0.tgz", + "integrity": "sha512-PoHlhypg1ItIucOaHmKE8fbin3vTLpDOUg8KAr8gRCF1MOZI9Nquq2i/44wFvviM4WuxJzc3demT8Y3dkfvYrw==", + "requires": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "dependencies": { + "micromark-factory-space": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", + "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", + "requires": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "requires": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" + } + } + }, + "micromark-extension-gfm-tagfilter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", + "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", + "requires": { + "micromark-util-types": "^2.0.0" + } + }, + "micromark-extension-gfm-task-list-item": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.0.1.tgz", + "integrity": "sha512-cY5PzGcnULaN5O7T+cOzfMoHjBW7j+T9D2sucA5d/KbsBTPcYdebm9zUd9zzdgJGCwahV+/W78Z3nbulBYVbTw==", + "requires": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "dependencies": { + "micromark-factory-space": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", + "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", + "requires": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "requires": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" + } + } + }, + "micromark-extension-mdx-expression": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-3.0.0.tgz", + "integrity": "sha512-sI0nwhUDz97xyzqJAbHQhp5TfaxEvZZZ2JDqUo+7NvyIYG6BZ5CPPqj2ogUoPJlmXHBnyZUzISg9+oUmU6tUjQ==", + "requires": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-mdx-expression": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "dependencies": { + "micromark-factory-space": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", + "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", + "requires": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "requires": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" + } + } + }, + "micromark-extension-mdx-jsx": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-3.0.0.tgz", + "integrity": "sha512-uvhhss8OGuzR4/N17L1JwvmJIpPhAd8oByMawEKx6NVdBCbesjH4t+vjEp3ZXft9DwvlKSD07fCeI44/N0Vf2w==", + "requires": { + "@types/acorn": "^4.0.0", + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "micromark-factory-mdx-expression": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "dependencies": { + "micromark-factory-space": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", + "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", + "requires": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "requires": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" + } + } + }, + "micromark-extension-mdx-md": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-md/-/micromark-extension-mdx-md-2.0.0.tgz", + "integrity": "sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==", + "requires": { + "micromark-util-types": "^2.0.0" + } + }, + "micromark-extension-mdxjs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs/-/micromark-extension-mdxjs-3.0.0.tgz", + "integrity": "sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==", + "requires": { + "acorn": "^8.0.0", + "acorn-jsx": "^5.0.0", + "micromark-extension-mdx-expression": "^3.0.0", + "micromark-extension-mdx-jsx": "^3.0.0", + "micromark-extension-mdx-md": "^2.0.0", + "micromark-extension-mdxjs-esm": "^3.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-extension-mdxjs-esm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-3.0.0.tgz", + "integrity": "sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==", + "requires": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "dependencies": { + "micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "requires": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" + } + } + }, + "micromark-factory-destination": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.0.tgz", + "integrity": "sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==", + "requires": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "dependencies": { + "micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "requires": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" + } + } + }, + "micromark-factory-label": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.0.tgz", + "integrity": "sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==", + "requires": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "dependencies": { + "micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "requires": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" + } + } + }, + "micromark-factory-mdx-expression": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-2.0.1.tgz", + "integrity": "sha512-F0ccWIUHRLRrYp5TC9ZYXmZo+p2AM13ggbsW4T0b5CRKP8KHVRB8t4pwtBgTxtjRmwrK0Irwm7vs2JOZabHZfg==", + "requires": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "dependencies": { + "micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "requires": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" + } + } + }, + "micromark-factory-space": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-1.1.0.tgz", + "integrity": "sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ==", + "requires": { + "micromark-util-character": "^1.0.0", + "micromark-util-types": "^1.0.0" + }, + "dependencies": { + "micromark-util-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz", + "integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==" + } + } + }, + "micromark-factory-title": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.0.tgz", + "integrity": "sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A==", + "requires": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "dependencies": { + "micromark-factory-space": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", + "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", + "requires": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "requires": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" + } + } + }, + "micromark-factory-whitespace": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.0.tgz", + "integrity": "sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA==", + "requires": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "dependencies": { + "micromark-factory-space": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", + "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", + "requires": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "requires": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" + } + } + }, + "micromark-util-character": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-1.2.0.tgz", + "integrity": "sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg==", + "requires": { + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + }, + "dependencies": { + "micromark-util-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz", + "integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==" + } + } + }, + "micromark-util-chunked": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.0.tgz", + "integrity": "sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==", + "requires": { + "micromark-util-symbol": "^2.0.0" + }, + "dependencies": { + "micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" + } + } + }, + "micromark-util-classify-character": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.0.tgz", + "integrity": "sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw==", + "requires": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "dependencies": { + "micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "requires": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" + } + } + }, + "micromark-util-combine-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.0.tgz", + "integrity": "sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ==", + "requires": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-decode-numeric-character-reference": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.1.tgz", + "integrity": "sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ==", + "requires": { + "micromark-util-symbol": "^2.0.0" + }, + "dependencies": { + "micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" + } + } + }, + "micromark-util-decode-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.0.tgz", + "integrity": "sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA==", + "requires": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + }, + "dependencies": { + "micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "requires": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" + } + } + }, + "micromark-util-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz", + "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==" + }, + "micromark-util-events-to-acorn": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-2.0.2.tgz", + "integrity": "sha512-Fk+xmBrOv9QZnEDguL9OI9/NQQp6Hz4FuQ4YmCb/5V7+9eAh1s6AYSvL20kHkD67YIg7EpE54TiSlcsf3vyZgA==", + "requires": { + "@types/acorn": "^4.0.0", + "@types/estree": "^1.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "estree-util-visit": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "dependencies": { + "micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" + } + } + }, + "micromark-util-html-tag-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.0.tgz", + "integrity": "sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==" + }, + "micromark-util-normalize-identifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.0.tgz", + "integrity": "sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w==", + "requires": { + "micromark-util-symbol": "^2.0.0" + }, + "dependencies": { + "micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" + } + } + }, + "micromark-util-resolve-all": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.0.tgz", + "integrity": "sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA==", + "requires": { + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-sanitize-uri": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz", + "integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==", + "requires": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + }, + "dependencies": { + "micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "requires": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" + } + } + }, + "micromark-util-subtokenize": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.0.tgz", + "integrity": "sha512-vc93L1t+gpR3p8jxeVdaYlbV2jTYteDje19rNSS/H5dlhxUYll5Fy6vJ2cDwP8RnsXi818yGty1ayP55y3W6fg==", + "requires": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "dependencies": { + "micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" + } + } + }, + "micromark-util-symbol": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.1.0.tgz", + "integrity": "sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==" + }, + "micromark-util-types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.0.tgz", + "integrity": "sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==" + }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", + "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==" + }, + "mime-types": { + "version": "2.1.18", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", + "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", + "requires": { + "mime-db": "~1.33.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" + }, + "mimic-response": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", + "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==" + }, + "mini-css-extract-plugin": { + "version": "2.7.6", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.6.tgz", + "integrity": "sha512-Qk7HcgaPkGG6eD77mLvZS1nmxlao3j+9PkrT9Uc7HAE1id3F41+DdBRYRYkbyfNRGzm8/YWtzhw7nVPmwhqTQw==", + "requires": { + "schema-utils": "^4.0.0" + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" + }, + "mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==" + }, + "mrmime": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz", + "integrity": "sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==" + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "multicast-dns": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", + "requires": { + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" + } + }, + "nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==" + }, + "negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" + }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + }, + "next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" + }, + "no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "requires": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node-emoji": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.1.3.tgz", + "integrity": "sha512-E2WEOVsgs7O16zsURJ/eH8BqhF029wGpEOnv7Urwdo2wmQanOACwJQh0devF9D9RhoZru0+9JXIS0dBXIAz+lA==", + "requires": { + "@sindresorhus/is": "^4.6.0", + "char-regex": "^1.0.2", + "emojilib": "^2.4.0", + "skin-tone": "^2.0.0" + } + }, + "node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==" + }, + "node-releases": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==" + }, + "non-layered-tidy-tree-layout": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/non-layered-tidy-tree-layout/-/non-layered-tidy-tree-layout-2.0.2.tgz", + "integrity": "sha512-gkXMxRzUH+PB0ax9dUN0yYF0S25BqeAYqhgMaLUFmpXLEk7Fcu8f4emJuOAY0V8kjDICxROIKsTAKsV/v355xw==" + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==" + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "requires": { + "path-key": "^3.0.0" + } + }, + "nprogress": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz", + "integrity": "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==" + }, + "nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "requires": { + "boolbase": "^1.0.0" + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" + }, + "object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==" + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, + "object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + } + }, + "obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" + }, + "on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "requires": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + } + }, + "opener": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==" + }, + "outvariant": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.0.tgz", + "integrity": "sha512-AlWY719RF02ujitly7Kk/0QlV+pXGFDHrHf9O2OKqyqgBieaPOIeuSkL8sRK6j2WK+/ZAURq2kZsY0d8JapUiw==" + }, + "p-cancelable": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", + "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==" + }, + "p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "requires": { + "yocto-queue": "^1.0.0" + } + }, + "p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "requires": { + "p-limit": "^4.0.0" + } + }, + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "requires": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + }, + "package-json": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-8.1.1.tgz", + "integrity": "sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==", + "requires": { + "got": "^12.1.0", + "registry-auth-token": "^5.0.1", + "registry-url": "^6.0.0", + "semver": "^7.3.7" + } + }, + "param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "requires": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-entities": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.1.tgz", + "integrity": "sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==", + "requires": { + "@types/unist": "^2.0.0", + "character-entities": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "dependencies": { + "@types/unist": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz", + "integrity": "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==" + } + } + }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "parse-numeric-range": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/parse-numeric-range/-/parse-numeric-range-1.3.0.tgz", + "integrity": "sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ==" + }, + "parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "requires": { + "entities": "^4.4.0" + } + }, + "parse5-htmlparser2-tree-adapter": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", + "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", + "requires": { + "domhandler": "^5.0.2", + "parse5": "^7.0.0" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==" + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "requires": { + "isarray": "0.0.1" + } + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" + }, + "periscopic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", + "integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==", + "requires": { + "@types/estree": "^1.0.0", + "estree-walker": "^3.0.0", + "is-reference": "^3.0.0" + } + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" + }, + "pkg-dir": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-7.0.0.tgz", + "integrity": "sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==", + "requires": { + "find-up": "^6.3.0" + } + }, + "pkg-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "requires": { + "find-up": "^3.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==" + } + } + }, + "postcss": { + "version": "8.4.38", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", + "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "requires": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.2.0" + } + }, + "postcss-calc": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-9.0.1.tgz", + "integrity": "sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==", + "requires": { + "postcss-selector-parser": "^6.0.11", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-colormin": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-6.1.0.tgz", + "integrity": "sha512-x9yX7DOxeMAR+BgGVnNSAxmAj98NX/YxEMNFP+SDCEeNLb2r3i6Hh1ksMsnW8Ub5SLCpbescQqn9YEbE9554Sw==", + "requires": { + "browserslist": "^4.23.0", + "caniuse-api": "^3.0.0", + "colord": "^2.9.3", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-convert-values": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-6.1.0.tgz", + "integrity": "sha512-zx8IwP/ts9WvUM6NkVSkiU902QZL1bwPhaVaLynPtCsOTqp+ZKbNi+s6XJg3rfqpKGA/oc7Oxk5t8pOQJcwl/w==", + "requires": { + "browserslist": "^4.23.0", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-discard-comments": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-6.0.2.tgz", + "integrity": "sha512-65w/uIqhSBBfQmYnG92FO1mWZjJ4GL5b8atm5Yw2UgrwD7HiNiSSNwJor1eCFGzUgYnN/iIknhNRVqjrrpuglw==", + "requires": {} + }, + "postcss-discard-duplicates": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-6.0.3.tgz", + "integrity": "sha512-+JA0DCvc5XvFAxwx6f/e68gQu/7Z9ud584VLmcgto28eB8FqSFZwtrLwB5Kcp70eIoWP/HXqz4wpo8rD8gpsTw==", + "requires": {} + }, + "postcss-discard-empty": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-6.0.3.tgz", + "integrity": "sha512-znyno9cHKQsK6PtxL5D19Fj9uwSzC2mB74cpT66fhgOadEUPyXFkbgwm5tvc3bt3NAy8ltE5MrghxovZRVnOjQ==", + "requires": {} + }, + "postcss-discard-overridden": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-6.0.2.tgz", + "integrity": "sha512-j87xzI4LUggC5zND7KdjsI25APtyMuynXZSujByMaav2roV6OZX+8AaCUcZSWqckZpjAjRyFDdpqybgjFO0HJQ==", + "requires": {} + }, + "postcss-discard-unused": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-6.0.5.tgz", + "integrity": "sha512-wHalBlRHkaNnNwfC8z+ppX57VhvS+HWgjW508esjdaEYr3Mx7Gnn2xA4R/CKf5+Z9S5qsqC+Uzh4ueENWwCVUA==", + "requires": { + "postcss-selector-parser": "^6.0.16" + } + }, + "postcss-loader": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.3.3.tgz", + "integrity": "sha512-YgO/yhtevGO/vJePCQmTxiaEwER94LABZN0ZMT4A0vsak9TpO+RvKRs7EmJ8peIlB9xfXCsS7M8LjqncsUZ5HA==", + "requires": { + "cosmiconfig": "^8.2.0", + "jiti": "^1.18.2", + "semver": "^7.3.8" + } + }, + "postcss-merge-idents": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-6.0.3.tgz", + "integrity": "sha512-1oIoAsODUs6IHQZkLQGO15uGEbK3EAl5wi9SS8hs45VgsxQfMnxvt+L+zIr7ifZFIH14cfAeVe2uCTa+SPRa3g==", + "requires": { + "cssnano-utils": "^4.0.2", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-merge-longhand": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-6.0.5.tgz", + "integrity": "sha512-5LOiordeTfi64QhICp07nzzuTDjNSO8g5Ksdibt44d+uvIIAE1oZdRn8y/W5ZtYgRH/lnLDlvi9F8btZcVzu3w==", + "requires": { + "postcss-value-parser": "^4.2.0", + "stylehacks": "^6.1.1" + } + }, + "postcss-merge-rules": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-6.1.1.tgz", + "integrity": "sha512-KOdWF0gju31AQPZiD+2Ar9Qjowz1LTChSjFFbS+e2sFgc4uHOp3ZvVX4sNeTlk0w2O31ecFGgrFzhO0RSWbWwQ==", + "requires": { + "browserslist": "^4.23.0", + "caniuse-api": "^3.0.0", + "cssnano-utils": "^4.0.2", + "postcss-selector-parser": "^6.0.16" + } + }, + "postcss-minify-font-values": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-6.1.0.tgz", + "integrity": "sha512-gklfI/n+9rTh8nYaSJXlCo3nOKqMNkxuGpTn/Qm0gstL3ywTr9/WRKznE+oy6fvfolH6dF+QM4nCo8yPLdvGJg==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-minify-gradients": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-6.0.3.tgz", + "integrity": "sha512-4KXAHrYlzF0Rr7uc4VrfwDJ2ajrtNEpNEuLxFgwkhFZ56/7gaE4Nr49nLsQDZyUe+ds+kEhf+YAUolJiYXF8+Q==", + "requires": { + "colord": "^2.9.3", + "cssnano-utils": "^4.0.2", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-minify-params": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-6.1.0.tgz", + "integrity": "sha512-bmSKnDtyyE8ujHQK0RQJDIKhQ20Jq1LYiez54WiaOoBtcSuflfK3Nm596LvbtlFcpipMjgClQGyGr7GAs+H1uA==", + "requires": { + "browserslist": "^4.23.0", + "cssnano-utils": "^4.0.2", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-minify-selectors": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-6.0.4.tgz", + "integrity": "sha512-L8dZSwNLgK7pjTto9PzWRoMbnLq5vsZSTu8+j1P/2GB8qdtGQfn+K1uSvFgYvgh83cbyxT5m43ZZhUMTJDSClQ==", + "requires": { + "postcss-selector-parser": "^6.0.16" + } + }, + "postcss-modules-extract-imports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "requires": {} + }, + "postcss-modules-local-by-default": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.3.tgz", + "integrity": "sha512-2/u2zraspoACtrbFRnTijMiQtb4GW4BvatjaG/bCjYQo8kLTdevCUlwuBHx2sCnSyrI3x3qj4ZK1j5LQBgzmwA==", + "requires": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-modules-scope": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", + "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "requires": { + "postcss-selector-parser": "^6.0.4" + } + }, + "postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "requires": { + "icss-utils": "^5.0.0" + } + }, + "postcss-normalize-charset": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-6.0.2.tgz", + "integrity": "sha512-a8N9czmdnrjPHa3DeFlwqst5eaL5W8jYu3EBbTTkI5FHkfMhFZh1EGbku6jhHhIzTA6tquI2P42NtZ59M/H/kQ==", + "requires": {} + }, + "postcss-normalize-display-values": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-6.0.2.tgz", + "integrity": "sha512-8H04Mxsb82ON/aAkPeq8kcBbAtI5Q2a64X/mnRRfPXBq7XeogoQvReqxEfc0B4WPq1KimjezNC8flUtC3Qz6jg==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-positions": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-6.0.2.tgz", + "integrity": "sha512-/JFzI441OAB9O7VnLA+RtSNZvQ0NCFZDOtp6QPFo1iIyawyXg0YI3CYM9HBy1WvwCRHnPep/BvI1+dGPKoXx/Q==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-repeat-style": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-6.0.2.tgz", + "integrity": "sha512-YdCgsfHkJ2jEXwR4RR3Tm/iOxSfdRt7jplS6XRh9Js9PyCR/aka/FCb6TuHT2U8gQubbm/mPmF6L7FY9d79VwQ==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-string": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-6.0.2.tgz", + "integrity": "sha512-vQZIivlxlfqqMp4L9PZsFE4YUkWniziKjQWUtsxUiVsSSPelQydwS8Wwcuw0+83ZjPWNTl02oxlIvXsmmG+CiQ==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-timing-functions": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-6.0.2.tgz", + "integrity": "sha512-a+YrtMox4TBtId/AEwbA03VcJgtyW4dGBizPl7e88cTFULYsprgHWTbfyjSLyHeBcK/Q9JhXkt2ZXiwaVHoMzA==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-unicode": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-6.1.0.tgz", + "integrity": "sha512-QVC5TQHsVj33otj8/JD869Ndr5Xcc/+fwRh4HAsFsAeygQQXm+0PySrKbr/8tkDKzW+EVT3QkqZMfFrGiossDg==", + "requires": { + "browserslist": "^4.23.0", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-url": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-6.0.2.tgz", + "integrity": "sha512-kVNcWhCeKAzZ8B4pv/DnrU1wNh458zBNp8dh4y5hhxih5RZQ12QWMuQrDgPRw3LRl8mN9vOVfHl7uhvHYMoXsQ==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-whitespace": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-6.0.2.tgz", + "integrity": "sha512-sXZ2Nj1icbJOKmdjXVT9pnyHQKiSAyuNQHSgRCUgThn2388Y9cGVDR+E9J9iAYbSbLHI+UUwLVl1Wzco/zgv0Q==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-ordered-values": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-6.0.2.tgz", + "integrity": "sha512-VRZSOB+JU32RsEAQrO94QPkClGPKJEL/Z9PCBImXMhIeK5KAYo6slP/hBYlLgrCjFxyqvn5VC81tycFEDBLG1Q==", + "requires": { + "cssnano-utils": "^4.0.2", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-reduce-idents": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-6.0.3.tgz", + "integrity": "sha512-G3yCqZDpsNPoQgbDUy3T0E6hqOQ5xigUtBQyrmq3tn2GxlyiL0yyl7H+T8ulQR6kOcHJ9t7/9H4/R2tv8tJbMA==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-reduce-initial": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-6.1.0.tgz", + "integrity": "sha512-RarLgBK/CrL1qZags04oKbVbrrVK2wcxhvta3GCxrZO4zveibqbRPmm2VI8sSgCXwoUHEliRSbOfpR0b/VIoiw==", + "requires": { + "browserslist": "^4.23.0", + "caniuse-api": "^3.0.0" + } + }, + "postcss-reduce-transforms": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-6.0.2.tgz", + "integrity": "sha512-sB+Ya++3Xj1WaT9+5LOOdirAxP7dJZms3GRcYheSPi1PiTMigsxHAdkrbItHxwYHr4kt1zL7mmcHstgMYT+aiA==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-selector-parser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.0.tgz", + "integrity": "sha512-UMz42UD0UY0EApS0ZL9o1XnLhSTtvvvLe5Dc2H2O56fvRZi+KulDyf5ctDhhtYJBGKStV2FL1fy6253cmLgqVQ==", + "requires": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + } + }, + "postcss-sort-media-queries": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/postcss-sort-media-queries/-/postcss-sort-media-queries-5.2.0.tgz", + "integrity": "sha512-AZ5fDMLD8SldlAYlvi8NIqo0+Z8xnXU2ia0jxmuhxAU+Lqt9K+AlmLNJ/zWEnE9x+Zx3qL3+1K20ATgNOr3fAA==", + "requires": { + "sort-css-media-queries": "2.2.0" + } + }, + "postcss-svgo": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-6.0.3.tgz", + "integrity": "sha512-dlrahRmxP22bX6iKEjOM+c8/1p+81asjKT+V5lrgOH944ryx/OHpclnIbGsKVd3uWOXFLYJwCVf0eEkJGvO96g==", + "requires": { + "postcss-value-parser": "^4.2.0", + "svgo": "^3.2.0" + } + }, + "postcss-unique-selectors": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-6.0.4.tgz", + "integrity": "sha512-K38OCaIrO8+PzpArzkLKB42dSARtC2tmG6PvD4b1o1Q2E9Os8jzfWFfSy/rixsHwohtsDdFtAWGjFVFUdwYaMg==", + "requires": { + "postcss-selector-parser": "^6.0.16" + } + }, + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "postcss-zindex": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-6.0.2.tgz", + "integrity": "sha512-5BxW9l1evPB/4ZIc+2GobEBoKC+h8gPGCMi+jxsYvd2x0mjq7wazk6DrP71pStqxE9Foxh5TVnonbWpFZzXaYg==", + "requires": {} + }, + "prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true + }, + "pretty-error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", + "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", + "requires": { + "lodash": "^4.17.20", + "renderkid": "^3.0.0" + } + }, + "pretty-time": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pretty-time/-/pretty-time-1.1.0.tgz", + "integrity": "sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==" + }, + "prism-react-renderer": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-2.3.0.tgz", + "integrity": "sha512-UYRg2TkVIaI6tRVHC5OJ4/BxqPUxJkJvq/odLT/ykpt1zGYXooNperUxQcCvi87LyRnR4nCh81ceOA+e7nrydg==", + "requires": { + "@types/prismjs": "^1.26.0", + "clsx": "^2.0.0" + } + }, + "prismjs": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", + "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==" + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + } + }, + "prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + }, + "dependencies": { + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + } + } + }, + "property-information": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.4.1.tgz", + "integrity": "sha512-OHYtXfu5aI2sS2LWFSN5rgJjrQ4pCy8i1jubJLe2QvMF8JJ++HXTUIVWFLfXJoaOfvYYjk2SN8J2wFUWIGXT4w==" + }, + "proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==" + }, + "proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "dependencies": { + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + } + } + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==" + }, + "pupa": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-3.1.0.tgz", + "integrity": "sha512-FLpr4flz5xZTSJxSeaheeMKN/EDzMdK7b8PTOC6a5PYFKTucWbdqjgqaEyH0shFiSJrVB1+Qqi4Tk19ccU6Aug==", + "requires": { + "escape-goat": "^4.0.0" + } + }, + "qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "requires": { + "side-channel": "^1.0.4" + } + }, + "queue": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", + "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", + "requires": { + "inherits": "~2.0.3" + } + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" + }, + "quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==" + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==" + }, + "raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "requires": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "dependencies": { + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" + } + } + }, + "raw-loader": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-4.0.2.tgz", + "integrity": "sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA==", + "requires": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "requires": {} + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==" + } + } + }, + "react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "requires": { + "loose-envify": "^1.1.0" + } + }, + "react-dev-utils": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", + "integrity": "sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==", + "requires": { + "@babel/code-frame": "^7.16.0", + "address": "^1.1.2", + "browserslist": "^4.18.1", + "chalk": "^4.1.2", + "cross-spawn": "^7.0.3", + "detect-port-alt": "^1.1.6", + "escape-string-regexp": "^4.0.0", + "filesize": "^8.0.6", + "find-up": "^5.0.0", + "fork-ts-checker-webpack-plugin": "^6.5.0", + "global-modules": "^2.0.0", + "globby": "^11.0.4", + "gzip-size": "^6.0.0", + "immer": "^9.0.7", + "is-root": "^2.1.0", + "loader-utils": "^3.2.0", + "open": "^8.4.0", + "pkg-up": "^3.1.0", + "prompts": "^2.4.2", + "react-error-overlay": "^6.0.11", + "recursive-readdir": "^2.2.2", + "shell-quote": "^1.7.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "dependencies": { + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "loader-utils": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz", + "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==" + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "requires": { + "p-locate": "^5.0.0" + } + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "requires": { + "p-limit": "^3.0.2" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" + } + } + }, + "react-devtools-inline": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/react-devtools-inline/-/react-devtools-inline-4.4.0.tgz", + "integrity": "sha512-ES0GolSrKO8wsKbsEkVeiR/ZAaHQTY4zDh1UW8DImVmm8oaGLl3ijJDvSGe+qDRKPZdPRnDtWWnSvvrgxXdThQ==", + "requires": { + "es6-symbol": "^3" + } + }, + "react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "requires": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + } + }, + "react-error-overlay": { + "version": "6.0.11", + "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", + "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" + }, + "react-fast-compare": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", + "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==" + }, + "react-helmet-async": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-1.3.0.tgz", + "integrity": "sha512-9jZ57/dAn9t3q6hneQS0wukqC2ENOBgMNVEhb/ZG9ZSxUetzVIw4iAmEU38IaVg3QGYauQPhSeUTuIUtFglWpg==", + "requires": { + "@babel/runtime": "^7.12.5", + "invariant": "^2.2.4", + "prop-types": "^15.7.2", + "react-fast-compare": "^3.2.0", + "shallowequal": "^1.1.0" + } + }, + "react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + }, + "react-json-view-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/react-json-view-lite/-/react-json-view-lite-1.4.0.tgz", + "integrity": "sha512-wh6F6uJyYAmQ4fK0e8dSQMEWuvTs2Wr3el3sLD9bambX1+pSWUVXIz1RFaoy3TI1mZ0FqdpKq9YgbgTTgyrmXA==", + "requires": {} + }, + "react-loadable": { + "version": "npm:@docusaurus/react-loadable@6.0.0", + "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-6.0.0.tgz", + "integrity": "sha512-YMMxTUQV/QFSnbgrP3tjDzLHRg7vsbMn8e9HAa8o/1iXoiomo48b7sk/kkmWEuWNDPJVlKSJRB6Y2fHqdJk+SQ==", + "requires": { + "@types/react": "*" + } + }, + "react-loadable-ssr-addon-v5-slorber": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/react-loadable-ssr-addon-v5-slorber/-/react-loadable-ssr-addon-v5-slorber-1.0.1.tgz", + "integrity": "sha512-lq3Lyw1lGku8zUEJPDxsNm1AfYHBrO9Y1+olAYwpUJ2IGFBskM0DMKok97A6LWUpHm+o7IvQBOWu9MLenp9Z+A==", + "requires": { + "@babel/runtime": "^7.10.3" + } + }, + "react-router": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.3.4.tgz", + "integrity": "sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA==", + "requires": { + "@babel/runtime": "^7.12.13", + "history": "^4.9.0", + "hoist-non-react-statics": "^3.1.0", + "loose-envify": "^1.3.1", + "path-to-regexp": "^1.7.0", + "prop-types": "^15.6.2", + "react-is": "^16.6.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0" + }, + "dependencies": { + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + } + } + }, + "react-router-config": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/react-router-config/-/react-router-config-5.1.1.tgz", + "integrity": "sha512-DuanZjaD8mQp1ppHjgnnUnyOlqYXZVjnov/JzFhjLEwd3Z4dYjMSnqrEzzGThH47vpCOqPPwJM2FtthLeJ8Pbg==", + "requires": { + "@babel/runtime": "^7.1.2" + } + }, + "react-router-dom": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.3.4.tgz", + "integrity": "sha512-m4EqFMHv/Ih4kpcBCONHbkT68KoAeHN4p3lAGoNryfHi0dMy0kCzEZakiKRsvg5wHZ/JLrLW8o8KomWiz/qbYQ==", + "requires": { + "@babel/runtime": "^7.12.13", + "history": "^4.9.0", + "loose-envify": "^1.3.1", + "prop-types": "^15.6.2", + "react-router": "5.3.4", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0" + } + }, + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "requires": { + "picomatch": "^2.2.1" + } + }, + "reading-time": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/reading-time/-/reading-time-1.5.0.tgz", + "integrity": "sha512-onYyVhBNr4CmAxFsKS7bz+uTLRakypIe4R+5A824vBSkQy/hB3fZepoVEf8OVAxzLvK+H/jm9TzpI3ETSm64Kg==" + }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", + "requires": { + "resolve": "^1.1.6" + } + }, + "recursive-readdir": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", + "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", + "requires": { + "minimatch": "^3.0.5" + } + }, + "regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" + }, + "regenerate-unicode-properties": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz", + "integrity": "sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==", + "requires": { + "regenerate": "^1.4.2" + } + }, + "regenerator-runtime": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", + "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" + }, + "regenerator-transform": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", + "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", + "requires": { + "@babel/runtime": "^7.8.4" + } + }, + "regexpu-core": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", + "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", + "requires": { + "@babel/regjsgen": "^0.8.0", + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.1.0", + "regjsparser": "^0.9.1", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" + } + }, + "registry-auth-token": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.0.2.tgz", + "integrity": "sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ==", + "requires": { + "@pnpm/npm-conf": "^2.1.0" + } + }, + "registry-url": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-6.0.1.tgz", + "integrity": "sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==", + "requires": { + "rc": "1.2.8" + } + }, + "regjsparser": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", + "requires": { + "jsesc": "~0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==" + } + } + }, + "rehype-raw": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz", + "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==", + "requires": { + "@types/hast": "^3.0.0", + "hast-util-raw": "^9.0.0", + "vfile": "^6.0.0" + } + }, + "relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==" + }, + "remark-directive": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/remark-directive/-/remark-directive-3.0.0.tgz", + "integrity": "sha512-l1UyWJ6Eg1VPU7Hm/9tt0zKtReJQNOA4+iDMAxTyZNWnJnFlbS/7zhiel/rogTLQ2vMYwDzSJa4BiVNqGlqIMA==", + "requires": { + "@types/mdast": "^4.0.0", + "mdast-util-directive": "^3.0.0", + "micromark-extension-directive": "^3.0.0", + "unified": "^11.0.0" + } + }, + "remark-emoji": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/remark-emoji/-/remark-emoji-4.0.1.tgz", + "integrity": "sha512-fHdvsTR1dHkWKev9eNyhTo4EFwbUvJ8ka9SgeWkMPYFX4WoI7ViVBms3PjlQYgw5TLvNQso3GUB/b/8t3yo+dg==", + "requires": { + "@types/mdast": "^4.0.2", + "emoticon": "^4.0.1", + "mdast-util-find-and-replace": "^3.0.1", + "node-emoji": "^2.1.0", + "unified": "^11.0.4" + } + }, + "remark-frontmatter": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/remark-frontmatter/-/remark-frontmatter-5.0.0.tgz", + "integrity": "sha512-XTFYvNASMe5iPN0719nPrdItC9aU0ssC4v14mH1BCi1u0n1gAocqcujWUrByftZTbLhRtiKRyjYTSIOcr69UVQ==", + "requires": { + "@types/mdast": "^4.0.0", + "mdast-util-frontmatter": "^2.0.0", + "micromark-extension-frontmatter": "^2.0.0", + "unified": "^11.0.0" + } + }, + "remark-gfm": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.0.tgz", + "integrity": "sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA==", + "requires": { + "@types/mdast": "^4.0.0", + "mdast-util-gfm": "^3.0.0", + "micromark-extension-gfm": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "unified": "^11.0.0" + } + }, + "remark-mdx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-3.0.1.tgz", + "integrity": "sha512-3Pz3yPQ5Rht2pM5R+0J2MrGoBSrzf+tJG94N+t/ilfdh8YLyyKYtidAYwTveB20BoHAcwIopOUqhcmh2F7hGYA==", + "requires": { + "mdast-util-mdx": "^3.0.0", + "micromark-extension-mdxjs": "^3.0.0" + } + }, + "remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "requires": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + } + }, + "remark-rehype": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.0.tgz", + "integrity": "sha512-z3tJrAs2kIs1AqIIy6pzHmAHlF1hWQ+OdY4/hv+Wxe35EhyLKcajL33iUEn3ScxtFox9nUvRufR/Zre8Q08H/g==", + "requires": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + } + }, + "remark-stringify": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", + "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", + "requires": { + "@types/mdast": "^4.0.0", + "mdast-util-to-markdown": "^2.0.0", + "unified": "^11.0.0" + } + }, + "renderkid": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", + "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", + "requires": { + "css-select": "^4.1.3", + "dom-converter": "^0.2.0", + "htmlparser2": "^6.1.0", + "lodash": "^4.17.21", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "requires": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + } + }, + "dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + } + }, + "domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "requires": { + "domelementtype": "^2.2.0" + } + }, + "domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + } + }, + "entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" + }, + "htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + } + } + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" + }, + "require-like": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz", + "integrity": "sha512-oyrU88skkMtDdauHDuKVrgR+zuItqr6/c//FXzvmxRGMexSDc6hNvJInGW3LL46n+8b50RykrvwSUIIQH2LQ5A==" + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" + }, + "resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "requires": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==" + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" + }, + "resolve-pathname": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz", + "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==" + }, + "responselike": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", + "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", + "requires": { + "lowercase-keys": "^3.0.0" + } + }, + "retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==" + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "requires": { + "glob": "^7.1.3" + } + }, + "robust-predicates": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", + "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==" + }, + "rtl-detect": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/rtl-detect/-/rtl-detect-1.1.2.tgz", + "integrity": "sha512-PGMBq03+TTG/p/cRB7HCLKJ1MgDIi07+QU1faSjiYRfmY5UsAttV9Hs08jDAHVwcOwmVLcSJkpwyfXszVjWfIQ==" + }, + "rtlcss": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-4.1.1.tgz", + "integrity": "sha512-/oVHgBtnPNcggP2aVXQjSy6N1mMAfHg4GSag0QtZBlD5bdDgAHwr4pydqJGd+SUCu9260+Pjqbjwtvu7EMH1KQ==", + "requires": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0", + "postcss": "^8.4.21", + "strip-json-comments": "^3.1.1" + } + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" + }, + "sade": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "requires": { + "mri": "^1.1.0" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sass": { + "version": "1.77.6", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.6.tgz", + "integrity": "sha512-ByXE1oLD79GVq9Ht1PeHWCPMPB8XHpBuz1r85oByKHjZY6qV6rWnQovQzXJXuQ/XyE1Oj3iPk3lo28uzaRA2/Q==", + "requires": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + } + }, + "sass-loader": { + "version": "10.5.2", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-10.5.2.tgz", + "integrity": "sha512-vMUoSNOUKJILHpcNCCyD23X34gve1TS7Rjd9uXHeKqhvBG39x6XbswFDtpbTElj6XdMFezoWhkh5vtKudf2cgQ==", + "requires": { + "klona": "^2.0.4", + "loader-utils": "^2.0.0", + "neo-async": "^2.6.2", + "schema-utils": "^3.0.0", + "semver": "^7.3.2" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "requires": {} + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "sax": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==" + }, + "scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "requires": { + "loose-envify": "^1.1.0" + } + }, + "schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + } + }, + "search-insights": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.14.0.tgz", + "integrity": "sha512-OLN6MsPMCghDOqlCtsIsYgtsC0pnwVTyT9Mu6A3ewOj1DxvzZF6COrn2g86E/c05xbktB0XN04m/t1Z+n+fTGw==", + "peer": true + }, + "section-matter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", + "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", + "requires": { + "extend-shallow": "^2.0.1", + "kind-of": "^6.0.0" + } + }, + "select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==" + }, + "selfsigned": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", + "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", + "requires": { + "@types/node-forge": "^1.3.0", + "node-forge": "^1" + } + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "requires": { + "lru-cache": "^6.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } + }, + "semver-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-4.0.0.tgz", + "integrity": "sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==", + "requires": { + "semver": "^7.3.5" + } + }, + "send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "requires": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + } + } + }, + "serialize-javascript": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", + "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", + "requires": { + "randombytes": "^2.1.0" + } + }, + "serve-handler": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.5.tgz", + "integrity": "sha512-ijPFle6Hwe8zfmBxJdE+5fta53fdIY0lHISJvuikXB3VYFafRjMRpOffSPvCYsbKyBA7pvy9oYr/BT1O3EArlg==", + "requires": { + "bytes": "3.0.0", + "content-disposition": "0.5.2", + "fast-url-parser": "1.1.3", + "mime-types": "2.1.18", + "minimatch": "3.1.2", + "path-is-inside": "1.0.2", + "path-to-regexp": "2.2.1", + "range-parser": "1.2.0" + }, + "dependencies": { + "path-to-regexp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.2.1.tgz", + "integrity": "sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ==" + } + } + }, + "serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "requires": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==" + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==" + } + } + }, + "serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + } + }, + "set-function-length": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", + "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "requires": { + "define-data-property": "^1.1.1", + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + } + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "requires": { + "kind-of": "^6.0.2" + } + }, + "shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + }, + "shell-quote": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", + "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==" + }, + "shelljs": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", + "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", + "requires": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + } + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "sirv": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.3.tgz", + "integrity": "sha512-O9jm9BsID1P+0HOi81VpXPoDxYP374pkOLzACAoyUQ/3OUVndNpsz6wMnY2z+yOxzbllCKZrM+9QrWsv4THnyA==", + "requires": { + "@polka/url": "^1.0.0-next.20", + "mrmime": "^1.0.0", + "totalist": "^3.0.0" + } + }, + "sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" + }, + "sitemap": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-7.1.2.tgz", + "integrity": "sha512-ARCqzHJ0p4gWt+j7NlU5eDlIO9+Rkr/JhPFZKKQ1l5GCus7rJH4UdrlVAh0xC/gDS/Qir2UMxqYNHtsKr2rpCw==", + "requires": { + "@types/node": "^17.0.5", + "@types/sax": "^1.2.1", + "arg": "^5.0.0", + "sax": "^1.2.4" + }, + "dependencies": { + "@types/node": { + "version": "17.0.45", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", + "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==" + } + } + }, + "skin-tone": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/skin-tone/-/skin-tone-2.0.0.tgz", + "integrity": "sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==", + "requires": { + "unicode-emoji-modifier-base": "^1.0.0" + } + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" + }, + "snake-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "requires": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "requires": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "sort-css-media-queries": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/sort-css-media-queries/-/sort-css-media-queries-2.2.0.tgz", + "integrity": "sha512-0xtkGhWCC9MGt/EzgnvbbbKhqWjl1+/rncmhTh5qCpbYguXh6S/qwePfv/JQ8jePXXmqingylxoC49pCkSPIbA==" + }, + "source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==" + }, + "source-map-js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==" + }, + "source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==" + }, + "spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "requires": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + } + }, + "spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "requires": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + }, + "srcset": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/srcset/-/srcset-4.0.0.tgz", + "integrity": "sha512-wvLeHgcVHKO8Sc/H/5lkGreJQVeYMm9rlmt8PuR1xE31rIuXhuzznUUqAt8MqLhB3MqJdFzlNAfpcWnxiFUcPw==" + }, + "static-browser-server": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/static-browser-server/-/static-browser-server-1.0.3.tgz", + "integrity": "sha512-ZUyfgGDdFRbZGGJQ1YhiM930Yczz5VlbJObrQLlk24+qNHVQx4OlLcYswEUo3bIyNAbQUIUR9Yr5/Hqjzqb4zA==", + "requires": { + "@open-draft/deferred-promise": "^2.1.0", + "dotenv": "^16.0.3", + "mime-db": "^1.52.0", + "outvariant": "^1.3.0" + }, + "dependencies": { + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + } + } + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" + }, + "std-env": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.4.3.tgz", + "integrity": "sha512-f9aPhy8fYBuMN+sNfakZV18U39PbalgjXG3lLB9WkaYTxijru61wb57V9wxxNthXM5Sd88ETBWi29qLAsHO52Q==" + }, + "strict-event-emitter": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.4.6.tgz", + "integrity": "sha512-12KWeb+wixJohmnwNFerbyiBrAlq5qJLwIt38etRtKtmmHyDSoGlIqFE9wx+4IwG0aDjI7GV8tc8ZccjWZZtTg==" + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==" + }, + "strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "requires": { + "ansi-regex": "^6.0.1" + } + } + } + }, + "stringify-entities": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.3.tgz", + "integrity": "sha512-BP9nNHMhhfcMbiuQKCqMjhDP5yBCAxsPu4pHFFzJ6Alo9dZgY4VLDPutXqIjpRiMoKdp7Av85Gr73Q5uH9k7+g==", + "requires": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + } + }, + "stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "requires": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-bom-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", + "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==" + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==" + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" + }, + "style-mod": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.0.tgz", + "integrity": "sha512-Ca5ib8HrFn+f+0n4N4ScTIA9iTOQ7MaGS1ylHcoVqW9J7w2w8PzN6g9gKmTYgGEBH8e120+RCmhpje6jC5uGWA==" + }, + "style-to-object": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.4.4.tgz", + "integrity": "sha512-HYNoHZa2GorYNyqiCaBgsxvcJIn7OHq6inEga+E6Ke3m5JkoqpQbnFssk4jwe+K7AhGa2fcha4wSOf1Kn01dMg==", + "requires": { + "inline-style-parser": "0.1.1" + } + }, + "stylehacks": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-6.1.1.tgz", + "integrity": "sha512-gSTTEQ670cJNoaeIp9KX6lZmm8LJ3jPB5yJmX8Zq/wQxOsAFXV3qjWzHas3YYk1qesuVIyYWWUpZ0vSE/dTSGg==", + "requires": { + "browserslist": "^4.23.0", + "postcss-selector-parser": "^6.0.16" + } + }, + "stylis": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.0.tgz", + "integrity": "sha512-E87pIogpwUsUwXw7dNyU4QDjdgVMy52m+XEOPEKUn161cCzWjjhPSQhByfd1CcNvrOLnXQ6OnnZDwnJrz/Z4YQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" + }, + "svg-parser": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", + "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==" + }, + "svgo": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz", + "integrity": "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==", + "requires": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^5.1.0", + "css-tree": "^2.3.1", + "css-what": "^6.1.0", + "csso": "^5.0.5", + "picocolors": "^1.0.0" + }, + "dependencies": { + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==" + } + } + }, + "tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==" + }, + "terser": { + "version": "5.24.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.24.0.tgz", + "integrity": "sha512-ZpGR4Hy3+wBEzVEnHvstMvqpD/nABNelQn/z2r0fjVWGQsN3bpOLzQlqDxmb4CDZnXq5lpjnQ+mHQLAOpfM5iw==", + "requires": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + } + } + }, + "terser-webpack-plugin": { + "version": "5.3.9", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz", + "integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==", + "requires": { + "@jridgewell/trace-mapping": "^0.3.17", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.1", + "terser": "^5.16.8" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "requires": {} + }, + "jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" + }, + "thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" + }, + "tiny-invariant": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", + "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==" + }, + "tiny-warning": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==" + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "requires": { + "is-number": "^7.0.0" + } + }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" + }, + "totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==" + }, + "trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==" + }, + "trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==" + }, + "ts-dedent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", + "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==" + }, + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" + }, + "type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==" + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "dependencies": { + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + } + } + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "typescript": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==" + }, + "undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, + "unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==" + }, + "unicode-emoji-modifier-base": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unicode-emoji-modifier-base/-/unicode-emoji-modifier-base-1.0.0.tgz", + "integrity": "sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==" + }, + "unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "requires": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + } + }, + "unicode-match-property-value-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", + "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==" + }, + "unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==" + }, + "unified": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.4.tgz", + "integrity": "sha512-apMPnyLjAX+ty4OrNap7yumyVAMlKx5IWU2wlzzUdYJO9A8f1p9m/gywF/GM2ZDFcjQPrx59Mc90KwmxsoklxQ==", + "requires": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + } + }, + "unique-string": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz", + "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==", + "requires": { + "crypto-random-string": "^4.0.0" + } + }, + "unist-util-is": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "requires": { + "@types/unist": "^3.0.0" + } + }, + "unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "requires": { + "@types/unist": "^3.0.0" + } + }, + "unist-util-position-from-estree": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position-from-estree/-/unist-util-position-from-estree-2.0.0.tgz", + "integrity": "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==", + "requires": { + "@types/unist": "^3.0.0" + } + }, + "unist-util-remove-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz", + "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==", + "requires": { + "@types/unist": "^3.0.0", + "unist-util-visit": "^5.0.0" + } + }, + "unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "requires": { + "@types/unist": "^3.0.0" + } + }, + "unist-util-visit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "requires": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + } + }, + "unist-util-visit-parents": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", + "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", + "requires": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + } + }, + "universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==" + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" + }, + "update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "requires": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + } + }, + "update-notifier": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-6.0.2.tgz", + "integrity": "sha512-EDxhTEVPZZRLWYcJ4ZXjGFN0oP7qYvbXWzEgRm/Yql4dHX5wDbvh89YHP6PK1lzZJYrMtXUuZZz8XGK+U6U1og==", + "requires": { + "boxen": "^7.0.0", + "chalk": "^5.0.1", + "configstore": "^6.0.0", + "has-yarn": "^3.0.0", + "import-lazy": "^4.0.0", + "is-ci": "^3.0.1", + "is-installed-globally": "^0.4.0", + "is-npm": "^6.0.0", + "is-yarn-global": "^0.4.0", + "latest-version": "^7.0.0", + "pupa": "^3.1.0", + "semver": "^7.3.7", + "semver-diff": "^4.0.0", + "xdg-basedir": "^5.1.0" + }, + "dependencies": { + "boxen": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.1.1.tgz", + "integrity": "sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==", + "requires": { + "ansi-align": "^3.0.1", + "camelcase": "^7.0.1", + "chalk": "^5.2.0", + "cli-boxes": "^3.0.0", + "string-width": "^5.1.2", + "type-fest": "^2.13.0", + "widest-line": "^4.0.1", + "wrap-ansi": "^8.1.0" + } + }, + "camelcase": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz", + "integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==" + }, + "chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==" + } + } + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "requires": { + "punycode": "^2.1.0" + }, + "dependencies": { + "punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==" + } + } + }, + "url-loader": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz", + "integrity": "sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==", + "requires": { + "loader-utils": "^2.0.0", + "mime-types": "^2.1.27", + "schema-utils": "^3.0.0" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "requires": {} + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + }, + "schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==" + }, + "utility-types": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz", + "integrity": "sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + }, + "uvu": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.6.tgz", + "integrity": "sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==", + "requires": { + "dequal": "^2.0.0", + "diff": "^5.0.0", + "kleur": "^4.0.3", + "sade": "^1.7.3" + }, + "dependencies": { + "kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==" + } + } + }, + "value-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", + "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" + }, + "vfile": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.1.tgz", + "integrity": "sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==", + "requires": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + } + }, + "vfile-location": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.2.tgz", + "integrity": "sha512-NXPYyxyBSH7zB5U6+3uDdd6Nybz6o6/od9rk8bp9H8GR3L+cm/fC0uUTbqBmUTnMCUDslAGBOIKNfvvb+gGlDg==", + "requires": { + "@types/unist": "^3.0.0", + "vfile": "^6.0.0" + } + }, + "vfile-message": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", + "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", + "requires": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + } + }, + "w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==" + }, + "watchpack": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "requires": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + } + }, + "wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "requires": { + "minimalistic-assert": "^1.0.0" + } + }, + "web-namespaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", + "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==" + }, + "web-worker": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.2.0.tgz", + "integrity": "sha512-PgF341avzqyx60neE9DD+XS26MMNMoUQRz9NOZwW32nPQrF6p77f1htcnjBSEV8BGMKZ16choqUG4hyI0Hx7mA==" + }, + "webpack": { + "version": "5.89.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.89.0.tgz", + "integrity": "sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==", + "requires": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^1.0.0", + "@webassemblyjs/ast": "^1.11.5", + "@webassemblyjs/wasm-edit": "^1.11.5", + "@webassemblyjs/wasm-parser": "^1.11.5", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.9.0", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.15.0", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.2.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.7", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "requires": {} + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + }, + "schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "webpack-bundle-analyzer": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.9.1.tgz", + "integrity": "sha512-jnd6EoYrf9yMxCyYDPj8eutJvtjQNp8PHmni/e/ulydHBWhT5J3menXt3HEkScsu9YqMAcG4CfFjs3rj5pVU1w==", + "requires": { + "@discoveryjs/json-ext": "0.5.7", + "acorn": "^8.0.4", + "acorn-walk": "^8.0.0", + "commander": "^7.2.0", + "escape-string-regexp": "^4.0.0", + "gzip-size": "^6.0.0", + "is-plain-object": "^5.0.0", + "lodash.debounce": "^4.0.8", + "lodash.escape": "^4.0.1", + "lodash.flatten": "^4.4.0", + "lodash.invokemap": "^4.6.0", + "lodash.pullall": "^4.2.0", + "lodash.uniqby": "^4.7.0", + "opener": "^1.5.2", + "picocolors": "^1.0.0", + "sirv": "^2.0.3", + "ws": "^7.3.1" + }, + "dependencies": { + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==" + } + } + }, + "webpack-dev-middleware": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", + "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==", + "requires": { + "colorette": "^2.0.10", + "memfs": "^3.4.3", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "dependencies": { + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + } + } + }, + "webpack-dev-server": { + "version": "4.15.1", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.1.tgz", + "integrity": "sha512-5hbAst3h3C3L8w6W4P96L5vaV0PxSmJhxZvWKYIdgxOQm8pNZ5dEOmmSLBVpP85ReeyRt6AS1QJNyo/oFFPeVA==", + "requires": { + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/express": "^4.17.13", + "@types/serve-index": "^1.9.1", + "@types/serve-static": "^1.13.10", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.5.5", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.0.11", + "chokidar": "^3.5.3", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^2.0.0", + "default-gateway": "^6.0.3", + "express": "^4.17.3", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.3", + "ipaddr.js": "^2.0.1", + "launch-editor": "^2.6.0", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "rimraf": "^3.0.2", + "schema-utils": "^4.0.0", + "selfsigned": "^2.1.1", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^5.3.1", + "ws": "^8.13.0" + }, + "dependencies": { + "ws": { + "version": "8.14.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz", + "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==", + "requires": {} + } + } + }, + "webpack-merge": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", + "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", + "requires": { + "clone-deep": "^4.0.1", + "flat": "^5.0.2", + "wildcard": "^2.0.0" + } + }, + "webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==" + }, + "webpackbar": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/webpackbar/-/webpackbar-5.0.2.tgz", + "integrity": "sha512-BmFJo7veBDgQzfWXl/wwYXr/VFus0614qZ8i9znqcl9fnEdiVkdbi0TedLQ6xAK92HZHDJ0QmyQ0fmuZPAgCYQ==", + "requires": { + "chalk": "^4.1.0", + "consola": "^2.15.3", + "pretty-time": "^1.1.0", + "std-env": "^3.0.1" + } + }, + "websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "requires": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + } + }, + "websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==" + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "requires": { + "isexe": "^2.0.0" + } + }, + "widest-line": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", + "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==", + "requires": { + "string-width": "^5.0.1" + } + }, + "wildcard": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", + "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==" + }, + "wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "requires": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==" + }, + "ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==" + }, + "strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "requires": { + "ansi-regex": "^6.0.1" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "ws": { + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "requires": {} + }, + "xdg-basedir": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-5.1.0.tgz", + "integrity": "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==" + }, + "xml-js": { + "version": "1.6.11", + "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz", + "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==", + "requires": { + "sax": "^1.2.4" + } + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + }, + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" + }, + "yocto-queue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", + "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==" + }, + "zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==" + } + } +} diff --git a/package.json b/package.json index 6aaba2b3a..5d8d694da 100644 --- a/package.json +++ b/package.json @@ -23,10 +23,12 @@ "@docusaurus/tsconfig": "^3.4.0", "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", + "docusaurus-plugin-sass": "^0.2.5", "prism-react-renderer": "^2.1.0", "raw-loader": "^4.0.2", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "sass": "^1.77.6" }, "devDependencies": { "@docusaurus/module-type-aliases": "^3.1.1", diff --git a/src/css/custom.css b/src/css/custom.css deleted file mode 100644 index 042e40d35..000000000 --- a/src/css/custom.css +++ /dev/null @@ -1,255 +0,0 @@ -/** - * Any CSS included here will be global. The classic template - * bundles Infima by default. Infima is a CSS framework designed to - * work well for content-centric websites. - */ - -/* You can override the default Infima variables here. */ -:root { - --ifm-color-primary: #0e42ee; - --ifm-color-primary-dark: #0d3bd6; - --ifm-color-primary-darker: #0c38ca; - --ifm-color-primary-darkest: #0a2ea7; - --ifm-color-primary-light: #2353f2; - --ifm-color-primary-lighter: #2f5cf3; - --ifm-color-primary-lightest: #5378f5; - --ifm-blockquote-color: var(--ifm-color-gray-600); - --ifm-font-family-base: Inter, system-ui, sans-serif; - --ifm-heading-font-family: Inter, system-ui, sans-serif; - --ifm-font-family-monospace: JetBrains Mono, ui-monospace, monospace; - --ifm-font-weight-bold: 700; - --ifm-alert-shadow: none; - --ifm-menu-link-padding-vertical: 0.3rem; - --ifm-navbar-shadow: none; - --ifm-color-primary-rgb: 14, 66, 238; - --docusaurus-highlighted-code-line-bg: rgba(255, 217, 120, 0.2); -} - -/* For readability concerns, you should choose a lighter palette in dark mode. */ -[data-theme='dark'] { - --ifm-color-primary: #4c6dd6; - --ifm-color-primary-dark: #345ad1; - --ifm-color-primary-darker: #2e53c9; - --ifm-color-primary-darkest: #2644a5; - --ifm-color-primary-light: #6480db; - --ifm-color-primary-lighter: #6f8ade; - --ifm-color-primary-lightest: #93a7e6; - --ifm-color-primary-rgb: 76, 109, 214; -} - -/* GitHub */ -.header-github-link:hover { - opacity: 0.6; -} - -.header-github-link:before { - content: ''; - width: 24px; - height: 24px; - display: flex; - background: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E") - no-repeat; -} -html[data-theme='dark'] .header-github-link:before { - background: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='white' d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E") - no-repeat; -} -.header-x-link:before { - content: ''; - width: 24px; - height: 24px; - display: flex; - background-size: contain !important; - background: url('/img/x.svg'); -} -html[data-theme='dark'] .header-x-link:before { - background: url('/img/x-dark.svg'); -} -.header-discord-link:before { - content: ''; - width: 24px; - height: 24px; - display: flex; - background-size: contain !important; - background: url('/img/discord.svg'); -} - -/* sidebar */ -:root { - --ifm-navbar-height: 82px; -} -.navbar { - padding: 0; - background-color: transparent; -} -.navbar__inner { - backdrop-filter: blur(16px); - background: rgba(255, 255, 255, 0.4); - padding: var(--ifm-navbar-padding-vertical) var(--ifm-navbar-padding-horizontal); -} -html[data-theme='dark'] .navbar__inner { - background: rgba(24, 24, 24, 0.4); -} - -.theme-doc-sidebar-container { - border-right: none !important; - background: var(--ifm-color-emphasis-100); -} -.menu__link, -.theme-doc-sidebar-item-category:not(.theme-doc-sidebar-item-category-level-1) { - font-size: 0.8rem; - line-height: 2.4; - border-left: solid 1px var(--ifm-color-emphasis-300); - border-radius: 0; -} -.menu__link--sublist { - border-left: none; -} -.menu__link--active { - border-left-color: var(--ifm-color-emphasis-700); - font-weight: var(--ifm-font-weight-bold); - font-size: 0.9rem; -} -.menu__list-item:not(:first-child) { - margin-top: 0; -} -.menu__list { - --ifm-menu-color-background-active: none; - --ifm-menu-color-background-hover: none; - --ifm-menu-color-active: none; - --ifm-menu-color: var(--ifm-color-emphasis-600); - font-weight: normal; -} -.menu__link:hover { - border-left-color: var(--ifm-color-emphasis-700); -} -.menu__link:after, -.menu__caret:before { - background-size: 1rem 1rem; -} - -/* pagination nav */ -.pagination-nav__label { - margin-top: 1rem; -} -.pagination-nav__link:hover .pagination-nav__label::before { - transform: translateX(-0.3rem); -} -.pagination-nav__link:hover .pagination-nav__label::after { - transform: translateX(0.3rem); -} -.pagination-nav__link--prev .pagination-nav__label::before { - content: '<-'; - transition: all 0.3s; - display: inline-block; - margin-right: 0.5rem; -} -.pagination-nav__link--next .pagination-nav__label::after { - content: '->'; - transition: all 0.3s; - display: inline-block; - margin-left: 0.5rem; -} - -/* toc(table of content) */ -.table-of-contents__link:hover { - font-weight: 700; -} - -/* article */ -.theme-doc-breadcrumbs { - margin-bottom: 3rem !important; -} -.markdown h2:not(.text--truncate) { - border-top: solid 1px var(--ifm-color-emphasis-300); - padding-top: 2rem; -} -code { - vertical-align: baseline; - border: none; - padding: 0.0625rem 0.25rem; -} -article a:not([class]) { - text-decoration: underline; - text-decoration-color: var(--ifm-color-primary-lightest); - text-underline-offset: 0.0625em; - text-decoration-thickness: auto; -} -article section .card { - box-shadow: none; - background: var(--ifm-color-emphasis-100); -} -article section .card:hover { - border-color: transparent; - background: none; -} - -table th, -table td { - border: none; -} -table thead tr { - border-bottom: none; -} -.theme-code-block { - box-shadow: none !important; - border: solid 1px var(--ifm-color-emphasis-200); -} -strong { - color: var(--ifm-color-primary); -} -.tabs { - border-bottom: solid 1px var(--ifm-color-emphasis-300); - background: var(--ifm-color-emphasis-100); -} -.tabs .tabs__item { - border-radius: 0; -} -/* footer */ -:root { - --ifm-footer-background-color: var(--ifm-color-emphasis-0); - --ifm-footer-title-color: var(--ifm-color-emphasis-500); - --ifm-footer-link-color: var(--ifm-color-emphasis-900); - --ifm-footer-padding-vertical: 4rem; -} -.footer__title { - font-size: 0.9rem; -} -.footer__link-item:hover { - text-decoration: none; -} - -/* //////// 自定义类 //////// */ -/* flex */ -.flex-row { - display: flex !important; - flex-direction: row; -} -.flex-col { - display: flex !important; - flex-direction: column; -} -.justify-between { - justify-content: space-between !important; -} -.align-center { - align-items: center !important; -} -.align-start { - align-items: flex-start !important; -} -.align-end { - align-items: flex-end !important; -} -.justify-center { - justify-content: center; -} -.justify-end { - justify-content: flex-end; -} -.flex-wrap { - flex-wrap: wrap; -} -.flex-1 { - flex: 1; -} diff --git a/src/css/custom.scss b/src/css/custom.scss new file mode 100644 index 000000000..a532b60f1 --- /dev/null +++ b/src/css/custom.scss @@ -0,0 +1,275 @@ +/** + * Any CSS included here will be global. The classic template + * bundles Infima by default. Infima is a CSS framework designed to + * work well for content-centric websites. + */ + +/* You can override the default Infima variables here. */ +:root { + --ifm-color-primary: #0e42ee; + --ifm-color-primary-dark: #0d3bd6; + --ifm-color-primary-darker: #0c38ca; + --ifm-color-primary-darkest: #0a2ea7; + --ifm-color-primary-light: #2353f2; + --ifm-color-primary-lighter: #2f5cf3; + --ifm-color-primary-lightest: #5378f5; + --ifm-blockquote-color: var(--ifm-color-gray-600); + --ifm-font-family-base: Inter, system-ui, sans-serif; + --ifm-heading-font-family: Inter, system-ui, sans-serif; + --ifm-font-family-monospace: JetBrains Mono, ui-monospace, monospace; + --ifm-font-weight-bold: 700; + --ifm-alert-shadow: none; + --ifm-menu-link-padding-vertical: 0.3rem; + --ifm-navbar-shadow: none; + --ifm-color-primary-rgb: 14, 66, 238; + --docusaurus-highlighted-code-line-bg: rgba(255, 217, 120, 0.2); + + --docusaurus-announcement-bar-height: auto !important; +} + +/* For readability concerns, you should choose a lighter palette in dark mode. */ +[data-theme='dark'] { + --ifm-color-primary: #4c6dd6; + --ifm-color-primary-dark: #345ad1; + --ifm-color-primary-darker: #2e53c9; + --ifm-color-primary-darkest: #2644a5; + --ifm-color-primary-light: #6480db; + --ifm-color-primary-lighter: #6f8ade; + --ifm-color-primary-lightest: #93a7e6; + --ifm-color-primary-rgb: 76, 109, 214; +} + +/* GitHub */ +.header-github-link:hover { + &:hover { + opacity: 0.6; + } + &::before { + content: ''; + width: 24px; + height: 24px; + display: flex; + background: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E") + no-repeat; + } +} +.header-x-link:before { + content: ''; + width: 24px; + height: 24px; + display: flex; + background-size: contain !important; + background: url('/img/x.svg'); +} +.header-discord-link:before { + content: ''; + width: 24px; + height: 24px; + display: flex; + background-size: contain !important; + background: url('/img/discord.svg'); +} +// dark mode icons +html[data-theme='dark'] { + .header-github-link:before { + background: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='white' d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E") + no-repeat; + } + .header-x-link:before { + background: url('/img/x-dark.svg'); + } +} + +/* sidebar */ +:root { + --ifm-navbar-height: 82px; +} +.navbar { + padding: 0; + background-color: transparent; +} +.navbar__inner { + backdrop-filter: blur(16px); + background: rgba(255, 255, 255, 0.4); + padding: var(--ifm-navbar-padding-vertical) var(--ifm-navbar-padding-horizontal); +} +html[data-theme='dark'] { + .navbar__inner { + background: rgba(24, 24, 24, 0.4); + } +} +.theme-doc-sidebar-menu > li > .menu__list-item-collapsible { + font-weight: bolder; + font-style: italic; + --ifm-menu-color-background-hover: none; + --ifm-menu-color-active: none; + + .menu__link { + color: var(--ifm-color-emphasis-500); + } + + &::before { + content: ''; + display: block; + width: 96%; + border-top: dashed 1px var(--ifm-color-emphasis-200); + margin-top: 0.8rem; + margin-bottom: 0.8rem; + } +} + +@mixin arrow-icon-size { + background-size: 1rem 1rem; +} +.menu__link { + font-size: 0.9rem; + line-height: 1.5; + &--active { + color: var(--ifm-color-emphasis-900); + } + &:after { + @include arrow-icon-size; + } + .menu__list { + font-weight: normal; + } +} +.theme-doc-sidebar-item-category { + @extend .menu__link; +} +.menu__caret:before { + @include arrow-icon-size; +} + +/* pagination nav */ +.pagination-nav__label { + margin-top: 1rem; +} +.pagination-nav__link { + &:hover .pagination-nav__label::before { + transform: translateX(-0.3rem); + } + &:hover .pagination-nav__label::after { + transform: translateX(0.3rem); + } + &--prev .pagination-nav__label::before { + content: '<-'; + transition: all 0.3s; + display: inline-block; + margin-right: 0.5rem; + } + &--next .pagination-nav__label::after { + content: '->'; + transition: all 0.3s; + display: inline-block; + margin-left: 0.5rem; + } +} + +/* toc(table of content) */ +.table-of-contents__link:hover { + font-weight: 700; +} + +/* article */ +.theme-doc-breadcrumbs { + margin-bottom: 3rem !important; +} +.markdown h2:not(.text--truncate) { + border-top: solid 1px var(--ifm-color-emphasis-300); + padding-top: 2rem; +} +code { + vertical-align: baseline; + border: none; + padding: 0.0625rem 0.25rem; +} +article { + a:not([class]) { + text-decoration: underline; + text-decoration-color: var(--ifm-color-primary-lightest); + text-underline-offset: 0.0625em; + text-decoration-thickness: auto; + } + section { + .card { + box-shadow: none; + background: var(--ifm-color-emphasis-100); + &:hover { + border-color: transparent; + background: none; + } + } + } +} + +table { + th, + td { + border: none; + } + thead tr { + border-bottom: none; + } +} +.theme-code-block { + box-shadow: none !important; + border: solid 1px var(--ifm-color-emphasis-200); +} +.tabs { + border-bottom: solid 1px var(--ifm-color-emphasis-300); + background: var(--ifm-color-emphasis-100); + .tabs__item { + border-radius: 0; + } +} +/* footer */ +:root { + --ifm-footer-background-color: var(--ifm-color-emphasis-0); + --ifm-footer-title-color: var(--ifm-color-emphasis-500); + --ifm-footer-link-color: var(--ifm-color-emphasis-900); + --ifm-footer-padding-vertical: 4rem; +} +.footer { + &__title { + font-size: 0.9rem; + } + &__link-item:hover { + text-decoration: none; + } +} + +/* //////// 自定义类 //////// */ +/* flex */ +.flex-row { + display: flex !important; + flex-direction: row; +} +.flex-col { + display: flex !important; + flex-direction: column; +} +.justify-between { + justify-content: space-between !important; +} +.align-center { + align-items: center !important; +} +.align-start { + align-items: flex-start !important; +} +.align-end { + align-items: flex-end !important; +} +.justify-center { + justify-content: center; +} +.justify-end { + justify-content: flex-end; +} +.flex-wrap { + flex-wrap: wrap; +} +.flex-1 { + flex: 1; +} diff --git a/src/pages/_indexComponent/Strategy/data.tsx b/src/pages/_indexComponent/Strategy/data.tsx index bbdcfc29e..74d96a1a5 100644 --- a/src/pages/_indexComponent/Strategy/data.tsx +++ b/src/pages/_indexComponent/Strategy/data.tsx @@ -1,327 +1,376 @@ -import Translate from '@docusaurus/Translate'; - -export const strategyList = [ - { - title: Basic request, - describe: ( - - Send request with useRequest, it will automatically maintain the states related to this request. - - ), - features: [ - Similar to axios., - Maintains the states automatically., - Response cache., - Share the same request sent at the same time. - ], - code: `const todoDetail = alova.Get('/todo', { - params: { - id: 1 - } -}); -const { - loading, data, error, - onSuccess, onError, onComplete, - send, abort, update -} = useRequest(todoDetail);`, - link: 'tutorial/combine-framework/use-request' - }, - { - title: Request when states changed, - describe: ( - - When developing functions such as paging, data filtering, and fuzzy search, send requests immediately by - watching states changes. - - ), - features: [ - Request debounce., - Ensure request timing., - - Filter whether to send a request when states changes. - - ], - code: `useWatcher( - () => filterTodoList(page, keyword), - [keyword, page], - { - debounce: [500, 0], - sendable: () => keyword !== '' - } -);`, - link: 'tutorial/getting-started/states-change-request' - }, - { - title: Prefetch data, - describe: ( - - Preload data to display view faster, or re-pull data across components. - - ), - features: [ - Update view cross modules/components., - Preload data. - ], - code: `const { - fetching, error, - fetch -} = useFetcher(); -fetch(todoDetail);`, - link: 'tutorial/advanced/data-fetching' - }, - { - title: Pagination request, - describe: ( - - Automatically manage paging data, data preloading, reduce unnecessary data refresh, improve fluency by 300%, and - reduce coding difficulty by 50% - - ), - features: [ - Rich pagination states and events., - - Automatically fetch specified page data when watching state changes. - , - Preload next page data., - High-performance list manipulation functions. - ], - code: `usePagination((page, size) => todoList(page, size), { - initialData: { - total: 0, - data: [] - }, - initialPage: 1, - initialPageSize: 10 -});`, - link: 'tutorial/strategy/usePagination' - }, - { - title: Token authentication, - describe: ( - - Automatically manage form data, it allow you implement quickly various of forms. - - ), - features: [ - - Supports silent refresh token on client and server - , - - Automatically wait for token refresh to complete - , - - Maintain all codes for Token identity authentication in a unified manner - , - set request ID with metadata, - Release visitor requests - ], - code: `const { onAuthRequired, onResponseRefreshToken } = createServerTokenAuthentication({ - refreshTokenOnError: { - isExpire: response => response.status === 401, - refreshToken: async () => { - const { token, refresh_token } = await refreshToken(); - localStorage.setItem('token', token); - localStorage.setItem('refresh_token', refresh_token); - } - } -}); - -const alovaInstance = createAlova({ - beforeRequest: onAuthRequired(), - responded: onResponseRefreshToken() -});`, - link: 'tutorial/strategy/tokenAuthentication' - }, - { - title: ( - Sensorless data interaction strategy - ), - describe: ( - - A new interactive experience, submitting and responding, greatly reducing the impact of network fluctuations, - allowing your application to remain available even when the network is unstable or even disconnected - - ), - features: [ - - "Just like operating local data, there is no need to wait for the network response." . - , - - Available even in weak or offline network. - , - - More stable data synchronization, without any perception from the user end. - , - - Switch freely between seamless request and normal network request - - ], - code: `const { - loading, - data, - error, - onBeforePushQueue, - onPushedQueue, - onFallback -} = useSQRequest(() => todoDetail(id), { - behavior: 'queue', - backoff: { - delay: 2000 - } -});`, - link: 'tutorial/strategy/sensorless-data-interaction/overview' - }, - { - title: Form Submit strategy, - describe: ( - - Automatically manage form data, it allow you implement quickly various of forms. - - ), - features: [ - Saving Form draft, - Manage multi-step form, - Automatically reset form data after submiting - ], - code: `const { - form, - send: submitForm, - updateForm -} = useForm(formData => submitData(formData), { - initialForm: { - title: '', - content: '', - time: '', - }, - resetAfterSubmiting: true -});`, - link: 'tutorial/strategy/useForm' - }, - { - title: Auto refetch data, - describe: ( - - Automatically refetch data through the events of browser, ensure that the newest data is always displayed. - - ), - features: [ - Freely select in 4 refetch rules., - Custom your refetch triggering rule., - Support request throttling. - ], - code: `useAutoRequest(todoDetail, { - pollingTime: 2000, - enableVisibility: true, - enableFocus: true, - enableNetwork: true, - throttle: 1000 -}`, - link: 'tutorial/strategy/useAutoRequest' - }, - { - title: File upload strategy, - describe: ( - - Simplified file upload strategy that supports automatic recognition and conversion of base64, Blob, ArrayBuffer, - and Canvas. - - ), - features: [ - - Automatically recognize and convert file data to File instance - , - Upload multiple files simultaneously, - Image preview generation - ], - code: `const { - fileList - loading, - progress -} = useUploader(({ file, name }) => uploadFile(file, name), { - limit: 3, - accept: ['png', 'jpg', 'gif'], - imageTempLink: true -});`, - link: 'tutorial/strategy/useUploader' - }, - { - title: Send captcha, - describe: ( - - Reduce your tediousness when developing the verification code sending function - - ), - features: [Automatically countdown.], - code: `const { - loading: sending, - send: sendCaptcha -} = useCaptcha(() => sendCaptcha(mobile), { - initialCountdown: 60 -});`, - link: 'tutorial/strategy/useCaptcha' - }, - { - title: Cross-component request strategy, - describe: ( - - Eliminate the limitation of component hierarchy, and quickly trigger the operation function of any request in - any component - - ), - features: [ - - Trigger a re-request across any component - - ], - code: `useRequest(todoDetail, { - middleware: actionDelegationMiddleware('actionName') -});`, - link: 'tutorial/strategy/actionDelegationMiddleware' - }, - { - title: Request retry strategy, - describe: ( - - Request failure automatic retry, it plays an important role on important requests and polling requests - - ), - features: [ - - Custom the retry or not, and the retry delay time. - , - Manually stop retry. - ], - code: `const { - onRetry, - onFail, - stop, -} = useRetriableRequest(pay, { - retry(error) { - return /network timeout/i.test(error.message); - }, - backoff: { - delay: 2000 - } -});`, - link: 'tutorial/strategy/useRetriableRequest' - }, - { - title: SSE, - describe: Request with Server-sent Events., - features: [ - - Automatically transform data via global responded and function transformData of method instance. - , - All control of EventSource. - ], - code: `const { - readyState, data, eventSource, - onMessage, onError, onOpen, - on -} = useSSE(() => chatGPT(), { - withCredentials: true, - interceptByGlobalResponded: true -});`, - link: 'tutorial/strategy/useSSE' - } -]; +import Translate from '@docusaurus/Translate'; + +export const strategyList = [ + { + title: Basic request, + describe: ( + + Send request with useRequest, it will automatically maintain the states related to this + request. + + ), + features: [ + Similar to axios., + + Maintains the states automatically. + , + Response cache., + + Share the same request sent at the same time. + + ], + code: `const todoDetail = alova.Get('/todo', { + params: { + id: 1 + } +}); +const { + loading, data, error, + onSuccess, onError, onComplete, + send, abort, update +} = useRequest(todoDetail);`, + link: 'tutorial/combine-framework/use-request' + }, + { + title: Request when states changed, + describe: ( + + When developing functions such as paging, data filtering, and fuzzy search, send + requests immediately by watching states changes. + + ), + features: [ + Request debounce., + Ensure request timing., + + Filter whether to send a request when states changes. + + ], + code: `useWatcher( + () => filterTodoList(page, keyword), + [keyword, page], + { + debounce: [500, 0], + sendable: () => keyword !== '' + } +);`, + link: 'tutorial/combine-framework/use-watcher' + }, + { + title: Prefetch data, + describe: ( + + Preload data to display view faster, or re-pull data across components. + + ), + features: [ + + Update view cross modules/components. + , + Preload data. + ], + code: `const { + fetching, error, + fetch +} = useFetcher(); +fetch(todoDetail);`, + link: 'tutorial/advanced/use-fetcher' + }, + { + title: Pagination request, + describe: ( + + Automatically manage paging data, data preloading, reduce unnecessary data refresh, + improve fluency by 300%, and reduce coding difficulty by 50% + + ), + features: [ + + Rich pagination states and events. + , + + Automatically fetch specified page data when watching state changes. + , + Preload next page data., + + High-performance list manipulation functions. + + ], + code: `usePagination((page, size) => todoList(page, size), { + initialData: { + total: 0, + data: [] + }, + initialPage: 1, + initialPageSize: 10 +});`, + link: 'tutorial/strategy/usePagination' + }, + { + title: ( + Token authentication + ), + describe: ( + + Automatically manage form data, it allow you implement quickly various of forms. + + ), + features: [ + + Supports silent refresh token on client and server + , + + Automatically wait for token refresh to complete + , + + Maintain all codes for Token identity authentication in a unified manner + , + + set request ID with metadata + , + + Release visitor requests + + ], + code: `const { onAuthRequired, onResponseRefreshToken } = createServerTokenAuthentication({ + refreshTokenOnError: { + isExpire: response => response.status === 401, + refreshToken: async () => { + const { token, refresh_token } = await refreshToken(); + localStorage.setItem('token', token); + localStorage.setItem('refresh_token', refresh_token); + } + } +}); + +const alovaInstance = createAlova({ + beforeRequest: onAuthRequired(), + responded: onResponseRefreshToken() +});`, + link: 'tutorial/strategy/tokenAuthentication' + }, + { + title: ( + + Sensorless data interaction strategy + + ), + describe: ( + + A new interactive experience, submitting and responding, greatly reducing the impact of + network fluctuations, allowing your application to remain available even when the + network is unstable or even disconnected + + ), + features: [ + + "Just like operating local data, there is no need to wait for the network response." . + , + + Available even in weak or offline network. + , + + More stable data synchronization, without any perception from the user end. + , + + Switch freely between seamless request and normal network request + + ], + code: `const { + loading, + data, + error, + onBeforePushQueue, + onPushedQueue, + onFallback +} = useSQRequest(() => todoDetail(id), { + behavior: 'queue', + backoff: { + delay: 2000 + } +});`, + link: 'tutorial/strategy/sensorless-data-interaction/overview' + }, + { + title: Form Submit strategy, + describe: ( + + Automatically manage form data, it allow you implement quickly various of forms. + + ), + features: [ + Saving Form draft, + Manage multi-step form, + + Automatically reset form data after submiting + + ], + code: `const { + form, + send: submitForm, + updateForm +} = useForm(formData => submitData(formData), { + initialForm: { + title: '', + content: '', + time: '', + }, + resetAfterSubmiting: true +});`, + link: 'tutorial/strategy/useForm' + }, + { + title: Auto refetch data, + describe: ( + + Automatically refetch data through the events of browser, ensure that the newest data is + always displayed. + + ), + features: [ + + Freely select in 4 refetch rules. + , + + Custom your refetch triggering rule. + , + + Support request throttling. + + ], + code: `useAutoRequest(todoDetail, { + pollingTime: 2000, + enableVisibility: true, + enableFocus: true, + enableNetwork: true, + throttle: 1000 +}`, + link: 'tutorial/strategy/useAutoRequest' + }, + { + title: File upload strategy, + describe: ( + + Simplified file upload strategy that supports automatic recognition and conversion of + base64, Blob, ArrayBuffer, and Canvas. + + ), + features: [ + + Automatically recognize and convert file data to File instance + , + + Upload multiple files simultaneously + , + Image preview generation + ], + code: `const { + fileList + loading, + progress +} = useUploader(({ file, name }) => uploadFile(file, name), { + limit: 3, + accept: ['png', 'jpg', 'gif'], + imageTempLink: true +});`, + link: 'tutorial/strategy/useUploader' + }, + { + title: Send captcha, + describe: ( + + Reduce your tediousness when developing the verification code sending function + + ), + features: [ + + Automatically countdown. + + ], + code: `const { + loading: sending, + send: sendCaptcha +} = useCaptcha(() => sendCaptcha(mobile), { + initialCountdown: 60 +});`, + link: 'tutorial/strategy/useCaptcha' + }, + { + title: ( + + Cross-component request strategy + + ), + describe: ( + + Eliminate the limitation of component hierarchy, and quickly trigger the operation + function of any request in any component + + ), + features: [ + + Trigger a re-request across any component + + ], + code: `useRequest(todoDetail, { + middleware: actionDelegationMiddleware('actionName') +});`, + link: 'tutorial/strategy/actionDelegationMiddleware' + }, + { + title: ( + + Request retry strategy + + ), + describe: ( + + Request failure automatic retry, it plays an important role on important requests and + polling requests + + ), + features: [ + + Custom the retry or not, and the retry delay time. + , + + Manually stop retry. + + ], + code: `const { + onRetry, + onFail, + stop, +} = useRetriableRequest(pay, { + retry(error) { + return /network timeout/i.test(error.message); + }, + backoff: { + delay: 2000 + } +});`, + link: 'tutorial/strategy/useRetriableRequest' + }, + { + title: SSE, + describe: ( + Request with Server-sent Events. + ), + features: [ + + Automatically transform data via global responded and function transformData of method + instance. + , + All control of EventSource. + ], + code: `const { + readyState, data, eventSource, + onMessage, onError, onOpen, + on +} = useSSE(() => chatGPT(), { + withCredentials: true, + interceptByGlobalResponded: true +});`, + link: 'tutorial/strategy/useSSE' + } +]; diff --git a/src/pages/index.tsx b/src/pages/index.tsx index b74e31958..88a17870d 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,123 +1,120 @@ -import Link from '@docusaurus/Link'; -import Translate, { translate } from '@docusaurus/Translate'; -import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; -import PageModule from '@site/src/components/PageModule'; -import CodeBlock from '@theme/CodeBlock'; -import Layout from '@theme/Layout'; -import clsx from 'clsx'; -import IconFont from '../components/IconFont'; -import Contributors from './_indexComponent/Contributors'; -import Features from './_indexComponent/Features'; -import Like from './_indexComponent/Like'; -import Strategy from './_indexComponent/Strategy'; -import Support from './_indexComponent/Support'; -import styles from './_indexComponent/index.module.css'; - -function HomepageHeader() { - const buttons = [ - { - text: ( -
- - Get Started -
- ), - type: 'primary', - link: '/tutorial/getting-started' - }, - { - text: Examples, - type: 'secondary', - link: '/category/examples' - } - ]; - const { i18n } = useDocusaurusContext(); - - return ( -
-
-
-

- Lightweight request strategy library -

-

- - One line of code completes network requests in various complex scenarios. Don’t spend time on the small - matter of requesting. Leave it to us. - -

-
- {buttons.map(({ text, type, link }, i) => ( - - {text} - - ))} -
- npm install alova - - - 🥳🥳🥳 alvoa3.0 is under development, details here → - -
-
-
- ); -} - -export default function Home(): JSX.Element { - const { siteConfig } = useDocusaurusContext(); - return ( - - -
- - -
- - - Just like their weapon arsenal, alova grants them enhanced capabilities. Whether you prefer using axios, - superagent, or the browser's fetch API, alova seamlessly integrates with all of them. - - -
-
- - - - -
-
- ); -} +import Link from '@docusaurus/Link'; +import Translate, { translate } from '@docusaurus/Translate'; +import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; +import PageModule from '@site/src/components/PageModule'; +import CodeBlock from '@theme/CodeBlock'; +import Layout from '@theme/Layout'; +import clsx from 'clsx'; +import IconFont from '../components/IconFont'; +import Contributors from './_indexComponent/Contributors'; +import Features from './_indexComponent/Features'; +import Like from './_indexComponent/Like'; +import Strategy from './_indexComponent/Strategy'; +import Support from './_indexComponent/Support'; +import styles from './_indexComponent/index.module.css'; + +function HomepageHeader() { + const buttons = [ + { + text: ( +
+ + Get Started +
+ ), + type: 'primary', + link: '/tutorial/getting-started' + }, + { + text: Examples, + type: 'secondary', + link: '/category/examples' + } + ]; + const { i18n } = useDocusaurusContext(); + + return ( +
+
+
+

+ Lightweight request strategy library +

+

+ + One line of code completes network requests in various complex scenarios. Don’t + spend time on the small matter of requesting. Leave it to us. + +

+
+ {buttons.map(({ text, type, link }, i) => ( + + {text} + + ))} +
+ npm install alova + + + 🎉 + alvoa v3.0.b-beta is here → + +
+
+
+ ); +} + +export default function Home(): JSX.Element { + const { siteConfig } = useDocusaurusContext(); + return ( + + +
+ + +
+ + + Just like their weapon arsenal, alova grants them enhanced capabilities. Whether + you prefer using axios, superagent, or the browser's fetch API, alova seamlessly + integrates with all of them. + + +
+
+ + + + +
+
+ ); +} diff --git a/src/theme/AnnouncementBar/Content/index.js b/src/theme/AnnouncementBar/Content/index.js index a19ade10c..b9af84a81 100644 --- a/src/theme/AnnouncementBar/Content/index.js +++ b/src/theme/AnnouncementBar/Content/index.js @@ -1,26 +1,38 @@ +import Link from '@docusaurus/Link'; import Translate from '@docusaurus/Translate'; import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; -import pngLive from '@site/static/img/live.jpg'; import clsx from 'clsx'; -import styles from './styles.module.css'; +import styles from './styles.module.scss'; export default function AnnouncementBarContent(props) { const { i18n } = useDocusaurusContext(); i18n.currentLocale; return ( + // Developer provided the HTML, so assume it's safe. + // eslint-disable-next-line react/no-danger
- ⭐️ - - If you also like alova, star it on GitHub! - - ⭐️ + className={clsx(styles.content, props.className, 'flex-col align-center')}> + + alova v3.0.0-beta is HERE! + + + + More simple, more powerful, support both client & server + + +
+ + What's New + + + v3.0 Tutorial + +
); } diff --git a/src/theme/AnnouncementBar/Content/styles.module.css b/src/theme/AnnouncementBar/Content/styles.module.css deleted file mode 100644 index 16635d4b7..000000000 --- a/src/theme/AnnouncementBar/Content/styles.module.css +++ /dev/null @@ -1,10 +0,0 @@ -.content { - font-size: 85%; - text-align: center; - padding: 5px 0; -} - -.content a { - color: inherit; - text-decoration: underline; -} diff --git a/src/theme/AnnouncementBar/Content/styles.module.scss b/src/theme/AnnouncementBar/Content/styles.module.scss new file mode 100644 index 000000000..a3430dcc6 --- /dev/null +++ b/src/theme/AnnouncementBar/Content/styles.module.scss @@ -0,0 +1,31 @@ +.content { + font-size: 85%; + text-align: center; + padding: 5px 0; + background: url(/static/img/announcement_bg.png) no-repeat; + background-size: 100% 100%; +} + +.announce { + &Title { + font-size: 1.1rem; + } + &Button { + border: solid 1px #fff; + border-radius: 10rem; + padding: 0 1rem; + margin: 0.5rem; + cursor: pointer; + transition: all 0.3s; + + &:hover { + color: var(--ifm-color-primary); + border-color: var(--ifm-color-primary); + text-decoration: none; + } + } +} + +.content a { + color: inherit; +} diff --git a/static/img/announcement_bg.png b/static/img/announcement_bg.png new file mode 100644 index 0000000000000000000000000000000000000000..6f81f963ffb2b2c9f77de879b6641017627ee7f8 GIT binary patch literal 194871 zcmV*LKxDs(P)UFG=CbSGHk^|FBOYMbn2GR%lJI|%?{t^+qJ{vC6Z4&j?roaycb4yYl_XK)(b3TWh!XOVjQ7EM z)YR1WyL|c9a`w4>@|24B%ys~R5$|xB_P%=|OyTcwm+fAk_sMthjE?ulc=yD4`PXpq zijO8%rtcjxOViYe)hg~^rV0H!gKSS zcl4)o^P_k0fsgZ>gz$fh_r`MYe}wB)qV=$S>{_7s%5m;)f9p@B^Pqa{V2Smue(YY6 z?PQtkTaWK~itAg5>|%lMdX4N{mh5AY>sOBOetzp)fbC(H_seYe#b@@vZ0%-~?r(sXiZh<5ReZtiZB_PK5DbCL9| zb?s-1>Q9&Nb$s!Sd+u+L0DKVbWQ_BrYwl}|@{)q>XM^lpcJhyZ@rH!?)NSfZpXyMJ z^|WU1cz^MUf%nE>_P}QAR-E;&aPg6L_|$3m&uQ&nn)bk7^|Nd9oN@1UhxM>?@|bY# zXNUODZ}Xpi^P*_$R)+JTarLch@r#M;RFmmOnevx=B~;_;QhLqJ&Gx!y0Co@UVw3c! zW9dng@Pmi&fOhhjfApY&^PFh%m3HlDe)5%U_|9MQmV))SV(L$@ z?|OyumSy$2UG=nA@{Wn~pIz^DgYbrl?|O0cr(Er0dic&+?|EtJQG)cWXX#6b=RJ?_ zc6IWWUg$`I?QCxDYj*B%ckzv9?PPQ5On&G@hxf@@@Plsm%2Dx*VCFrD@PlPMnkw_G zY4Vv{^Rrdn!@uIVv+JUl=dGpM&C3vw86=b;!&t9OW7|1o;*YCKSzORno;7EuJbbJ} zo?E-gzg=)&%d=6csk>5$V8-#KTeq!Wg-x~AuxdR|(zK&cFwTY~00000bW%=JHo0)j z00Q;qNkl?~g!qKG)dbt!@B??0r}eW*+M?TeI@ln;$nbgWLPu1je&eYj3BHEzB&nM{o)6HcaT zQ=`1PT28Oc&+<(gn&j2=4uvr#4>if}dR6oaPN;%BJfpXF9#blBtxDx~AJFKNQ$@Gt&)nXDW3O9sHt^|K zB9YvkNt_a=dwPH_yzXhqfJ8DqaOc4t30`iAWNX@U=bpZsmYh$Ymz12BOi$DO=@L)< z^liz&?R3fAR!MQm?RiOYadCZd{Xp?n@$JXr;;pR#nwTms9w;j=E2}S?Dyy$Aqu11J zY*>%?6sF3yJIcz|)^76h@-9yEw)673x;nPEXSO@?y0))oFpEQ8N7pu;FFLyNxAXJ6 zwl8M#^YbtA@#>(zu9>UTuB)#6@h)8DcXeG&=0`Fo2P0RRk*i4N)nsO7=2gCnj)R$l zk<9VTtB1_Y$?onUvt95BzJG0&ET37eZtfpI|o$c;kcWE^D8hY)zb~Wq!>-Vnx`+bdT ze@Ua+(d_TvukS9<^uoPn;p@KU-bWAKFYVINuCX6xEM8)QCaaz zWo1Q$%pr3u%Pg{A_GB`PWm!f?nQU2BA**PsuvE0Q(S)q;ovcrWVH@6kG?OU%`Op9G z$L#FkXV0F!e)g>3*|Xf-?1F;qg6wCx1+Q~yb{L0eG)L#*>;gPeP%xY=p2r>B%zd4m zJ6!M#U!#cve6xUlso?dqg4YGlUh~s9JjSOT#!d36J6Tk`c}L=jl_FCVmB9%t@snSwxQ&CB4d_VGf1BoFshl z`ST?4)ws0eRK!FMo$=om`B$>Y|FbbArH;TwRW$*P7Z|^&rmK{yKuQWwN29!vkOO@_ z2LcQys!RkV!UQ)y6Rh|gc)LQw>rHuJr8-OyWM!pVZlVV_<>Ae6Sgw`#t6_xVF-|eW;zQ)1UKlSv!$@7lTe4BAck>- zfJVQO#w{aRSv^@j{e&irz!)t8X>9cO17Tp8)i6dE8(DgqY-s3T)He+2`y2Yl`t|yn znnist;f^8w*w7g8$6||KZ|KztJZqP+*tFqPHs#LcVmQXgEfiP%nZU(Zbou{J{qo6<<6Z15qR;y^Jx}0zJ zswPw_=fwQM!I@WeS*w~qI18Q;*zlgI+;k4Aj)TWRppT$RB5^t;L531%^zjnBICc(5 z2yOsm;5_|N;$$o$0s6pedaGn$y2M>VXk$xq?jh7MO=F42Q&Qq7rtwxi-6=lz5XxAi z54~|LzV*-yz0T=#GgV(-d|Ot$c3XdQLvZ7!qpbd>ehRo_pq`*aUfxXJ)XdZr;fgMx zi?!1{1{rH~r(>Id$INLLW;!rAlb_c`NaJdT(MDIkICbPlx&S@$JI3)5AR>Q|fW{=o zlY?}XnNQKGt+f7`0y||sC_un4n7P{61a#Enm8KN zYH8wV5cp$uhYl_mP2fyV5E7Z4ouv`M4f@m4nec|oMKA)dSwIerX5XcmCAdKsOFNp~ zbwVAuy}NJMXx1^d@8hvuJK+l(FosVcjW2dP!--w`9Qx?uOGbw6i~aC$ZZ6}*mSHDM zF_$4-IGmH(d{U`T>hqP;SvrS<)hY$jkXl#ib4c)_+&YJuazY+-P^|7*t?$c$HWaIX z9CWfMmI-dqg~d`?>9EFbIB3S9P>d=p4uz~zR_P#oQ3;G;5lG_~hXV+M@PTVm)B%zD?pq>ETgYw$8O|`FBKp(KFb!r$GE9Ol*RjZd`XQyUy6y)+4gS!fT0oHMlt9V{8WHj! z;6ae1nm*S!R#nMOaucD8x2DZHc{Omzim3`shv;P@v=I)` zN2j~itq|(yTxkkjck1xg`3uATk(EL^T^BYK7LJ5AdI*JV^!PVA{e?Y+BR!qQo{^DG zBhni9&|#zpu){c#1x%6UC*;x6L*Rohhq4-Q?8y>Y4!Rnv(fbKz=o<)O=m9qfYAh}i z%-~cRn4ker<;I; z+N)9>IL%&>);Lhra&Chrg9JcSoc&PYx#QzcXI`(DAcynVD{%%1SxD|A?g8hWd*IHA zYzCnV$?^2@9Zq+WkAy%5ZUIGT;%;lYL{c&>IWIZ)5CqvG%t45wga#gh6a&R2^~Ijz z+v4JBLLLHm)YIptxW4RsZB1l3%5K&OV{oS9rmO?GjR6`@iF^m+3;Ik=U36`4Z=V8t zZ0DUKsX_0K?bC~ni;L5|Qvot&x(IX7=>kcPneD55LLW$RbVTU(AOVl@!7hO+7*I@R z4(2li8O&r%5phjsjvoPXAg%F$qst|-9GMTU$w+2|2HFP!D7qtB?f4)aBAn{L+~DBD zY$OtK0c}JWVtmzhYj?U`-8te(i-EqL5vgWiPXAaJ+bVaSdr?GN>`=jtK}yIDd@1L zP?YbzuOw9Q-n#nUvZ}Bu9F-P=8iX|%e<=9isH`B^QQ@d?$bON@MhSUXDgZg!=(8uo zRRw1`WU}RozERoF$aK6(ECm=@9!-?>wf&r!NPmfc{>R__0fFsxL4nvFBSPh3A3Tih z_Hg#_YZ1?QYn?lc;D>wI8S``51@wnl#sRTF#6l6tp2Y$ueN*hKb7O%L=i%6u2)e`g zzZ?gL#Vi(+&C&8HqJCTCf9N79%%mkH6vd^cB_t%L(jGQFHU2qmYH6#SoSHy)b8>jo zosd9LC_Xs}@i3lZV-iQPBzllH#tF0wP8P9EOyhP@65ZuUMw7`oMV$Jl=$W3JmJk=) zr5ABHOiQ2$N6{1&fub~oRiyXR;&PHH&L!mJd|TvS#UfMxrtY_#*QlziOKAk)NNHre zL5QO<<+{4=8c@R|Cydc}{h5m>@_@X$F;FFsrm(KiSh%uMSlH$yDuh7Bh@9~caz>(G2LMVgY1Ti*7{0&HH7_)kMvRWE8Mi^;~Wi7V!XX!=x zq-R5~&l=K?WsNcBh!WPA7&B)9eCX+s2J<4pj)n#T9_{)GL;INCU^bf#y-{;-O|KzZ z)2pN_r9oNKpi~;#&4fJ&UYHlnvCKw0$|=frb@Wn6K%-q5ZCAIdQAeq*MVcdutj1-t z_Y!9(CTlMznt@2v^J;JHyi+C0C+-P4)T-v4^9QP+D4=|rrvYP*Gu09e!$(zrC;gfm?G>-5pAyLJd$(B&?n43X{7?B8=()(pZL_7k5CNCQLZiEmD7vz`#LC|@O{ zQC@DfmX{OgK#IdEo!hfYE#-tc6niHMg>|*OvXVfDmCGm!i&a5a$aXjsqkEj!s2p_= z)~MjLhfFal8wJF8=NNs5Gr@}rndRMkE~0Shq^&|GE-L5&S*fh8qHS5$mzX%3nArC2 zB@KA^^2a~@{T~sxhIvQ*nzz;131=7NBIl0me>TTGJds@>cHG&V{OA2RZ@y`+U|8(N zV_S8;`HU+F+0Q7z#lkJVG5lHtKic?bKW_X{2oNDOclcQ>yuQxGPl!k>g7LRS{)a6R zY4ar7_9hn<(SA7r;f^-aVz(U6;U@gMq+5lEnYx z+aiDcMgF&{D$Z-9r2H0jl$75#zNo7Xd;sQn5d$D8b=7r^Rc}poRRPoHXF609z~Fp_ ze1*UU4+v{~uHM{~KfON7ubIoSscQmHBdjy&szWAS)AeVQNk=n&U4ZU@#G&hK!olAO z4o5>__j_&J42$M5gWj+hC9pBE zq3@+}v>jOuoD7CmGY;*o02wt(^TdR*dBSW~wl<^I0#*k-P}|Jq7UiW{Nl4?e)_l<1 zJP}l@56q~eIMr&UxA`Dgd!RmW&a2H6wPr#RZjsMW9jG2(LFdF-F!*$-nowObmm`QQ z$C*3mbUM$xPD$|7C#Rbb$iSK8j1UD#91=H@9PVkihwCXGf*zjREstbtnhP$sk`lU* zcmO;~Ng%Vs)&`0P*6NF=ZqKI(RIHVqGg;%N?C~|V zc3a;u^>kTwTXs>FN00*VysnO!ywkj{?bE!Oi>p(Bk?k%5BLp~R^78UeJMzHU=nxzZ zdLn=3iZ196GgtZflaurT@W{`+KpiFXVtg_ZnY;q#n2Z6C%*bG5{AfEf;u_4HygDMd zk?G>PN@nIm=HqMJbu~E7Ne=P4(mpVkV+UD|*`pn;OFJ8JX?MPUb-4(E%+6|O>E0|D z9j+a%M!Pe+1CY_JS-&T2alf;!+1cM+C$RBVquE*C-CYOpzp@-f^fyjdqw$s3jvGrN(X_BUkF?{Dy~D+wk6L70 zT23-=s1wp->3M8~~M3d*)xnH{w#$({fTlF`#&dkVergAucs3H7PzJDgEi+Q-DpTf0vY+{yZ5> z25iEMQj>GOE%L8nk^kjys;e1ORHgh7c$*Rplwa(SRGaNQ_> zOGu--t_qY6Q`jUAS8rBVnF1)Fn7E4ac~ic59hO&zS5`K|a$NxFjL+ewmGE^_6O%YX zI0i7%tu%cOg+c%%g`I_+8{xu{!b0OpVS_(ZNE0JSaTJCNJ3D(iM@Ec=#zKFOv8BI3 zR8{LBHV|snNdCaWe7&AAFHJE`b4EiXJj57=ky%W)z8UiFJo3u`tmF-HU;Zj-C zUZYgvq7~#(I<~ep6W&nMjJmbCMpRXz%obG#4<>34%sfUlMQuKqzci~a-759Gw>Ah! zfh>nN=ylVPAcm8xC=)?d@KaEAHt+QYj|pl7gF(rsWB1+hCpVKgoJ`R;!$@-H{xmS) zbpvKNCGJlikU1nDB?Hp~02tFH5(x?@=UZTq0&Pr7O3pnWB`Bz%no{g3F4>yCrJKwg zMKz_^GvN6+ReZBX6Vq!`#l^Q}Q>Voq-_xenA&w3%E*xjNDweVx?K zPIh;L$uW4eex&`nzSBMUu(R_>;vfWqN{Xx7^>wzp8|e;Qeq9GkW4Bw=z2D6=Q4J`e z>l)Y6u4|v^9s9eQrSyy({SsG82xcIgVPCM{`!c$17y(**A&`+_pYzSp z5%f{p0s{@g7$-i07z>9!0upvxxlQ_|+;>ub!h{Z~m1`}l7)ej8Cv$syR;zVSVO4;_ zQBH7TPqDYUhnmT%!fHiw!)mFtI2_FGs3h#MSIJeCQH4wa{>JEg%P8m@1T*L(bBsFP zIb?eTFxo~5b;#aT^i|0EM6P2Lsg5>4jdy*Cee`MT8(l8_F){IFXrTLQYz8LVA1x z)D5TzBqTEv>p){wT_Z9bRdv^L6XA?Hxrq=)qsVZes8YQt zUy+9c@_BweB<08%??uq>sejPZX zJ+IU2yf8jgc{Wq z<>h>>@}L%(kJQ1B5!duIq8awBh!(O+6v)}hyWuZ!@+Q5T+mDhkF?szLyWo6 zttGHAIe0WUh|I>((X2>&fGyg+(@oIhE7BV~G~wdh2dJek7uQN${KvVB_1Rte>}z)K zak;ak>E6+-@9*#LE&!U`?|$KQ$L_s-$#%G8Tgu4TMQw!!gevSdLLfF#TcM9H<1oW# z$85&o31>HK8HeC-`0PIWoYW>=u-TX}iXzM0+=*|&XO+Ge>5V<+bO3+Ml~-DsGiv2O z&ShAZD;19ARYm1{Tv{xOJ*ZV2j>=K6H{MsiuTWIVDk|PNES1P~$YhnMtoe zyX|szY&V_Faj$^)=KLhMiUqm3q5|+bCd5F16Ps)L!tgT@VL608vKkNwS(hQ`3d6B~ zBDj{1T*vIkt^c=0{`)VI0#tlLDtH0u$gL;GaqLTajwpt$a2jZe&@Eusog)Gk#lz=< z))=44oACsSkI6;p2!t$!NaY}y9`~FXkl-B(4ggpP>FL-#3vMLuveQ6GDB=+LDknXG z_VK){PI{jFDggnN!e-L9MgA2m^6yd_UsQ2ggOEn!hwoFWUNCdCs*$rCbt!~4QsjXu zf)CYl1{nl3>IihaW!;KAT*tTr9bB32tlU;d=aKkQ%t53UWuK@PGL{e_(yeq&FI5y})~ zFbG)qvpB^uW*kGFgFyyi55J$au|rvUeTyFeqrYLJU%$a%qoyI6wWuE&BEVr@tTESQ znF)Rn09jn@?bVMN%o7vQF~SzNpmwRR*?NL zE7eM~T1iNwb>cv+Y-Q$Xt(n=QUiCpO21=n{sb#f_)9Y2MFCjxwiPVN_{&M0>^@+iT z_tNW}R|SJANL9Q+uQN!{;_5&%yGPsp7Ti`r@)1R>&4l zF{z{e=C;1T)_^U#y4KcCXYx8OF1lt!ndS6~Nu+r* ztW=rF&&%()5HgjS$MX#IvV=P_J0|l19P$Zij9*0{O#zC?7Zg%LAE>HKx$--Hd8zJa5bp?%3!1WA>ta;9vuks6Z0C^Hnf5*1OjkdaSPlb!aq88rM3fHFkGrHLm*w&F&I2M|bU-3>qwG z4pBPsL9k*M@|2|v8#J(o8LVCLu`jaYRJyt#m2zG~QI5O?WGa=Fit;@nS8+HL%YqY;J6~9@AJ96o^P0<1XS?ApzyciV^nv__+d5E=4Jzpa8Kk7j=Mdi~RRpB=)yS zLfeoQPkZcSh$WzIK!rapEh(Azy_|!O$Ie*L26Ez)g?2L*6FFF=qx7Ga%tBjiteG7U z;}g)moMaY4&;&(1Y}S(nZy+f>{#8<3DxzO}5;pt%{e&lO0!_X8w#dJNMgB9&DL=%- zDO^l>%Ni9(Rm9;1OJh?~Om!yt+s!I@mC4lDXsQa(3DQ?WAp|Z~8ds|2;Z4(~ys8?i zSQFg|335kBCnva}TM3zT0f>U#LTZBh*PS!(V7b zo};0&GYrb85j+lKOrhdOx?@D>RYtOmW5)ihmPbtrP)5rT%VLK(w=t9@^s+5ki#0WQnSckQ4)8gkQc*_L>KdqHm5{2iUWGp3kg8d>a%l#A6ccW<%2_Kg zhZ`;&!Gkl^d=P{Uuh*+`A_d}gs#H#)R`H%Wp;9?>-T_=3yFW?XZo(PI1B4))P|O|^ z96_3cRk3c#0FUX>kM1pk8C*^|=OYf&NO#n45fbqLUk3o&BV4$mY#)_J1$pe54HwXFstOvvVJJ>_RQ^6d47D;V%jN zVKEd%1m3KRE5t+4DDikqMe&#aDYy{BC>IC;MsD`EMgBW3GA=1OAw3zA1b8zP#ihQY zm_xxQttc%iEk2a(Fh!s`FvikKyLyJR04P)Sl_V-4!8pdjRg(wz96`E|J0o`vO(nrmUgf?Ksv0-RO zT?J&0iS~Ayn=pg7VQ5tvlx*{5HbkRzj?(qzBjQlw9Rtr-1{sX5yqSxcE~rwv^13=8jJ=|n4kR}6XRhc@M?T%Zh}BZq<_kt&F^$Rv z2&0+#ndAJ9WX60r#`6ca*?MD=K*r=?1lm}3{2CvROio5z5m8M6nS;jTpQslRlAP53So`)uj{kxJFffnS*TPX zSJ?r7V|{mj7OIuqT`sMNJO|zjnwSsA((ZmY;KvfE89q8dheFt5Vaa!B_Z`}V-xr{V zoyJSP1)J^OmvO?EwnMsC2b@`1gN|MYG)3xw%~SkEQTqflYWzvG>&QlYBG#DS6MqRBYcl*gm>MFkvyfAUC%l zW;G!UA;4jv`PVG_gr;K{OoZ4*A94$hwe)-Nw8-XIn#~%EZ;SkQT4Y>WD$4#zMQJ(M z-lhrX%J_sNv7b)EuA4OsX~}7EsT8JCX%qdbNZh3ja{Q|Vk?-d{b{d7toVe5?v4_Ub zvF}41%QO+dQeihm52hxkGdD3_#53A#$3KTY6KQ_@x8$6pwA7-s=P?I~Z;Sl(7x_0S zFJAmm^|mVItx&4`(3n!4@_ov0Q(kjFluS0>&&tXn#=->gS3z&2vny4nI5dd++j|0OFU7$%< zs0*R4GSU<-3=`rAH~0%1Hkj67Y-#E2X)qcaSP*OMY$3$qH?A1RHjE8G6g{Y`7y&Sh zj5CI^TE?=5YEW_+YpG!_=}t7{JkWvJ$ZUbAXmrLOg=RP=FQ($Vb=0%5@a zOBKBIo&_h4z3jyi49*`r&)kxMPiJ@21T~;gnV!Z$P&bZQp>oWAU&j)UQ{wh~oR)n2 zBw=mJ*4CEfqoicO6T2eBF+f1Xb6b3e3d(tLeerY&t6!1gAlNbGDV`cAJD*~p0V|F* z@maf}zo~&Kq&=p(%F6PlZnnFoZo4i-#bjn?Dz9rTuj{Jo0-9M+JGvlKApnw}C-kuS z+cWI8;g{YsUvc3jAT4%dnkf*beCpDB>f$A%**f?~Cs+=EYH7 zE=7{#i~VG^9Hb2vtyrzA7S4mLmP@UQxpG1j<(6_qIe`ny-l}5n7eVW=SS;2(f*wMt zVnIP=S@EvI@xEfOqOw9(iMmRqaQhl%*kN(VEXy)WUqxTvXrHW2R#8gxrEL|ZZ)By* zrOSOXLKuCZZ1lZpYwLUWBZ?}7Jbrx1VB@EEFW>xBiev|SbO;ZKm7k5hUawGCpT20H^LjGI*X)gzSG2IFWIw&tU_R@H|NvS|x$1kdw|9 zmRoVN+8GMU+rOcq4?03cHPtbcP{RL573wZXnO$Z-L)vPeaxSSQ`CC)~9S_ z5%M6+u`$G^<|wa>`QZoF<7X$1F@3+jMXzu0>-D|hbO>W};n1O9Y-dL>y`guk#$Z^~ zqo5)H2Kb{B?FKM72xBN~l)WH`wko4F=834NsDQ-LN>D?|8IBq?D57e06bTJAT{f%R z57f=V+5A$iyi}_XoD(RlT&ja^wc6}$c220>-e!iz(?iefdPQr{0!QZ%(csv8R7#Q$;8W0tg5{bmK<=K*OAq8ZPTNbK# z2wIesd<1u6pu{67E+MpWTfFwMxOiag7KN4D;;A*LRqEGhdY~R@4wPO9WZVFCoQfJt zUOnU~tVMws<%Y0D$J7N^PdZ}OqVV0wyTV;YBPg|iAbOg=y*+caz5Q5Sndz8eDk%Yt z?MzN#WJV_0Z*&_NBf{oiZ2aYV0M@udVda6HM<*kLE}D*T&1I6^z_bq$LKv9<8<3zJ zjSG(GAb`gMVT&We9l#qqNO(Z1f>H~coa1sGrIwv;q&H@}*S~hpiXzIcW_@RU2M}W) z+7;n?zOc@IqbR!UA8PEonhYAe!1OTIw;Ktrw`TONn_7o*>QEqWq z05KfUt|;DPa?~=aU@413F}f^sR5FX>oviX5{JnmG_18N#0COz2RlJi`jJ6@o0nE`S zlf41zNCenGYNM1-Gyw2;L!VNlI_RR5|M>ML@!h}t`A>iU2i`po3ny*3Nx(MhG3Y__ z5Thjw$Go}=1dRY*5(Nd%Vv38`F;DH+LQTOn0I^dqh#6Y(qpxGU1o5pWW@Bzl@5!=_ zf~VSn_&2X}AN{PpE%M*6NR}a_rzOW1C9|Jp0^|rqB7L8fnuCy&jKIYz%%t?>gyaN@ zf!HJ`rK0$s5cd}!>{s!^JUfnc6!gnE34DGV9Rh0<^lfa5F;i#&<-ZTX^<(r|;!h@p;3{!dNI($v{O`8FENEgyw>vW+& zQ`5#~ShvBZqaoc!2t%DeeC-GJ0AHiCu+Sg!hnjktj3b0NdJ3UR@e|SzFM&2XaqPi$ z56Ghp8$HY)ZD?s}Xc-yH+8AOoX%9tIOYhj2eki)3kEv9+q|$(jioVy} zZiv#m!C;8Cn|n9tasn3T(M7{X6hlM1d14U;=b&l8Hy9k=PpX(o<&62G z^PimPsdA?BqSoS+3>>@NpN>&ckvNYDS9}`4P%?dd2ZZ5)5r-39Ri+0#D5}uVDVd)3 zc!c{`iQsRPoDZDicn!<_-kqQ0e6zK6vsLdYz6DdXer=6yMyCdZ<;Gh5&D2`` zRM}MhZEON}%eHT(Zcg*^);hZKw&{JrEmbmw(3ywY+4b&O@J7Mca1p$i z-S1v^t?%yd!rpv;e|=qWNi~AML1+WkU))6nyitv&``)(eV<+>a!zGxRgF|WueS>wf z!gRxCzxUbfKEfbhViOs21U%+Wd?$n~Y(6{GDX`_3TlHDrmzPW5!+T?HPWpZhL+NVy z31qV6)^aHXDyw^|dlsw1T3)GGwJOTX9S-6C^<;a#S6Qj3#HXUN5`Ln4?<&F7kS$v* z%a%5W>=z5ywp2$O0dZQZA|d{HBk=M&HZCpA&zd_?yJU z-@oiDefQ(b#CJ6P^7lU_zN0?^9e>PahpFtCxi)+%vjv+G>Hgf@+=3_7g@}>aPsR|! zcl)&neZ$$?dy?*xWFSG-S%fqq%T_6H(K`u%Lxx%#q!B*55 z3cfA!->^t18RF8CVhn;f7+4G5B4~v%B?ARJiiuzs#HA-ZPe`Mkasv1E;;6>1QVH># z)=$N)gruCDw1h|VspoMycpC9C6;BqWrsvSFC#R<;L2ij(M?_721&t;zBALD-dWYag zaN}{fJX`s4)kzq0n8ZTqDm*7Mphd7je;x+x@bc`jg5pn#?V-0 zqouzgt7fRh@9!U4%+e3(vta$zFx1ei-vHWZ(GS(=8EeqJUe0lB=;6XKwuo*j$a6&D zXAWPEXsng3$R%I*nDa|ZTfe~2q<>kCmkVQck zr3*qJLABaks}8Cr%*~f-Z*9=)od;nf2$HBubqPa`37`!?57nKMhcH3^q>@ZX;KBix zs8FR02&#rq!yXSD6TZOJ*0JZ<$*tVr09GP_IECFf1kBjN*n{RO%pslr_|a1$$f9sL z|L9pOE?y(#;W;m!BGgg;vAF(xs=l6^r%aUrfRquuSSz~)+L+3l8pva;kv9dGFTxkM zc~HhqiJS%-Z>)*z#x_`_U1+Pq-lAPIGeRVrzfFMSRH#)>xo#2}&+Guq804bLIGrL7 zSCjeUPqmbXNf)z2nHZXRH5qxxj2w+eB7@`5u7D(})jpt}a)s>05xS)SYtX=Tgd7Lq zilZH^c5s%_$PV%wE|J~nro;N|KC~*DUDpnw3m2NH%KDUbO1{}}J=T;Tv5TRHtAanp#tP-klD69~tK$D^n zWK!rnF1+eB+S}i8=zf46m7n03*()sQQ`$DMx80Jc{kOkb1oS%Z~oBp-92w z7M&{xF@vpdi~RqyNQ9__IG8;p6eT35Q7pm+7lKF%KWM#ClvBhM!qoVjg!rViS7@{W zGNMSgb3Q*OW^|bV+p2`PR|sLLN!*XDh$bj-g20#{M4d3uPKu9*-2`}xh;C`|2`C5@ z#V4f;rw9sc++@`DV2oauri!fTpVDVfp8(>J<|^o}1vsAzv|tR;o=+ z=$=9!6RId-(`QptI2-_X6kTI=p%Gmu6f$l!pk&fi817u@>GY3;3P%dV#*sqkRgB!X zZN%6R5@`;&j&}CInZsy=>sQa%&_>pfvA@UY??FGeF{C!ekl+{^BJ8o4HH3a{LyH7N z7WHHLjiJSw-mxby4uTp{v~trUyHEgsS+5tY=+ZPSJX6_mbv(acYYM7{*0Ati( zZoM?LqOVH3vKgfnQC(?AUF9;$91aE>wYBORXk*RI1U}5Q6Y5&!e67-3tFCoA=TT;v zuT?p{wY9at6}9s!wKF(BU+eU$=2a85UN0KAsZ>sQai|WQUhkQ}94g5tC#opN&QDJF z@$s|-`HZ_`W^N21+wl>}jxE9%cZ4^lKf>Uga0l$aO4#AtQ}S^@BswJZ5+pc2f+Omg z-YVYmY~9k>v$i&UUVOeLA> z`=aZ#gE0ph$6n0j&75AGcFfFlF#co z&mYXZ*v{m%1`YGa^E0=Frx$^bNp1!^cmy|&BNxOegPD^@nXbX{aTs@iz2PE=LJ!Pl z4r;HC1|zWhLS6&rYWV7$u7IrgsZ8lLx zv1QooOG`G0Vrj-^^V#jVWzYCxTR`U(X@=BlgFfZNws1IS6_`VMVwHX=mzG=K!`pmS z3R^IAjh$PS&dtqP-|xMbLYY!--LqO(%l8O&pgSz!*FZpfC#Os{%+katmT40}+AQeY@Z(0E5s#@wOlqDLGn-rXtz7v2P;$ zf&+;}!f?#k``aS_*A@w*hGdRcqN<-1p9&LeZo-jN#Kuw-ozmGc`zcYMOrbFWwo<9d z9K^sQNGeK2=u0h1PfaaK!j>7WIg*&Hm_W~^7A2&`$D=1m+ViI#A<#g8=g0B#Lv z4I0nxRY@?bN`fI){I^B^+Kc?>svlCmPkHeIQrMKYggfd!1R4P|KGeMke5hs%4)8eM zLZOlpXp{#&6Y^-Ru6rvFtcY&(a?zvSwE0$8eTC$o(LiNWPPa{RU3CD1&jc*cq`qkr zAj8xdV~K`oUMN)p8=T(=7moOwdN%yR|BGh)og;;WJ_hL6H)X0M6)_7G928WUhOp3s?8IGH3(81sOJw*Qvq9)yR|uMFI5L9 zqj*&l=%oTX^E3E>!S?HnfW#fsM1v5hIN8<9dFDQ*dAA2wXP-Fh;gK9qZ%OW^osaHc z(_6O*DO+r6K0vr*`qpy}_piHYkLPx}WLk7qsUHA)bj?Gs<93bn8aKtB0q$JS zq*6MTP2H3YlyS$l+xjvPLD#?#&8zRi0Kfw@Q9>918K;8Yu{Lv+m)FsOHY#0L9eFgE zfjTz-BF5i=gfB9;y9i2LO|tvfRb-Mr!h$1H^rz?C#^hDzWd3+0 zvdzVmLE+31ffL8kgEoRPN<@2w1}bpgn1!P`b3?xpuz)7^hy^N~4S^hXo#t5`OHZI@ zEFp^Z*&WyYtRMiTKw7_w#`vS%yGw*D>^p1()(rsi#lEDm@7wS9?e;I|mqL%C2`p^P z31MW|zhn^3SXemZVu~Fs(xr?!HW@w1I7E?ULHY#^RA6TgRqUJ{+|UzlQcuvr$1Y!j z#Ib5Ee=lWqtW>f39{CMLxuSg4Vtv0Vh@uLsV%1WvIH_c1iiM59mI-%^zNZVvsN?;p z#qr(&3l6AYxj#MO42#V13mq+Oqsy|kua~7iA{+7}kKdJkSNh#|KYsTw|N0NV8!mWC@fWbbfn%F!la4rvd_KEi zf}>dQ=yDZn41!y)xr0an_ZfTa5yINd>|sGb-~xo$ycaxLdZRip9JAznY@5pLgx4{@ zaQt5pf)SWse_Q1L+9IL8OlH@ow7=Luq$hDVQv4-jV_g(a5iS9t>Un%}jSMl)sU;CTvwak z2C6pYfojudlgYGFC2s`k*sNNS%iqd@F9>*u^o9wk54p({-rSToF{J~!j3(12oWFEo zb{$2p`cTNPThaM-_(P#(WTP|O*=a27Y$^=(bgnSyFn0D7cJ_opI1lf6`wpM-6%!Xf=&FX{sS~FVLe^RT|-da`d0k8!`Dl|BAGF=pw zq69J^Q*k@zgWO03ALref+ZlAi^Xu#wL=6x|2L`6m5SES30XdF8xsTmj1T{R80m;@K zCp_?XF7cpt5~FdHY~7xpGl#=7uvJog17&RSKs_6NeXK9O@thOt0Fkt~Y~Z}?d;m71 zYtv;y5zD<|*E~$&K&7SQbZTab%O>cZa?#asI+KTM?pDve>bJK+=YSbUo>0PGb<7Y1 z$v-8GaoTYalgOUtcZ^S75QyMjDiEPuA-{p*3OvoX*>YohkncT2K;)Q&rzjezz|8-}b z9@4mWV88*-uN}?q&aTGgf)$u6M&r;deL;R>cX!u*xWqVQX(^-I&W2z2_KZV2#3}ZS zdz4w0GHi#i8MO%)usP8y7AcSnTL#+H+pInxH&Wrx$*RpN9KTk}pUNq7tI`v=j53|n zYF&MAEw@4%YfR1k+xjQ)mJ*^yu`Jn)cWeEUhRxO?X44K`@b=#5PrNd`2Wo zr*MaTHg_>iK!QIlEg5-wXex@hy$7=s;TMsN7+8eHVYGKog)B2InftHDaYuGmS$tdM zueZqm>p!Pde|Yhtk^90{r@Z*S8r%(-n7^owp^v(hK=n1OIIiU>@)Wp@1_F&$*H!2f z8~BXN#>P$LH>x+`EgFzt%T3jrrhq9N3M0?4A`j?b_qA!#RR>s&624vutu&bkbohl< zrKt&Cqt{J^;ZP@xH$t5wg`IwXVUzG0E!-$%6jJCf9KqW^0`3NvQbu5Ej$SHR{UaNG zW7bduWU)Q{gfIHh#BB)Kjg2v`tF-9#JuQnv@D_y?N7mv{)>!XYlyFD?5d6)1_3h{( zyV%YRRN5yRqBXq)E!x|KR)xhWW^_@hL1S1$l#YgIyV4x%)y93?nvvvarnmW^xwToX zRJWp(!aZZv!6*$cfjTZBi>;kktLK|*YcJikP^V0o8EypO{DmHEL7M!ea(aW#dG|aT z)}IC8Zw~%w&?z}PV>L?f)1CLuP0-@_Or$Z~ZnRCg^GJ>d+#rq)Y@x_9jSjLUP^#RP zcy2u&*l$eJ+cV(dZuMJ)I_irFd!UyJp^MuALLGOI#h!bL(K+S(;~KQGp4+M7GSAdG z9oL|bW!JBpvbFlDQ%?s?fC_q(SC0%w-pr}ctIXuhTwHWroUY}eq{6R`4jOb^%;a@# zGi7wUql*q*Fd5~S=;PLrAIV1>`pNCVaUctnQU+t~*x*=>{;=Z^v2ZzM5|$gTBQ&nR znq;^!32sN^h>(eP5=@Q3BiG>3WQ4n@bR(^S9&M1oLbRfV1f|=B%UPDHXm{3Wuyenz z-6!Os(YU&I_BF2AeVB~$>#Jt}D`*|?GT(>IxeFy0!Wm2Z8XKdJ{S5p5{h=mf2?#@! zQ|wE-_qJV>Rlb1H;Uff+Ve^5=0g3E=hHcJf^U;J4d5#m8F1|k6F-gKFvfmb#J+S+lvn=0VZ{%1R7J` z(l}66l@fRh8fo=4U76&KCX?JG516V{AS7f8LmnHVM<9pQ zg*Lf-5@4F>3L%fpm^!voC+tT%362yNHidiq;gK-RIQ-~LztIVGEP;!{hJK^Jf&IQ( zHozN&TxF!i*uZQJqp_b5#z@vk!x)=xFpIQ5)-AT5ySf2T=*Py!^ey_a4Wu?&P+K9$ zvDmvvki$SzhUgH=C)~5G#@sMwXdjBHV_OYuKdNkHcQ12$D`63WAFbwgIFAY+4xo=q zU=pRWnQJD^Y&WW=iv#t%+FYwt&L602o7K&iX1eBFhnk>;Q|;zfDnU_Kq2W9(f)i(K zJ*x6LgXj^d`U1@Gy6=wJcf%PI$bMv_b75|N?3_NHCit=CnMUg4&JEb% z@syN&^ps2waQimTEgOG9ExT1bQ0yrtJVH>U_`JTj{wC%1nVHkB83G$K9Wz~E zZ(MO#l}WmFF_|CP&LrT0n#rJ$tDw0GM6daouE}lCNCzJ#MGuus(b;D&-VDu&(b+Vyn-I9!B2x`9YsnstpK;0A-Z^1i1<;RtRjAvNAStwDb*K{FwOkdLsy=FB8A}uJq;K zzI^%e$M0Se5cykBN&oN|`)FfcAo`K!zJBUB77L$(pO}r#>$%UKviP}AG=3(`z5qvLklYS+h zt*2lO@ni^r-XJ1orsdF2$7lx$ajCCppN^&;39z+FOXDwMFOO~gw?+Pni~KJiQmRtk zR=)sav^wPj&M)9K`l7Kar7rNHsyd|#ZDgyy|J;}&PicHxWs;|$SW?ZD&`mx!67+am zS7ic$qZ&YDbEPT_uIQ$zNxm5l&?B2Gra)j5ol{Jv(C1JX?b-l2AXn+6s}R_vg*tee zcZND4R6!->x`!zpO>Dip^dSYo*tvWpMXbJ&&WttmNCRa*rA3Y zZd6YgV@%kLE(&k+{s#Y0Z;Zf^rEdXaw1&_}mRUbE)=rpXh-TV{di4fQN3C{KhMbz6>=E0PSyN@x7K|i2pmpT(CcOd5_Hz8g7atS-{y7C zp9KLZ+)nQ&FOBX9ayak2$Zi~C?x|y&dw|>2!*KLYP&&3sZc8L3AE(({bh_kji_RE$ zw$9IsrwMpW7oSgiic3lcZrOA6;})#HAc^$|o6-6yI+hU*sXt%4DZ2sgI4zsHA)IlO zS2nZOk=HSWtj5gET36T1T3+7T>FM^>HjOR_a-3ek@O+IR$o54Hc;w}GY@ePAW6}JJ z(++MEi>7V)7lSjfG#|`Fx?&QRUz3yLgdqm62zBJcXd^SiLfG-`N5#tEq)^O8gtz(N zL*(e_Av2;K%tR$6LSO^tqUfM92p5i{ZaVKAF|vq!o!tSv(004JVR-IZ-*I&dA+1ZR z0j&dl+fYfl-*tf`x}*6@aHD&l-Ocwk_d9fko4I{yX;-tq{$&?R74%a%ya&cu>R!r- zwQI8ro6!Y$9Z-Xi#oU)ULKY_kIjkoO zoZX-~ut`}AtFWx1IjnV$h9{y;tVQu&0lP2u7Ii32EZkm&-j>Qr#i-+br9)QfAb?S^ z{GJ=A>{V6>2`nbzzrijX6|%PF(b9@#5IE@B#J=UyK6sh;Rg@<7!Rjlq?@i*%mwm!} z&G3 zio$E!`!mZSo0*l_xq_DP7}XzJzdsXJ75I_YTtdkGw#fg%A}R8ubFXs|Y-m3XA8W7( zVBLUln8wt?IML%ghc?~G=@b}qumy+Dc2XMe(bLkop$BcT@f@o(<6@58+(|ty`FR=| zb_f9|tRQ$8pPVF|trFssxd$ptCDOSodVCRjrK7Sy`}%K-{1q4Z&oAKV^*vje*Hv@2 zqq?qMA9>5s=I1t_-2+~s157*Hl0Xh8rEj%1*S?tZy zXD#Z{V`T`rjVLl4`XPOEXrou(JE0$1H0T#=3{mJ*&^g7>8?7<4x9e%x3LlQ%UOFn< zm4?MupbU^jFJbx>oiHnFl!hqgn%kQ>+tD1Qle(5b2cZvhRLNc3)Xe}Igg2bc>fpir zft#}&Ds?SE4&VwEtj&?_I8!-;K__>P^>POl8rFVttK6Sd!2y-?>`vvCd~!QEui+l} zC^=?wC_1=F-0n}JecQAf{u>gH#{+7IXW)|u_=DZdr>8+2CB!kk#Wv^2denQS&x=7C zMW6cOV&IMX;;n)Dfwkhb^WtJ?Ve9Ls&`zanYN}pjJJzPA&{u^C9ITez?zmmcTf4}c z>B>9px_}kOn&6CfY;V&fM(E&{*U@psedvYDDEoa~uw>kY+{XC$pvZQ(j>cW%Adg1C9%bI>SC{ta ztM&+5lx{7gvMy0bp{w;-&_`WgA&=epszHXsH4B$fG;Le&*6h31XTR=fy3w!=NMnC} zhdb0Sz<^^(W4CMQv#`tERdC42Ak?9;!|@BQqnzbfBFN!m;p~#nF0vgqpAUUim{Gcr z!QE88*rc`-sc#OZ9I)cB#_}3-htl#@&_}KB!RIJ{FJ%_TYB_guE4P%ND9WvtySl5?|v%% z@w*?tLw@7UcQ608^r!#(KLLOI{=0wq^FRLXkHSfl*#+5QE0ox3K((%>=OAgdk_JOwp0T&cYGGAmNo3V^2R^MhlH2V`JREZOj;2 z@i!P-2wxamHnLiJva&`-z~2y#U;X`AEiL_U{OZS?ei1gKLr8N>G;q7N-mx)IIvRRm z0*1PZzP$$XW~4Q0dN-mIV}_b&tAQ@i)Qzn;lx*>30G~stjM5Oy(PqLK&B`bfM!9pF zT1gPYtfnEMjDyRAOJ!{{T?Hq&b6azBE$21V^J?Y%yxJUW_NwRtJyST-aarqiyDvXo za_3lgaQ-Z)3Ldy8R4ON^9I7)fTGI#JcV3Ct{mJ|3SmGUUI_UyrjhGb&daR%uJsX^( zVZCI_1C4CSz?SFkv40y99G(*H+Ey~~ao}UgRy|!o5=-!+zW$u^8iYC4>bZH!bnzMi z4x~Klr^bVmoiU<7)e2ChubV zG!Fn|o0%Nj>^6FePV`7|bp5~Vy?IES`?~LGP@IjH^DG*~F`k&9yC^LiXREZ3h-EpH zaaxJS0bOLPQBmV8L|tixXdI3s7)i99#TZyIF;18)Ow0rlve({!?cMk6v-i2X@991? zx1aCxd(Aqx8!p|@>;?Cv)EeH>r2cz9@AG^I1e@>pSM+iAS2e*$pTka<6!X=E4GJh@ z>9qP3Ta+#}EU`e1CJz5%sOX~Os8@k|Ma}V~z!8i&PEXknyV>5<)b6%K`HY}OESY<{WvGK(fRH2?&zSP{kHCeqAdP#M5eHTEdza~oj6=(r^5AQV9u2LZ;w zhtUr!<7+PIE9oCl)MlHIu##}LN=YE@K)p(wEyIWHXgc9Y9NJVqe0ad2U+L)&(g`!- zK3iI%l|I2TX;dU~Y>!%wCcPQmPO{v15EHX%xr$P^7!=c6S|ZInCTSIujww8ifr>*( z^f9Gm!Dyn8bU?zP!W03T(EHi`9RNc<`joPn58MXMt9 z@L+R&QLtkorUTNyGG3$4bjikGb~`;8Y~W0BHo{F;>22P2VGa3cr58>FAM#D8J|^KR;fvxSlnl2Hv4_8lP2NMd-sm>PZikiXuC{HK4A-~z^2GyJ^GH@kK>(=0!WNaU?q zR*{bD=`+nTw{$wXfBOj;96DWomb@s_Y`#dh^=WHM_iA1z-^%1zQHV$B;S*QWBM2-f zZ+?DqUOt(X$GQ!99*ik;k9_ky_dEn}$mQ-@_ey9xDsyrsC-V^aRY}mH4XLcGEJl?Z zg1>Ge@th!oe&)0x7;0!+LbM@sAuTr>LPDHdoI{*z3mY10okQm4Y6&`SHfUSW;&!w7 zX5n06VMF1~Hs``-j}A1ah>X!WE{*kIOYF^?`ex_Cu{IA+kFkilHqT~Hw#9Pt7eu4_ zI*2;zkjGKlq1Wpp#=sqVPx}tf(o#Z<(o&2z=!c`g?APt>JIkF)*Gru`N}SNuCI|!K z(cJanl3XWMXJ;-Q${tQjbIMk!B61@RJJ;8T53`4z)^oWKHk+$q9Xq+29?2VN!`azs z*~#m>&oKVTR%IjCTm^?pviURfXM`H>RSJsM?qL`*hyHyqK5e*EOf6?quC1$J2}}S=vXZ1EeKtlnd$ECCcZc> zIPTU&9*bDfZjmv1!lkhY0$Zx;?%wa4*{?#(Ia^dLMaKnG8t7+3geRR~0 z;`*j)_Q-a%cby(BEj69eubpm6B`a9Iv| z!;E9r4Mnl69#yNk#BHFare=1)#bu3-x(uu=FB5N|MV6f^YL}W7wxfJla~U69aamh) z8AX-KcmfYMR%}-`2sdowK73vw=pfP1e1=RTNf5arinHb3&wua#?gvsg&%#7VAbLm?f2Tx&-b;j-@&vmQOEE8=*>x% z-e0TMQ8I?~hsUlQWF?FixVnFQ=Z-fP33pu21^$w$`Bu zQtTxHxsnwUO#0^ng;+rxl+=?_Nt#HxcpNCM@xOh@fA=9-Kxo7Y0z9!WF@4AzNZmFxg1f=X%j+Tpkesz`!dKc>59!j{ z^dXc27Y(I7fWbpkq*ht8`4T;-rUqU(4H{a|Z+MxSqGX-V#Mld1AQ$4>hy3+En>K`Qg|D&1T1f@--(rM6gG98_7^(t_&P5V%w-0UYqg)u6R^IWrYt;ioOyy@KL?4fUKZblbOeFq*pG1#!L^DK=hhj~SU3py4-cO@lU9SfIAslA>HW9>`J zk!7y$giob|*C{1D*I0KdSUF2P0iZdx{uV84>dTo-Kxj zsmwKdD2JuvsEb7g*!%bIPr?W{dXd3_jE&Gx&2eZ!;^yRlz!r3WRYi`Jh6! zuL9nb9g*}^Q3XeAQy*8y_O<(0Gjt$n)W6EXVQGKq2tE}~EpOs|O4pJ@Q+4|iLcmUu z{w2~!+qqb_tDRoEA^6MfwB60kVRLiSO?^uDH3OnpWx#EDfRJLKdVr-Gv#c9Mu=z6U zIF`S-xy?c~s-B%)R*MQZM0+jMQ`{}PxTsfV)dV6dH7hHeHM&OZQB(5)J{2fOZK2|@ zwN0q8P1s1^utf;ixJVUcTWkWMM#4C^)IS)Fv)vY5^;n|B#l>wvzyZ{-OegfPOn4wd zMw6f-1$ay)t>Q@1HvO#HDZw2v8A(ZYF&Ug*uABmJBw>vblaYu>4g{JbX*2^0($&>B z@Ts)^BG^crpl=kmB`RMkg<90cW}+Svj@Ghz1--5>E32m?#^|b77^g1O*DEH4 z@yPVZh;je4NZ{x<7LOzSiUp1xJuvngc}-BE7#V?M*3ZwFu!AUL8o)7)xkt2LRJ7kT zpu;yBUqik`9e??qzzz`{!FmGh2<~un)z=D-J0io%`|MQgDrI?li@tWq8Kt6Ax=d&5 z#@Pu{ngU1?iN``qDH5-EG$8&@T-&<}%>wH#d2hjao1lO@caVAY?L*?<#7XC&ff(uI z+mJ1krtH_`z)5QZDnsfsqN)tSsn6YjQE1bqL=+X?+tbF%1po-8XX=M#!l+~?(aIg>;i$QQ+Tu@;jK!X}l*hc0$K`D~@Tmk9<5}e-cq2rs6*>-xIOakcYMU#yD5;+dac*h58RA^a z!i_m7HX1@u=<&o^@8LW)#!i*7v5q;YH$0m?+Q#(68+C2$ zRVlASUo1y*baL8xX@_S?DH6`X8<=(2+jqdH!b6UZh(ioG?8_nGut#NEE>Adea|tu_ z5uK28MC9^j#mT8t<&>R0tjZ)VnH>S~j@-tRzJWEDjEcPg9Q=w<7Murg$^M*#OTDZdv4Y7@j@4_0j2sc7QPi8{97d128;El%aW1eZij{<)RKWEvM_&c1V%e(#^qj(EsXEn>c! zAm$+AXm4+?cBrN|A?N7Y816(OGVfbz4SPJyOr{Og-R+V9{CT4K8SNsX@>v^cyY%%O5b{ zfPjN_94js>E+1Uh#>dA;UDj$6J`;67qak9>Kg0gYk>?xZKoHy>z?3pBgdL-{7=R$4 zBX0D8 z_>Ewg;Wxt2@j4n0QS|e3)Pe77Utiy$>1ba(06Y$Shfx0Zmp}c{n`=AH`pY=Q34L6edQNrz2dot z&P=95-e^&gEI(71Da)MC-+DDE*JS}W`=H;E`9dN`Fec0C_T(oqoQxDCkCnuRBS`_$k zSc>qk4*FeeR-8%>b4xjf1Cx!2+z2PakB;1uLwI6gh#hvy=BAZ26_U*((hd*TorZ_g zga)<3st|gt@2;z|9iOqPBW>6*Ioo_!g^bZ;Km=-4cHiw1TBI?3B%`A~ zx$)zBp&8{mH+p;)rU`q;p&Pxuz2=Mj6oCu}PFJ0Om#s%n4qqc~LWBlAnLy&~buA7hNBC8V7@ zh(vR4TB+W|ilwXi^b|22O(2nKEMGXjoRhvd=d0;dy~*kgELYsr%UGUpy9#nQV37TS zC=Pmm4H{C&M+d&tsJWRQNHNRK6lg|;Vsvd~8R;8qHOTxL7kZ9_@pMF;kB%pNa9K$Z zO>IDl@pRkKIILLGM>jqYTqGp02iEohEG!=o)&Z{yysr;nSV?CO%7Y|JdX6r7pv41T ztXNKMPdz|oEYrtSjGbl7)Eh)`ByrB@R8pcH*Q>0eK!reKDnp4UiK`h(j^O}y5MW%xkQ04Uq zc%xqmJf9Z%qWuSm;%F5eveODJ|NPhA5qJo{gt%ZAN`^a+5k$c!NvSyXu_Sin6Qu}=xFd-Z zT)*~^@F^4j0Qy#i4}&8o&{uXxl)j&nPR;m>JFZewnp7Ne$Mx%Ez}s7j0EK#!qqF{F zen^9G6P%t*`C6N=Y;iJzJ=4P4)WRBSMI$3?13C?wa{4vrUZUa4;G(HDU819K)QC<` zSOXXu8HJl#dqF8+VkoqW1`kaPq<#=O)!~xa%Ou<&+{?m3s29*JcF`1BRh)Yzev$h= zgc`xZ2oV+EKIE_VA^(||DMS=+ujQNF&Ey7^Ynj-h{6w5__gem|tfEX^e$iWz0JfEd z%u%|@vdkbFm2VMEOhUhb7W#R#w7r_oLvLG_Oo&VI@Jf(}OqM4vW*LXBxL8&^FUKOq zJr7Ce?ig~o=jG)f{fj`OG9-rpqZlI%?Oad}az=?Ug64{~K{>VDpwd!{{Eb3-p(U7T zxmnwwt(|KT+mjaOLdg^B+}zfJ1A1pI^c(1ry;fXq#sTvj@l{q9Y=r!#<)E@=*c|poV>UCng;X8}^;_F4Ux@CAkr$ z_T^6Yhq?Cl>k%D?CE4~7CApovQQ?eH(c)I3${kL_fpw=e=red0?@^?J6|`%bV(DOftBTo-;=w>lm1=LHZY>IMfIp<;btK zFPLZ`e{{v=%kmm)I9AqHW>-*LU$e5ZQj;+HVQqXRVQu{Lnv1QAt!>Sk?FJrE6T1Qn z%Esu1?ZyWV-auJBSR*|Fy=@O{g}BuAfu-$soTVtKPv_PYLXfyP0*@FhQZV{}i-l<; z=>dIn6i3XI<#r64QKsG?$sCN4VfXvQDb{dgB+?g(-BvR)GA5L*tBJ~t#8%}ScBmj~ zbVBmPrYaN5=s?*->IC!~^$>4NfHmsti8n;rIm8@ggdBoAFwdyx_>F#|5kie=aqMSi z4@7f}{EYN31=7ubCcqehH@5#JCLK_3`1u_~P5Zr$_VbM%ijIo*o%Z!zAmHeU8afz? z_Kg~fJ{V&Bcsk_!fBc_+{ucs|K#_rU2N&JwD)kmw+^~pxi|%#ruRS7neu7w3vWGMf|0tiAxebj43Xji%J@Ib`p8b)CwqT1!&w(S?q{=%S$oE{#nr3=!jCanZ=o8CV$LU@v&#S+2)SB zY1vXk3N|RaDxPVWL&nkjE}1nO%pm->1_ydI$tUQje`lWDixQPZRH-0j6zWlPO{n=| zSZKjX*n17DIOzFas0I<|0FFhZoHr7Hbe|DOs74bS+Rb&b;E$@RZlVsKW1pGhzDS=) z;)(7mo@Z3-NO>Da7;0dq;o~#2w1js!anz>|0p}bo%EZxEaa3L5-*@C+QMKgnu+;9b z@8D47z~ScA{!+Zgk;BrNDYgmDmG*W?xPgI26MIwmz$SDaZtdz#NJa@Tnwr`N2scjM zHoq)0ZY(zqfGTP>)dEGZK^agl4(3Sb$p*d?64DX& zwQU)0F^AHYiguNmJ~O9erRqwVfm)Ig+?~9?fn_RMaOb-%KcD65nJf zrwBb(l>iX*xIsOdk!XiON8%fsRi+J$HeiiaDmm~=i7`iO85A5g(t)ym1ac_w%k?(K zW&QPK#%1Vddnuy5MnLdB zB0B28FM4{x4}%W6AM#_ic@VT4~K;m~>_{ddPwS`a^4R`E6|>{@%zSoUYhe zIVs9a>C8!&`tW-insSr6;kOU@D}BiS%<7H&yZ7%SR8$0xBL0ie<8HoLkuDzL0#M{!=I4gp~3h7DpnEElN+X>)2TQ5lO#2QfxVNJ~xw zOe&QP+K`Z1tu{ofg-b=-Q0uJaqWTt=ZlGADxwcj-Y_eJr#o=7*QP@mm;XKAe4J=Zc z>pa@HQYC^21W_COGgr>sQAdZ$G2Y-A2V5%Ur4Wk;W;5hlXF zG>sZmxb10>G}_x6+S`Z$N26vhw3ihe1RgNJ5^@k<^a}k(K{v;Y(%o@)cR}||K?PnN z?+|=c>{RVk73}y_aNtG-5;%5zjzqvHy(Q$MRjlXmfd^J}w(2ab_ zWjoFm*mJ5vZTCSsHsNp@n1 zq7>ogcCCqa%EZ=;jEvUSjMj<7RGW+m;*C~TaG=X=LW$*xGBvf8d)>-fCGkcoAxJ$M zWBJuWshTe{rXurO5E{tbTp1d z12v+EEQVf3`Mw_VjgIO8Xy7#l96eFd^j@@(cLYC;9tw^Qj*gD*iTc~W{o9}a`j@}_ zP9&Dn+15Ky(mRMg^)s7?t7t)WJ+of?(yQwI%W2v5Oad8rc4C)K+kwIb^7VZ0&9$v! zJizLPK9biHmWnIJA&8Jb#|AY%&g-ClGRO|M}Dg+WnpDv!AY0l99{(v#kUXn>wL(6zGn8*Z}Q(>W0`3F zPg$AyMVX?go)|-ysS`2|o!L*iB7%>6vsal}X0lgXW|>>`IukPvl+=HMQdIY9a!V$g z%#_OsJal<7S&nWiOD4~e<>`n%CRs*0pGAZrn|~#rbjLcS*u7XQf5p1d9Dy8@Ih8rZ z?%K*AP8O|107p=5ZDr78h<2`6+fo}8)Kb_IqOA-;Q9Ub18)_R6#sN$0TtoBC2Cde) z5F(DbLg!{Jy{-jeG`9);sB>GPp5Q{?TZ($2eQG6KfQ7s+309A9{9lMwBD)yyUP1IUVI7 z3VWyWj)>BdQhr+E)LD|7UDC1c1jukYJao!k&&?gq&34Ll%pG=8bt3y~_%Ll9V~%7M zfd@t%X~XLfj5=oT?q;jrNhTF~Fwg#|dS|X8j>tAo-c@}xC-8X2qR?LRca6!7$sER^ zF+U;7P(2g0@!q`mqo((nd05!9_s`6G_W&MYVdjVc+d~LgXjrHw^qppJv7n$a^gToz zC&!_Cjf>p{d))*abT@N?rdSP@E0K-eGdlzx8mKwwNaRd`Pj^*S-wp&E^t6B{82dZ> zK3Jzz_*9^=4V+OWdfWD^c9tr9s&-HpdqkA61KQZB@ZU$7TU8S@r2C?%o~!AJF}nP_ zFw0OQeYB~a;G^1M2_lXoWS`UPcFZ{1yByR<91(U(?-Pj-gSc&WbwRFiI^d?Rc55GK zT2@2AQRBAEJCx-wtRw|pEDxx^xPdsN-1C|NbqypQE^f0fUtod-V4%NkW#G&5+UyFj zV{K(^WrZmOrr3m)gmLlIwxf%aHrp4+`lRI&|r6(Ww86T}|$T8SBl zBGD%GB{#*sY@M*NnNXyr5_r_t+t^H(EtDCjP8-9v!nW5|PW>Y6c!`&yq;1;x0C+Jn zEqdD)rWFec<7uvoJ-`|Tv=OcFTkxAcP)rYD!Ll#}n+k!4FMSUUMMX#PWP>2%fSwNx z_4ooZe4joIj_!$$x*Z(_;D`ziei}?qZa?jz$DZ4N`?o*;`CtA5dyD8wJ;w7g^YJ;?b7!a3J6JY|<`6`NyMEn9V5o4mV|a`b5;~{f zfj;8#9ZmujO=tLLgw*qnh#MCN*%0zS)Q2=MMD8S=bPTN#&tPamUlxXE3IK=+FNTYC zKh9eZ4Y-_CI4k&)nNEdPEJW}U(|TwWh2qfK=q$|2z{CQgM=zXa`5!g)GO)nu&ETO0 z1O#Y5#icvKs*rnS8h+7)qrJThO~bvs46HAH`;foRhx`Y#`&(v`M+Km9KcCCnh&O%` z8DK?4W?4G!kIm04Dw29+^L3DqzLLu_iwG&?@}e!7E^{(dCfl0NlFiF>C{U4QWg_+q ztt#`ApR(j~0+300an9tVd(J2MWHCgdy5bz9aTM$5i%lxr6$@3zTuu-Iz}TNsh@|r% z$)lnT2@0ug(T22e=2s!JMo4XCNJB#{3f#0IwbJgyxuuPeqXEX0W>$9GEOc&b)??_= z)&TTCfr`giT^&%P&7-+lKSwV-#@h6C5&FVHPyJk7VV$QZF-Bd7r$>Zm-B{gNX}M^s zhpK~UqO-$ZGNssezyhm3dx>#JX$k7v@aiyvOWO!4O2o5u``id8Nxm@*`RJjOlZpt! z$XrEa02p}q7xR9c>B zPC^$8-iLrov~bYJa9;X@B2m z>0}1GlO^s~+36$dn5o)9BtSfrh&8||kKU7OXLkZ+(-r3)=?ZcS=+ds7pU$L40u(SUl>ZJ?<} zy;*%)<5u$pR6+bPuw1QH&#H+!2Gp}+>%u*51RFJOJjQ@OrN+gD+hSdqH?V418=$um zkaV8Ft+C?>8|7!CD^lW@t?gRE2bb{#=t9SNx{u_34bga zRk+2?rYsdYQnaenPrNKsD2!kCPbd_v3k$}rFF_T?uZ{bOH(*xjSM+nc3Oi?c=5f%9 zvBn7dQx=ePE=@Uj>JdFX?Kk8X<>%|W5XFm?=xASwI(mGCc$D>{Pkp0@2rq((Kj;?q zG&qV7gvcWbLyxFff{@^$*x1`cf9Uz!?Z5r)zy8;MqZ@I^pa12rfBDN_|MENfA|mKlhh42A69Z>^Hki7)-gz9z`Qvw7>94>J zt|ACDP{=#!PXo`eK@v&l+7>vmtFl{{Q}vbcJgL z>m=JS4NW=Xl2aIvIb~sJVnQdPaHEUD$Y5v?P8ZsSRyenLU9^BpGu-r|MYxxRwUKE! zpR}wn{J)6IZ2EeL?saSWCoH%x-OAM3fG+91=nQ8SPUkl(6qQ*CO(S2>TN^>hKzG*g zXQFB3p~*#fM!$W?U*$vo?{|N?pMU?i`Ggo-Z&|T%_uAWQcek$PXI|T42Fc3T6`8%w z=khkQEwjwbqWmlwAqWJax}tp9D_z#B`AnH^GK*ekWyBKZ%d=$e z^X{J}b)S&@m6xN-a_7v^SMJ3qjAh+u4t%mf#g#2BL6C;Nf{+6dqdBiCTPj<$1RRBP zmBbrCIhAvjIfXa1Avbf7I2zKTZK!mf)1ths0dmpWW^IczWTeCyZ8ymHc_Il4wq-}5*M`vkyX$gEP`pyWfRMsOpBRcdEbVSdijgpSxT&K>Cbv$)i zce2mTT`z%g1zBLjxoJ*^5ow3RxsJo@!&s^$J7y0{>QL-f(o`s`r@ORl73Z6OOxykF zXr4ygvAYXbipu<>d2%vVDrx4)(2hPMtVr%fS6id`JJ1Kh!5ZHYl4t-K?-4Yrf-_dr z_+HaXAcAEIb}M_K?|QksEwnLoW^b{#Ahci+gN{&5cR}chrr>?|UT6VUE}DXw$Zm~D z{lfiB!A|6Hq|c1c@ly8;M{%GZ7Rg_IOI1~H0Np?$zoj5!w4wql6-hTLOtR=|Tk>bE zD41huzoH6S(h7g%oBLNdEVUnXEul-cYl$JFsk*D(;iwwrZIFv@`nOlp!7g-ItJ_KxxWT9LhA<<> zZbJD+IkB2Jm7!ckZJS+UYogNbO)C+F4dkL38Om0AmWV-zO_{RQ=4E}^%X)=P*+iKQ z$Bowa3q=RAzf#Lmd9q==Fg=nw4R^}RiT;-a6^fD9{r!S14tT*bV%#cpqa((CBZ_`V zI9?kcKr7mhq_2bM{^*fu0*-|Pq>TE#jyi~Xy%06z8^!)u>{gpbP~M zMMdBC4fc(SrbAIfPotuPZ};2|4!%7U8ys~Tut5wGeBnZD&xH$5>EXiDo~YPIc=F=O z?H3m=Jc^C&x&7#ZIC3F2KK9CuD?L}@uROXEf8)xP`1l()=;@RA7gwHKp?Bi1Jb4~} zBmTy{E4MB`e{t*b<@n3=-mT})Z{53e>-ptNm+xJ^aqm{j<>&XFU%o|OgO^fLQU?DJ z|A)Wv?O*8z(D|2t5wOFcBdvZU(Yz|H&I9j;nK{HJ#ybHmw8f^g+An^?U?MLiZQQWhBI6ZmbYN&`h4y0=6( z|DQf2omav+)EZ7oWI52u%gTU#lynaAGJ#FPz<@mq)?W0>COAPFipyaRt2FhJuE@i= zj@0@huD?yCR4P^iAeqVc%reD&xssw^M5jv46<9OSIIPkhb2Et zr;{OjLoU}9$>rdQ%uK|9eUfK_FX+{ze3BO_yh@Slh&v{UEppst#b~L|$q~lbV)sgn zHuAK2XmXyPdRZLg&K8xNNyKp!2TjgZaw-QT9C;y?bA^?)m~9kld9O0pGRZyl zJoKnVF>G6{vsT;C(B>?%HwZVlC$_e(whr5sI_E=}eVv{cDX^(DbM9AR1cFE* z-yp!y3u9~@MjW18QLkr3N4aNbxo7!Usi!?#R66v;9AgpohnyqT%rthNp~;N}9Pf6`pY<-HMFsPXcf=gM8uRxj zY@!V_4@)NSSbX<>(HyglMj_ugftYkLjPtz^ZN3+(nSrE3qX|9caYy$am#IX~%s?%A zLU+edb95ii(3enmL{`jHMG|}vTP(4JgJ=U)D%h!D#6b*Fh208{RQc~$`S)?u7ht1m zA8{NN{`*Ign8Ux_zstYkXrF$;!QWv?$TyCv_Z>u$io+5{9sUkon~-#LbvcOajV_Mf zXhO`_rh~(ggW7@kf?m~Z*6<<)T`KMBCcwq!CXQ^v4qJn0QS4OUl6B*XdJa8bUQ^c$ zAPUTldtqG$Y5)|oD>YvvF$Wi|%zmg*^F*Tt$3{m#uYGWFN$0mQ^srrlaCCGdAtAw5 zWN#2|V5))6*f^wf#My2@%@MbOFQF#2B|rgp&~J>ggk#EbbORF&m{$N9mQzzxXmX3O zRMI1Kq#zHarJdc>Do2`UtiF-N90VYVcFH%>Y-4pQ!!Dz>^-b$)Mye7qqp7QiHqWq0 zOtop9NK8z$S!KaSqB50xVil=qQ$b(-0@NIe3B^R}1p8E4*$AsJUJz|=Xrwo8W#y;> z<`q0a78tXMf*$*gN2V2i#$4a_^8zBxff~_5(?Ps3;v03a5XFAj*VA-2J#;X109gk+ zRHE1`OZPoJLs3!KqyR0Vffu)fW9b$g{DSU?FX+`1`oeEq2=2KM8+#kvLBBx^5*vHr z_Jy7&u@^2}h^M2m@$}^Oquam>!j0Qko<54d@hBeTL9ZV@zjEX9qgz*=zlgtl<1%jX zz>4S3<5TeB`Mt}RFWtNM;@gOP5kE(fz%_>$h%Q8sww>>Ig^#Ed# zlz@OgT)i6b2e8PW|H=dc{s@$W7Qc*BPAf)T@ z*&2lvp^qg^=N%#6cpUiEVshs!uM6uIZ|PqZ##kXGbv-9);J6O<4)?f8vyuP)hcu8* zFX7?hd}L}N)#-f5oI{$7UulJKG%qNpttF(kHYg;hwvy$d+K>jUS6Xg15J%9Ru*1TiQmd_P zzUf@sPzWC^8)6&iZtkYmqm5@9&2?kW%|sh*bLRraByv|Qnq`ecp(Q_(B#rAPdR zd0HCE+RUFNKYJ!%;~i1QGfw+@X5Od~v0xfaV{h*}m{L#>8`h}NgzW)5%=cI>+KcM8 zy--A(pM)acJWSfCEbjI0Eq2fB6&!cJ=epQXE{r|tE{N1b?nQL7xtW;ogZEtdL_*xR%4he_DX46vpse`}~SXGYNtKngBb8y>K(;uFqlfGT} zVmWk_iNcLX8=ww1H{^?MBJv9b^@Ja@hy|O)+skV%1GBRnXFj`Jvo`QSiv5~(shOR1 zSpjSyYBT}Gv9oI~<1T9n2`-;q(i3cj9o7~Dj?uXB^thFTjqwlCGy^p%pVLPH9++*! zJ+MvRhGQk&mP1AfH7sMeMulJlLXLC}=-6QOC`#ysiDeZNk14LEx0^~zim^*VE8E1> z)b^ARjwafzDiiHu02>)Gb}@-lcC9Fl#Y!a+qK`zIvl}5sYpPO-vU=r9;a=eym6ys^ zfCs%&q?V=DFW4B@E9!+R6rrQW6Ou-hg`@DTOiT#;P>6zh#mE9tNB=Ze)BC+1=~wvm zW8(38dir2mtW-w)UPnctUB!S4}k~q2jRw0aO~3y7ouXLKofur;)^F3Y0#@j7nnNe0FK08xPh4lVB^M+*}sbazR* z-Mf`Cn38fUCFS~H%3y%Nk$blS0-j&}fxzQxKtRCNtJkky9SjKg{_1xDS8ox2{F&{( ze>`)+N;GljjySWsiiHR=G^8RoxKTN7lt&bt#U(r?oXX)U?NLN;y@(|BX3>ZD*S^_6 zSC(*KtHN^-Z>dJ*F{g^1C2Zg=5#e!WU6I74|IZJ}@lI$l#|g+Q-Y@G=$G-t^CzSX)?QFwgc(y1q7~bEgHJN9h{e z=%O{B8ZXjOQv(b7=Q(}b%1U@b>3=n%6C#~i4ZRG#OicJh`0Ydf-9P02W_Im9c!R|o z*I2WWf1gWYi&!?AWwurHlTdQ#uv^LBLW?X4>|s{X$v)}iI$i#}Jo6K>zKZ0Z_P^2teAj$B^+O8zP<3rK+^4)=K!subtAOKz1)ELJKxfz*8t z@uNZRaKQ!@*J@w2uveuL>E=Np+DfE-Rf@>3mXN}mll+Wmqea`GCAxsd!+EYYMBCiv zJlE#j0B?%(92P9iL?v3nkhZqw4$Lz;8rqy;jzz|(hhFc|QCAmHSXVeU=GoTa={Z(j z=NZuk-slhk=N{0I>OJeAA%$QR?XQ3jPx}bZ@mrF4J=nWb5-lP!%m&UPQ%0N*^a7CXh9K5nwFc*W?0VJ7~bXdjWpFTu|^s+ z9myY)Kkn|LLDsx+mp~&MywS)2Vs5Td@nYp2M~r?X3h8BI3Zxso@1C(&C0WCBjov*X zjvKy8RMBLBv-TBEG6bw&={vl8@w8*}-CEXP+ZRVTgq^bcs2mYM+Z^ z{VUid>tEfDhxQ7VjW#(fb?q}^5M4A?cU3RJi-LX?e}`&>ZP3vs_*ITXfyxmbMZau2 z_A74HO--D>QRCL-h6?&>_2%ZO`gCBxtqJyFzT{q`*|g9V>|&}DGBUN z`3xn;c--f>(eZTKbSzAOAPEU^V37^Fiy^caCFt0W+kWr>3Q@}k3F!x#x<;wab z%T>#mZMLkuu}o4<*=0;k*~Qo?W2UybIFC=>^ZgR*|( z{-2GFr~B)Tff)+L&r%E+P-MYavA{(uFvOxwW!lf!PobDz=x48r$mQ^xj)qYsI$F|- zE<_zj;a|Q(JyAnFbgTyw(rDiv0*uDi907v#+`b)qAsX|I zC`OAOf{v%LPai$HaGQwZ5xp6E1K{xpKyu+qY<%pED^IS(KPAYxapeU#<4XJ^`k`kx zp5Nf{M*NLi7;D^%2XVy5gE|N}p2t6D%(!*y(!If3_XY_p?p?Zf=|u_=$E`~*Ze6;5 zeel+$lt0`f$Vf@~L(1UQlz{8EQtn-+*Y~bpzY6*o4ER3a>fqq{AFf`#`iH+^@&WST zRyID#e~rQrixsgKdCaR2kyP%A08x>kAtiyiy8dD#K{tw+Q2afgCa}bNTllk}t7MFVeXP*^}XHSfJCInDcviSs7ZG z!Zhh+Bu-8i7Vt69wKqa5aZ0nIt7}s)oXD)sw1#|dh_j}lctK}MBjEuxq%)}j_j$0r zg3g{6);O6$I$%w2(O(%|G~gl-@eko5T#C*7_96eiAM&66!R$BJuECyipFrapxZ^Hp z;@Y*JuH6+?^g=j_d*pHOW~InXmXF8{Ofs^HvShCi#UVq|S0)A}!$Dl4n=f`Rmc#wZs}r=R=vXWIsZwyqzY_u}W<* z)+#x4bguH|WQ!L1ji8V=cvM0f=m&Wm?0O}>&?1flPg(DkxLFXRL&SPz3#1mLEJdT{%iQGAf?5>Dpmx@of&x}u1#SGn7z^hWl(~O-Z_NVw9iExgc{r$cj z4m(F1J`l?0bDA>c_VacJ-;i$c9A8yzthqF}*M|yE3y?9aW&?JiKtJ|9#P94}W zdjvb|rn(xHZkTB_IaDKn0~Ky=U8qzM5npPzfz3^~WxTQZg-~Na1aN>kzKC!RVv|`l zXk*r;MijQW%+3y|nJre3(BU!wL(1|j_q&P4HmQ^zsiO%l<6IJp(s@x#KT6c`!ItrY zjVj}Dqv@mYsvz`h)OOo8&ek&Kvn5xmh}f?f%PG9V&?9|&$}#~uj*ThSioOx5(U|S+ zq!`dfQpReGkc}oLPQ|c4mKb9S#chdpZ_pdtY9|%C(ak2&#?D5WD&j>sdjoMCcv7Y; zvw4Xq4kU}(C@|QlF9Vh!^s5#4z*!rZZt%W@#~q0LV)yF-p@&ETW3A{*p&C^zEQ}~X z8w&axAsZbTnU2OPWjb1cwaRP1A-{vDs09c(qI}O*DY!=-2kwHSf_;fOqJ!y0@KE$n z@Y7((HwZR}IAUXaqI!CI2sVOm)6dfjvB4LDUtEYHws=ZVLF5s8;dX2+_9%oJFK#n> z#6G!w<;tTgS6*O)a^*_=ljlSjL>o7*Jm(3=l`A)R!;*64MoK*K#pNdiA1`j*dVb@{ zWyTPO5Zqr%fuJMh5|1;8Ia0)vlxom$V!rW?c#(ML37_ zA=wDYXEgL`@Tt!W-Uu!-wW14coPP|hy>QKpFeq*n{hA$tu5%3QjgSt3lOf9yg}IaS zE3GB>!$rhsnphdo#j`MHAX^Ha4Eisuadqt_qA2K`Xljj1eJ`VMh!RXJko|4_?L+?k zKjeS8f9?K#o@{(g;kf_PU5qx&GV_sUo}XzZUh=$ynIm6-i2SWQQ{qFP&^Fe^QqD>Xp!q&dcEvIl)PUZ-2incPO7J;KVs8iu1Aw(D<4O))> z0$+qQ)V4G@-_$xc3n_>5TpbvM2t==Ut}B$XMQ;*?=<7I+L*MC)+E|Z5B8iSVPm#O9 zVWae|@5G9Q)g9%Y`Ut#L+HqLoXbMBX_GubH{9rTAp9La|t9aY$^WM?~OrxA8IuB*~M zs#K1<&m7++vw*`00nX48TMR9#E zFH+t&a^@G@u|y$zXjZ}CBQ%T%W0B_@p`18+5;~)qDbR#=_rjtQ85$WmbAlZ68N!T$ z#>koDW1$-**641WLDc!q{>jdcPgS?iafQ#)(hM6?D!4`kv_X{N!!hSb;w(pz#=-fbN$C$PV~93Zx8YJr z$8clHay4lRMJo@slQQg-%A}Z>7^Mu5#vA#uS&+XJkxd*hychsmctS z#8$CDu|cg$Dv^e=)kZmi6pqx?m#Lg=ZZko|p?LYy#u&SmdWB7WD@bC&cmhO0gt4G7 ze*GGU%21mEqbz+d9E^y74rFjFD5i}^{1mSj`u+SA1Rr!G;6MsTKPQeZye9l0yqHFf z%1|^ntDN1Uc)sDwtCa(mjNT58>Ise(QjXxKu>>Qrw}YP&al}4lr)+R=6dmlr_T=e> zCoiy1xqabD?1dga^oX$I2E?TF{p7`s_zO^N+_-Z4$`i&7CJ#tR<8ixu`4QdUz+NT( z`4fybF2~=Czm#&Bu;MvH8@LI=xOES>0lv7Day^AeGAsa0fXNK{P4rofb#*@&wqFIZ~yw&zx)ZZ&Bxwf zqcGUk%DWIAE^tA;tC(x>I6^eNomJTfo^3(So#{Zm&kj8nD;T&`q#+2Xh8Hu49(8^E zSO`Br7lEQtC6FuXMWd|r&$~*)7M$xILO5W^wg%WTO%1$oHnKMO<%W}M3^#dbV9m{B zCVV=zIJ0LyG=!*t|K5U6p5H#?-}yuS^KXB9e~YIV_u*3cEn3^Q%*?LcHM^TtWOn~8 zMjL316-nlqMFKl?nL2vNl)WMvc?FqOW+l z$tGp8Vp*O%kCVS(jFsmVLn%t!ftvcs;$rMnIP%L~%K9SQXsIk@du*i^8DLnk6b1z~ zo9X;jV;Wwf<~gcKHH%Hdqg>7upH4e+NBeLzww6dZbfs3q3$DAbP;Sj>5NG_*A! z(OjyCMFdAgAtW3T9&L4P9)uYlb^1Eby7D^DLQkO@B?^hCEBC|-1sS8#U1>x_NvU3} zQ{Yl50cz;&?ISvzI!a2NN;@&lD6x0aA2M>-^RNSo(P4U_aym>?sSd#!PV4l591h1_ z$Mv+db;q=iyiVa@b4kI0c#e;(7gc2^51Ts@SR|*>Pqqq5QB^Y6)W6G4-W9IcUvA0h zjeRfX9KB(SVZCsryblZ0G=?>5%)|EHVZc#f9`^pd01mdq?uBahH1seN`o4E&=C~Uv z=Z*X@0}?szEjW(sJqe90&=h>_sjouF=yAnyG( zQ#Qp`pJE=eDOjWF)S>!|gIiMyyPEeXgcF-?YJv>tI+h_8 zb*lk<2$iXui;Eivd=YNUF4yp!Lya~S1dYPGGF#&^u(smDyOe}AVVwnVu!IAOQF^js zYx`j|F75+Ywr%hOf<=in2sTC$YfdckfYlwAwj8~&5w~r-%_31~MAPFeV^(9}h-IZ{ z5<6j6k@v;FY-cH{LxCGf%2l3W#K5JJgyjkSBu>~(Bod9h;rT}ftg}QQHo_81$N_&! zYO2l4RzeIVM4=Ne>nB8Wo6W>T*@R6iV$JJ0*qlciP;3}aC=MXuP>5oB%r_P|+ngsK zqNaWU9d56UIr58d#*7>N(F;81h=xmL8ur))KfkEgey{tZ{i3l;fo@}AfoVfnVxbs~ z@(qsWh;!ekgd4%ZJuKuPx)>tX7)OD+rhVkG16cK2LVOwQz7BF4OIt*9K;>5 zw_^!Ju3U({asgfxf)7kOzTV=6V%eS6fesUv)&;z558<#FW zhg*dx9e-=C+u^H+cP$MaXezk2@1^XJchk8#JJFxdES*nv?1O|nw!JlCm-lrHZ; zvF?!YfOQsMeY9tmS0O1C3l}ugOA`=c4FMKsCX_R`E&b|ak#FuT_9*9Ij0Nzpx0X(I z78Zz{G_bOQE5g9^qR6Dg_#T(m;cT#ks=z`z|Ao^UTm@8I8CFodJm4tv^K?tz<_hV zjlO-zzxRjyr~l=e*>709arb^EQO32O^7HTBH{%qJpUm#Q&B}aBB!aTGBAr>0Sy8@O z)-~N-v$y$0W?5VLSw&g8SA-uZqnA%&z%gIM-6>m{net3oR*sI_V}Tqw>{OY|na?5O za4*gQVz^J{<;gMT(CKtAr{ulDq{F?EfP*_#aw>^Lf`YU`lN`knR9l==SxdB0IoZ;Z zgWk4~n<0fEIdd(wXr`|v3sSK&rIyc;G&^pg)o1Gg9wGGWJZF9BGZO+XN4bFPb z;%G*zn{#1vU0t0=hrYqX!`YdUql2ZQh5F_(A`Z{GF})`}=-coHyHj9JY3__b00*40 z96GA!0(zKZd8dMg`clu54*Sx>lJa%djz-uY+OKz(ltL(qoUd#S6kT^Ji6~ij>g>pM zDp_BrH-lfem_BQT?aSF%#u*MUO z`Ma>i-aQQj9ASH5dySz58ck#9NiRIGy# z?DthIv39hJeqrgT$^Xcqs~S09TnXC_Gi+0}1DaKus;isYtGkX+N)Nq8^;uvCJ-~^= zE*3WjwJ1`lW`rO>830Z2y&80Zgi*LtaCG*|vS_Ry5S=PDYQhRP=rd*oNzB%$)w2UG z>YBARdJjGo`h}V=Yb$7`$KYc;!DZY9eX$7%8!n$m2|6}L6UIjqKCHwg*p81%ZWVZA zfgfyEd4M$w2aRI2Vrgp$XY2!nf5qXRI3p=324jwd1o&hvleUwlEJd5!l%>+rl4xX` zuw%+@J4vud;)F6LX~IsqnmDz}+EK)tPy9YHfn3yFP`p+|kG%GaUg+gl03qMk+vLpVAV{6sRVV72lz8fKLttXX2=P`Q1f2f%URX-{kqp2a?5 z?syV)JN6MzJn;I$jTiAO6}SDy1uo5%$->$1D^LWIXT|30<62E}B(fbM=1Arj|g--|G5{ z6U9{uCjCVs%FY#`8VM9eS#PPY7PYbG3?#`wxYb1yBSS+g4x6+R+5l_J@5O!}=1L=R z1~jENtf36xRH|^~O5wC7F4!$ZIeNG?{Tn9e4x@9TktxoO2H~bgUUW{gHZe66S=w}3 z3x}@J(9{5uN4nfL!O7AZ_3KE6c<3cvq~reEhy43~$p8GC`@bRDFuQ;ME$ZlhGBaa0 z?A@Xw7Loph_LR(g#GcdFT^Lr7ZoWk?sjH z;|qqBk`nO(HL(OD>)9&$rQEcV>}01ju5NQINn1xbeKyzBtFj$;ce$RvQw3E=GDaV} zj^^~W%k~rV?6hYecU8&B&r~1`=s7rhgDdLIMF1F&IW*?Uiyy;!d(AQ1Fh_yPo;f$v zzk`HhuU8Wq)=RG&dqXt^&}tl`x@{4h5!$#o^S%HbvE4JF1yFQEh8FZT(vxn2jEe5a z9eN1k6LK(-V8kIN8?eV#R2=zN zRh@}C9G3js86fsC(eSTk#Rihi9lAJbV+n7vK?UjNr_}@;$QG>@YL6zQp*w80yH(SV zy4|gbu!Df(NDXW3X3eRa(1+q4bC2bk*)IcXEKQ`aQIx9;&=2cDd8o01aPwIg_3{^& zwY3$@HsDaHSs8a3rC;~}99daQaG_W9HR_U(u(oDPD6(No1j4J6blVRM9O-eQh#u+Y zP>9B%MHUZ)6PA{?qn4lJkTM##L1>Y{It>mPrJvEHH%Ru*~A3O=?OjPCzT$`ILhl~D()2%3&xT@v`jo1Y3+YWd;!*&RtP=E0sZ2E zA2A0{IQoSTcEnGiIPjaEj>c>wn!Dfnohre%d!Ase5*6GN3u7!6D8a!!7qCm|xe)v0sWk4u4h2tQdv3pYbRjnO zM(mYG@xq<*f*|9CFvLQiH;QHWT0F)d7Kfs-GHQ+k1C z%pr2eB@GCY&4C6Ss}U&viWe%60|6ZT7vhga>AdS%O}!A0vQ-vDbM}9nJ5z`dw4hGT zTWUh5^O6Pkjly|p#Zi<9!8WApX0(D?2}J>&o-SIOVzAHglzg5v6eD;G))=r7!GbTh zFXD;NAJ7vMdc#okbg*dBn$NlhMi(!l`xO5Y?+6SYN)Zv(78Vv(hKAw1BEU8Lw-5Pu z{gD4`Cd8s|@A6XR{B5 z%oY`Tc;(L96$F6gB9wz1|qy~kJ^BF*ch z^0tn)P7hSLjqy6gbF8kk(35CGUxxrN_+zQwBpVR0&V$~{~c zn$g4DPCVfp4m>HLH*$xalIexYaoEu*%~6$%`9)`^V;YghunL0>VhxNlk{yvUnw`Cy zot);lJG_f-6-Rm(kzU!5iheZjCF;O*BU$whwDFz;z@CZy3WC2H7dhb^<*^#bHH1M0 zn-xNhy>~)A8v1_m1bR`S6Nzb>XzWp6kxK;JBMWw1@XWs`s^oZa<)R$DIa)ek8{~b1|bcrku zR&iiWLiBL7Bs8N-Sg~|TN)Cj8oh~tVaPm1*2|wRFI&!F1w>xyH9YmHn4>jnkx@)<8 zU~{v^ZJ=vF^29QE%t9(kl(DRacw+z&=m;`jo^^3sUK?-`Whw(729^md)XOlbAZ`=} zSqL^}M-lrq{-H*MlGcoWcHsb0XhjjbQL|w?E?HoOj+84_;y!;S*cgX&gSRbwA7>k9 zIhvH7o`8V!2e7E5$4TXF50bWHP~kR3)PdyDBxIh)Sgs~m}-v8sHNk(y|eAyz9&o5a-#n^e&sOPpbo+M1fm&tJZ@L2qn* zs?CJ4jWOd%DkP$<{q^-_^@<7OvJq6OoVB<8JaWLYQt*cH&v-cC?Ftw~(f>1)qUeo1 z_*ua|*pX@fW0^LdZ~zzxB%a3}FCN8T zzRYrt%a?DY2!kwC95?O(HBz2SAso-+`H4_+(9gZ+gO@PhV0Fj!fJ=jza6riMhk$!T z8n>8c=i1GAJ1RMrsW5E_@4+${`TkJ z{gG!DXSo~P1uMxpzBa3fW)y~m#}XT0CGyMr-silD9$bH!Vu+mbbFAHvQa7YA%DFQ! zsW)ow19{nU=7JHCr35z!m6F!?5HVWNsm+LO6IMnBCMKpLw~{Z7E%KZ_uZz-&kpB!e2qP;){IVBp6-EYM+tRI(1wohVkSJJqA#TD%GGZy)mS{2~A8fBwyH@6)%LaKjQ@ToXzS?2~LhOS~b=LuD*99r8T)Pwuij z-K30=B1a%cF_+Tg3ACg1M5jd>SP;uNT7vQ_gYs&FDvNos5~Rg~C4>m0B`74Qr6s2} zq%b6eAfs>&f>9QVhHyDO)S|G$a!-9NeX+R1`KI1kuXk>4;Ed6>LXSC*<}oRDqiu|{ zH^%UM4jr`lRJqujov6f=!du~@P15RavuT&e=x zID|CR>97-wQ6lL^QQ-!)XatWy4xLo=UbYjbn-g5DJLaZgu;Dm-=-BC)y(>kI4x=)b z-d3rG9X~ocKEo0vEjf)@0yu^?1@1XAD!N*dy!zsZAD@DN|JO*j6b|Bnv=sI;fa%*pr z3hBF08A~*wRyT1yES4xWn_|-fov7RFteaYmpiwu>ItHK|b>k_=nhT;h)R=KFV65?i zWo6|vx7N?rtU%B4g-GMGFvz+4FQk+5xPxtcyIkb|>DlZZ0X|1b95{3ptN-S%Y$Lf=YT z(gg?y5U3-%kxCVmq3=T=G)3R{5FkwY0Me5PLL^fZLE050D4`P<;0y{XN>F33XL*ck zt@+d1=k9yBah-o)&!1)iRc}=x)^G24pJ$BEKvE?t#CUEl$~el> zm{}W9mOycYSO#$#7PYYqGEaTO;grakRQNgGSXyqr;ef2UB@7%Wr#DZH1Z>oNrqX>l5=Q=B<_HSf;e1wGHNsm%1Ex)xC)&%FNA|LUJ%?^ro~< zft%y%RT3zeZ|DLudQ%C75v&x8m3gsX34V^p>*|*t?kn!@Br;Z>!?|(7;DB=jZc#NK zK5&Q3kjSDWE`~rl4-ID2`GHU9bvKbrX>Mlr#xi(c2iT%KfTu%jT1b1GcPlA5AMS>A z%J~wy7xWEC4FqKQNq9Z>D(B8Eb08%y`~cyUl9K&6_&E;F!NNiM122-cIJZAa0;4`| zlvD>D?vvPvJ291#xES|J9K~S6t9Z|A&nvN*cPT|)b;YLqmFQIm%SK(@IF!Z+&pJ$R z6`6|JMv*l4sH-cY=(2_!$Kb!0;Af$eaK(;L;kx#0BTdNk*2`bDn1>3J^^3pX5dRsAzwpfj3m*fS@ zkGr!9j=);XT4aJoijYdKMP_bhsX~DY6@_9JdgHDbbd);WP2q=GsY)d!w=}nAsfLZC zU0|Cpp+==^sb;CGrnIZA3AvQn6nSQsJOzqFf^a~3q;$#cfB?fU)Lc0aer#?os^98<~O;#3MmMiK}du{JhaSf#+rfgMZIL{e;QG`;dPHaaQE(8e%B zY)&!^9|{ABIPkhM)M&*x`rFKPSa8{C@S*@L~KgmMZOr@7oR073(h$9PHun zf9Kae?B_QO`j_7azx?5MetxU;T0SX`Lm0>&0wlL`W zY;di5fo&QKk6%M?NcAbzVw+NpT}mzdqP179T=6{SBrGuJk3r?|M0?xumFj919c!M? z8a&rXRFL8j0hIwTy{ttu8>Ss!==h5@Y^4nyF(fbsgo(p?d(B#EuJ3Jt64|!5|6<<& zzy<`yx_!ey<8};eq9i>Up*Pk=5O!T+ePJ9jCgvWCi!tBO7b}ao2e$b)3^49}T>o}2 z1`Td8vayZEd-N@vB*h^DD#;PDu&=WFBs%U*N1T$8&bT-E@zkm5ucSXnaZF1ljxfYk zl9Pq{2s<^6@yC50eT1Ddz8@B4d>?t0Fyl$%h_I0R4F3{A*eC{b7>5{day*vqqN1Wg z-h{wB8WIJz`5V|qquvDlIVvbJDl#O*awc`N8?~_Amh2X_WDiH`n}nHe^MpvIe{Gsa z!WbI#XY=kzQXE0T#9^MGq=(j4p*}$1P@0>snA5+~P0-ct24u9g8}g!Cmn6ju^oCTQ zqC|BY4>qJKmHDeTuj)Q$k)fQw>Y?;_4%Kn>x$^mZuyX!paInWqkLUC5FV*hzq&PfI zARHt;P#4RV4$>LdVGIpMAQo-0YRor$_yr%eTBwgugk*hq1H&2Ep-{EkkQyisw;?2B zcZS@)4?05KN|xQuyRmJ9O{2>tCC%X?B?}~2IB5zJ6D0>EG{Wmk>civCEdw#SQvwJF zY@+mq4ax!5Cuc{)dD?My6!cNNR!n*$uKw&e$5cpjcokvJ;gvX^=p`{YioFs=NTtYY zq;8}L>lQCS!A4BIOp6jtMOd~7o0eOqCo31y9Ca7>ivRv^|I7tzr=@f~}tRq^*w2;HNV0OWmvzT;(ynwhT@DM;lm}nUZqPDh)wh_3NMp{OCOpgFkLhJ&Z z=^9+$1cLzQ-#+B8`XT>!z~!8P%fB=DF^3E34c_LE&=7d@!d*-~G*V=xFlSf1UTAt+x3DNoyL&fHW096?k(<8Db@dtri4F@A8nalg zq$zS86tfOurjf>U^UN17W-;Y}oz#JpNUpa-rgu}O_Y3dL8t=?5@2*_d9^NlXy)#en z=9%8P<=)_p7I@3cYkCSuddRU!L7hrUO3G5FT}oFINsSacxm_Ex&grclyB;7p934B` zI(vFr9NT&vd)fqtqs6h0ep(!-vRYJ*Diphsvgl!j zsSjN$BrU3}R4TDS=~t1!=o2%JEn8KEEjsA4NNZH6vaHGki{prXwvx8`vq^hoCzV-k z*+$!z#oE$+^pVYkjcUT?2>7pw>7@Rs0_b;}-(+2T66jX5T1q=0I<( zyWK0agyF zfo)3fz?zT~1Jag(nfUCH_GVyqpWdVHg-=2;;2kGHX0?o`E;Xf zah;_`BdXgX8by2Tw5$;Z(8>E>r|EurG9v6gmMLMFafIP533VP85td9cBqI4#@(t#2 z7>AvjGzNNO@_tydF};@zrv_U%jORjdf8)`kO-wm%Y{JB`X*`Fnwvf%#N0ysGmUDBK zQ3Cg6`3Oc)32#1QNiV|Sv56>4-XS##BD-&k0OiAEK?2LIO z1{%n$C@Yn%l}dm|TPp>JLrD@N0RxN~@Heg^nld8=QzWD4^XDsYaLmI#y0U^-UwY_r zmx|JZ1jkK*`|=?*n*8mrKrrIEo3!K|KkLQGqw#IaeQkF7B*|tc2H-yg17&j{S3_EJnxo zVcm!uEsh&M8^=73I2cNc&(;@<{Yi0ReKET?ii(-TK|dp=iS$)WqQjKlE;2P85f%>9 zxvS$!F zNexc5o)IeJjKn<097CYaQO)AQg>SHfey5HyVvGT%r(MMFpA1g0){(BIwVln(&?74H z4mv!W$0f7A9@DVt3R_?6w!S!J>A~N~SI=}&Zl+~&!APnDf$z{+hpwK{yUw$DXX#2E z#swXHI<-0Tej$HxeD9l4NwlF>bKEw;<;s1`b#{0w7Lr$&ufyME`pZVsi!+w4X3#sasuS@d;aemr1b3xf+7b-adgV*wDbg|)!^$AixTpSeC}u=(Q!!T4HuRx2u1 zgk2P26;dIdt{7`DV6++}bL>-|q0O!Ou@D|>^wqI8z+Ea#4z)f;Mg5v*!!ffszQmmD zPpo6Mdpk&D^hzrgYwM0RkkA{gx3_z@F&6nkssjX$4ph`P#H=^kf01G+JoA7Q`7%cO zLYPSHaj=LAH`qtV>}B@rvU}?wnWIwWBLXY*&zNaGwlOAKl;SJXvahm9*~g7!(i$80 zKgwib8+33=Cgc6dsj!V?(ik77Vd@Zhl>4VRu@V-ROwW_)X+&}ur)9$?jVD8r?~~35 zGsb!aZ7R5hJ-Ts!GelIx8pF&H1tkLeC|(FMo(m$05fv0kTEjAm)JI52DqBWVXHZfv z1{;=tw(Pe2b81j(>SpT9pA!;NQ_VM1IRG2fZEl{L5QI7vG}I@s?5HH!0nwpUwki|M zTU%F1b+jhvnpf&3bnDLRw$5+_wvs{T(mLh&eD{h#j7s^~6`@4t=>>#fl}Zm#z({L& zT=iH9W}-R3UoSnbz7)8xVE5-FDxL?cgWXr$Uq0to9tZ@fddMBt(Pq*Z!9GHB`1mwG z0N?z9PjD!=tEfqZsDne{@CY4p8*)?cpvcXwnL+2$I_1H#s8oUcco6P(zJ!DZBC^Y3 zr?Lz?M+y8K2j@%9?S#`)m^YT=cFvXT)Jw%G2N-k2)rX(mXD=MWxr^Vt#$QhTDKl_8gpu2qci2ysw#VvFu(l!x>MR*7GjI=RjH*r2G9yMcNr4f(Z z%|z=LyoVq@=+Q;9uLw2J=?vHDI!1syi26}9d}x{YYWX6*X~K>HeGs*cq&9XK7WIrS zFiGchNJ)wN%Vd9}ld&QFu4g8}92A>VmKO213}RRHxCmHm_-$A9DR{yk%rzjg`(6VHytHziqDv7D9}ppz_KILAuT1h6sg$US+Qa9 z#$x4#cW$XSb44-UVB~1clDBtPN=+9%%8^d-mUm^MkG>6S6<9S=VAWW%Yg@8gYU=7j zRa-gIDm^fe+MR&S+uF)g934CDNMK~yIhNC7K@WpB>{?n>jy+R-j-4%zOO7g7Nn3z5 zhw8|3nwqL=K`OQin#0Pe1#wuXe(r7qSCo5GaHQ&|7pf}CD*A<^!^*a*kJN^h>WE~< z7AcKBSV#M9%Z^B5l$F^|ptjyNBZFR4+1Mu8+Sq2>_S=+|k*cuCj%LG#O$I$4P1sDt zPLRTg&9*@-mZZoh3_GH^7nYYMNrq3KK8%hLu8&D%@J40LwHB69@V}l(8x|5E zBr&dB;WGL)$-Y4fGj%`vLs8SgiYp~yriTP%~4RH~}U9oH|9UDVKy}kXt zMrp$WP4Z2)xX5MnfQ-txxNV*E#61!r)89l?b^}CTt>ec!blE!0_sN8Tu>y_H(=$koSPxFks7&4Pi8Dr z={U&T{Ed0!8}m1*ktmK03PM^Xl51fr*+=?>^hRqpdQ^~Q0qWemd*!Mws^=4wN~LaV zB^qIMpYX_o862!Tx^=H!B~dX?vSUTa5D(pX58damZ!lMsdsA?vd=Az*X^ZE=(c$iK z{Us(F>J^@J0072&mFv$*fn2{%PlDAx?rJPh)Btb@uzB-!wJ?tkVacKnbsKs>`U1Ue zSf!|!QQ{VQfKV((9g^-CI={SJ5{^`C^Ueu=WBGhINsJPXsQ@y1UN|`pU=s~5!6U33 zapy*N;2EvoKX>lzK2y%?&q9Fglf)Pug?$tlFuoHgutcu|5*S6Cd4*Y27)Qs8Now#` zCDF8~c*L|QF>%l9mKUjvMA8(zQ5i9$icGaG`0DBF8Hw|oGyg~WW+s^9>(iNz;~Km` zKwY{Kqp8rCuBD@=r=#akNLz^fl2FLLs(!7I88 z%z0j3-dpa7H88lm`zq&Ep1~^=yS>OOd|QaT%I@d%LN=IM6T4lI)nx1hwO?gTrTsyQrn-VikwX+)}<@ssYv< zHqzWKhprd1-kG_X-Y?iHT1rBLBuGJ5irhOTrE7^hV&(Fbg4uF8#%L}^7mrx07 zSFqGo-qnK^w-h>t3@J}(D}zFqVEIMS|_gL%2ti9&(r4!%wjfqaVUX8vpPi36S@O z?GM`}7DxQ>>Zf=^f0WXb(1?HU&y8;W?H@j@(jx-0s~_@L->pJiyndIzn$JGbVZRUW zAVUV*^It=24CZ5l!p4oj!NA9X@8SpH=-@t;h1&du{CD|}t6iVfzJC0e9$k;2EMO8{ zuB=Hcjw`j*=%!yAAnDQ2&_R--0qrOaYtIIb8$6Gp zD!w%I9y4fT?YN=0fitiTFqFa}3fdQGjouFH7{LUC{D`qX9vC34L8@Ya?#4P|#*Q0e zNNbFJA+@oMXv#Kf=`qzH)iJit_utlmHHVFKaZGmaW208?@fPAmPMSB-T!)T`d);LJ77P4h1^J<#4sZwIhjK% zY}{ZY>DOc~u_xtWd@Ah5jj$VECrNfZy77qAMzXOngaFm8;q=Nc&FWCK52*@}zLu9mn|~Q1 z*|D?yfVU}bzlb6?w`RCGcACT8&Y$0DJ_s)f#R8>-Zs$w(_s=hv?3_QSKY#vQTuBLg zMfXca&z~C|Eg@Yoisz&I`}O;y=k`fz&}02UJ;cTS!D#&`m&cCMF~?Rw`zkJus~`1> z^TPMI2&Vi}T%X7^jv_2tNO$ZZEK9#kZ_-P9UP4eXhhwC!i0}7q)tTb34nvN*y*+wq z#B{I7bmXu9?Bc>+Q3jfyL2yJgx}CX%>Jtvgx?DPQ`i=;n+&LqvQeg79Bj}@Im?4cl z#43i@G!i7tMba;b!2WRQj0mp$ApWh0!)W=MFcji8PFiN-qFEcKBsw)Yb810f6mXaz znrU`HOHW$Dpy$g(8_gd$P0?vfTL(!BZBAcE;7XvR^rUbGuHAAL^wzl2{6K8!~)ME)ie@S0Q3oT~9eaK($L;lNu4af-y_%SEon!#nvF#-&v z#+0`?h3HTzq~n}x2G=-~g5Frc;xG_xD(MCm1{zVGlCCIBPqWZyEYk8Ub_*?b73hbx zNVAwtS6I9t&5^Frq|c_kASIG(G3$WJSX9%Oz96-ci*8tl*%Su_$qbA+97-|z!1SXO z4$)HY8a9gNdN+Az!nq;$mb&Sex|&kxjV5n-c~_0R2G&t|lf0?NPTnPapNlsLAhg7o2q3htF53MDHX_!mL5kHiH!m$$NnBJ zqR*Oga&(&NVXtUQKNqZ6WwltfV9dc;6_v`$$*Ql*IEa+eHtNw?E?fFQk95Rbz7%7!}1Y&ud!R0BSaK!r^9{R-(`@J6YdmZ>14J+}1 z?fDCVuYozQuFij*|9UXrbH|xbE4)*wW(7iuq?#W+ zIiAAJuxo2lF+J~Do((*1@U%uuWniG!y4Mq#6!gZ5k%hHrQ|aI&EGJkxtOq&ug;zHB3^e+60M%aSdln{aLjYbd?;C^Dp5gkvKk>334GTS>J{jkGie zivu;W2*fgI6mWC%$b{C)NOR_VnXj~(&m=rC=c4+V$^<-5s8lK|XDXkxz9eA*nW1bI z9V( zU*OIq76+=`VA~L!^Row#9QDQZz>XG=pB+y;%OO}0M#o1Jz4j7e=SVCbFWxIECSfs- z6-p6&qD6bArhB9`NL!2)qgJJey`r~xopK9LZ(*Nu%anzO=|y-u>P#>G{g2WLg+b#S zkYyXkC4oDab|U{r&%6XTM-3a#I!H8J!s6wU#0@)hnnCAimA&M0$3-e{5d<-wrrZ(v z7`BempU}FXV|3dH5eG9p6SfxU=;)a_U!bdIHUuE`pr@^)t#6{GXXLD-57YzBZqDpV zG{N~yA23Y1Hm7TGdN8AtoRPMPwuwGH#Rd5VTy*PPFfx)##4c!YYY1J0>+9fTdO-)R z9wr#}8;Pb5x{^0xbQOM2M;pDTzkSGG?L+>19x7CR49LrQbqzKS>{I9vT#h_YMsqaR z@(eWStuMr)CC?x&tuURxBqWD{MxohFOEXX?6lthZ*<~0IZ2Ke@?3e1 zLoT3SvrVKsn!0l3v+{CzO^RHe(pAt^4jxB}ysfRNtEsD;o^nW~oCJqFrLD(~i5#6I zHn36Y>Fg-TL1tC)5gnf$Ys6wiYJt8#%-BDF`RL1QoeI!4!>8paC zBBZjFO@bqOYXZmEt!&wD+1N(gPGnDHfL?C16^%gbL{hX(_Cyj~qe)2_Nl$IKK^FMY zSQOPqKTOKNR3mBlVFtio(a}$n-Y3OA6*1X%tXyJAempcBW?#oD>>d0=!XrN3|GhuG zMk-`@_`~q3zkhx^O6m{u4|%YGkV^Y1BC_rA@em&IeuM3#Gy)d_G0#{S27F^-Fh4Lb zzkMNaFg_olm9_lA{CwC*NpoPjvEcf6?eUdoR|cyw)VRX1^T&^C*Pg9isTDC<>`!W| z0p9Sey#lg1sSiNEp0ViY81Ovy9PsRIu=Z@IeTGQ}SY9MQI(j<>I)vI->m|Vv(=ad~ zyrjJ>Mq=PE?TvvfX^h$Kz`rAbG0*_l=-62AScm;shtvs+Y)WHHN8=)tM&lR|95M9z zSYsnPRMyww;UHNuhF@KjjV*rMSd3ZUSo{c}c??&mEXqF07G>*5q}*$~H%$*{VpO-P7qoR_HjVEtJF!(DZWbVeJ`%!boL9lYn!N-C6*eJ`W$VVYjsX?hxk4RxW;+ELR zNXtj0G=jRp{MuwTM>k69gTxn;IA((AE@1{3FfMchY$G8dA+oi#H370@hGdAjdF4!k zxw7>o`cx__TPrJdE1CI~AcC;nx;IyD-fY#?eOY-`$-WKUt1q9ULxp3qFCj;iK!SN( zy@`m5(t{p6Zm#fpz4U9HW4|_Il zyq?D#^>B=iqqwa&u0AetT(Cxq;pl+k7y)e*(~V+=fbr-929A-$x{)G`GDeE)ig>$1 zFYM7vrbVPS>VAeCxn+8*2=k7;i$xcI!oKBi|N58HC$0>E6$QkL!K2LlIxW7$utID? zT%_s6X(`Ua@1GH>M6}8R_raYh!ei?4!#{$s>=c2r0kib93tblT6dzZ&Mp}Y(X(V_I zQmH8bmf9y5- zYd^lt+ckK5*=*<{F%%BtkTKm${!_Q<$EP@kW}#TgVNOWWkIhO`||*tO0uH z6`FMHQ)VsbK(wlW#&I_n2No8ZyNYyrKbL}D@3fN`|c z;lw}cP@0?TT~k^kDpi0QUFs_EUP@sIlOb;-NzNNOyJ zqF8Ywfzf2grLlH)Bt4p@+UzjmK%t5qu%a#Hj-)q4D7K~3ZVD@vo&v`z#~xLcBXc?W zr<|NRxi=O-b8J!AFbcbuC)s4%Y}o*{!B|l&O)@565XE348WplhPv1vBwRuRQ z;%PMfd}>D^7GJUK(|8!qo(_hK#?zDN_}K4_D)FBlelVne&>rjW&s+{s85@uKdjDbn z4~K^&LWa>8`(6~qiW^E*&=$)bD)9^Obu1tqyMVolAKj7Cs0P-2fgYATkR+H2~=ESpM zjr-|)VGV77vN)CiUs9Lb0NP@=dA9=aXovMS3lH9_&|R;+eeZz%7}vsX$Bd1k8J1)Q zTRO(*aqQa|>>A>WQ?ZM1Z!m%b8x?yJCNhyuk;N>2TWpkl8$*4pY-4d_V;YsQi?YRy zdyU*rzcGF99$X#~q(m6-C2rHFA~xvR26w0cJc@k^45PeP5j2kErmXWD*Gu%&~8Wb6c8Aqh~6YNj8xlNR+Bp{^19V&EKSxN6gcvOm36{c~tazC4{ zvNhr5{7c=JR}nx#VG4|+l}{MEq2wQgVQ=1KPY3Lxy2_h3=}sw1RdhX+H=ob1cs!r? zfZzyL2LlK8(nIa8_LzSO{Km^*ST^A1aDUG2ZGy+)gHjbApX&_%x-J0c!6ZCFLz{(% z1Iv_PwOg}L7@aLa)8as^11zR z=#B6=RJoDlsE-@n2YzEe4kBb!N~xR^&22p6sE<3#A(gXei$#xH9895c#qe+l8b?u) z7e6D3QCwVXT1*@Q0D6u-~_Uis%y7zn2 zzy0+u+-)ND$-9UcEbOELLhd30v}e8-xv^Z3vLkz9e}U1 z_}e%);=lUsL;gx1(&TpmIlsG{cP%gH)$Wf!f+TwFn&w1F@Y=*6of+n!foWhDrMoon zMbqBWkwJQzL1B7fA&jG+Ez%1W8jYe5wv5>{k{B3nWEN^ZllXX{$c5}ERB&4M^9#&3 z!2fc%3r&%mp6(!!U!NJqQTiei36+$Z(v$*vl9K6AlaiTIlTuSqkejhU5JtRT8%I!Lv>^dPsvU-r6rPotB zTb4Q*+!Na#A4-TQxNW&uCAT(~^ntOn(2?fh+-9B zMmu=w!JR3KV=><#Gh)`ij5Tsjb{&u3#>QlHFcvem-q^Uf_^pvd2LdW$v$DZl(R=qe z6H5nhkHSJ4F^wv>j~ijKQ}-fHP2YoI1I_di$)~2LMPDqKV98<0H%?6^qd0c*Bl|mG zA$??g%J}Oefi#E8z^EgN6i67WjIU9ml>P>mE^{GKbDPFdkLGR|N6p=s3z-Yr+>DBP z16Qa3fu%}_Q96#~Osu8l94ccixi>b!G9mI!YAQ*NNb{g>*hM4F-VuwxYhGVp ze6}7y4sbX`U+j1>P#lS)AdT)NqQVU?KzU&7vB#(mZgeZEL&ZvQomV211sodC6nn*c zq&(mr9WgE1qq|#mBR}67DH{1hUELlDl8YBH`YXOV1Ez~! zS6WcmhGtkRs}?6Kk|BLrm}jW^Ree~YIQ22s+^LM@MH!GASt{GAvLmbPtv;)4bf>_( zLGMJDZB5u_*dFnE#g?m8GU(B!EXg)I)+XC#qJIJ^BMEbkPyE2i*yyLR6JUNlw0UZ1 z6U|9jiMVlci^eP9jV5t~1@n&8PwnmPcq9IOJgE;vV*N!iJ!y_%AU8g&`eW&G*got} zvVvKohVl7+3-Pbx{Z{=x@Os71@AZP;!XP{y`T2o|3xR`v`S0TWNM&HF68H`e&mOzx zyDkKhdT@P>diq*ar3l!@;7Pg43F(}t9c!+S89n;gv-)^#Z4IrlSB|gLRyUkT4sM0* zs0DIkjea_w#dvZ*N-wiUV>+-bp*IGszlin}2$O9N#^Tq29ql;opaXz5z!mL439S6*@xRz+=jp`2Xuo69T;uwxGl5h5Opd;@QecaC5lr*&!6An`nDakr=Tg8mB$Wy zIS%TVVcm!uWu$qW5FGXAcJ}Mfjh2wc*dfWmWwH33;VlSHhv^O$>*G%j7 zihjQJ`-`Tx=-q$&%ZZQE1=tK8N{Hcw1SI8H$|VmaBrK-9fp{m5hW^u zSn48HE^@U?XK)F!dGZ);sJ^VKoZ zHq+DA^2KRV+xNSj(U<`SM#$&b7;V*9_ij zel!r(^dN4$1&V{z2D<6rzSS6L3?vQ*CL(DDib6#?sf|L7VmD1;ad%gdUYM57(9stH zYi^N#H(l|WbjD|kOa)@J^s)mTmS(cNKp(xs>?{&1pXsH{QgpIykud2eTzoHC$a}kZT>bLE;-O8}dCdFaf-*07`Z7bNL z8OW%pn0cO^4V`g>X~vOlQpQA?txdE|_7Ss3ZET)qBt=7Y^mAuyhM=7%MaN>*LJ|bR z;{AJ+seFpX!h~U?lKO}@jAo*_ggO6U*xvrmFn+atc-5aC{rwN$iD8HTJNjy0g>ls1 z@7=pYKYaOdmkRqwVIe&nguHl-X$HU>q9u0WwZNL=1y&vO^Vl`;${@>&wa3-9tU0Qy z2buKcdhB|1#W``n}AUH@~^wOI#)@yk3g`Rb=Fxmcs2YYM#j$U-JHP|0>XYBgA0ExzIkFn<9 z=Z%d>tbB`Uv~OG&ZS{g{j>fiiE{27|*l3hBj(yu$j2XKZBm2hnu;0Y6BkU_s9I%g0 z%Vb}_Zpfx(8>eL4r6TIuNM2yT5hlhPAK~L*XGhq_8)EZvBbihO{q9#%AI8SX$t*8! zgoNE-c@c(e?4w60Q+X4@O)AEZB=Ld1`jC*Qlc`2X>Zax9%%4*ugJ$M}EZ?MZNH$f7 zk2mzZ8x%15dGjVUH8mk2)toc1=H1|L1er%B@Qh<7lII*SYhcTwd?M5cYU&f7kdAo5 z!lJd6#m1j>mHar*wQVaaNUS_jzSRAbl3}AK^ikblB`l*i#ad;><4^Pd;c+wgPxHYZ z|KV}f!=0~2+?fm|^ zo#lh@lJMn{@N)+c92|}%@p1Ox+yPHCASn(G{=qTlC@gB~&!V}FzInVcPS1)**{3ls zu;yOHMa5pfc#R0<;Z;<`FOMYNA_Zbf5(L5mm2P{cd$&eRM~WCWDrIAfOmE#{D2EUu ze=r^S{ocj8qLH6}`r99QE^+6(F;omOI92kG;=cF3;hwmm2?uo-Fh1+6L`K4u|gVWn>Kp-%CH`8O%gBi?=&d$zoAnM}+T9kjF zyHtmHQf9Zg(_Gt#jf}TVbhLb>^PMkFk*G5@6Bp$AxCYlY!JnmTZ8NMF*hGMnroI3} zFi8S#N%{vy=w5ebZ{!8`Ed2H%e~l0Me+2}*diCn^wSa(}AK(5+q9ee7q2;eM0Xdfq zUR~bBaKoT5hbvZg(=-NerK**;SgNFRl`L{97KNB*XwnJ`(}4TROV7h_gLAQ%R^HhI z+*bRBxKdZkFlZ~o=X^ql1MK3y8skW^8`?9K#V5y+rCS8$LW`!^c=TrJ2Hp*1l zwp+r-k&%tg`iYEem2KICZ4%}iHWQqS1=ZXp8^Qul==dqr$kV4bXp5!yl8}3S8Vz%3 zbW(<4Y?9&ABn&*_+lOPp-k=A_koRb>kAL_{5*+QrhW_x4w)?+t|1j*oYUsZj9}oD~ zhj>F$9Da1@QH&!VNkLe)4 zHjp74?XO+Y61z6I;ELS|?$)XiTB%**n93R{5UEUsWXQAH$0$)bmIOybttXeo0&2ci zYhCSW-8)dtjco&-2&3@);uvF$jsaB3vZm@3n92*k;i|Z^VqP+pjkQF$zP+7%33UJlIUy_z?h(#e3fb;0wf2 z*~gd-q+}->G2D<%FD`Op+u{Z(4!%u(C9xrkNJbWgM;pnoZ-mhc%ov@Fm?ZU)%(+-@ zR|(_lHi0#VT@(-ww8a`H8&5`svFI>1jtIFyvf{q++^4_&7#0zhR&!v@&1b+DZPjJFC~TwM&p8m=jod4j&|gJcn{oxoSQt9yuPV{1 zLJuAu&sUz$Bc*cniPTR&KTj_Pd#C|5Uzzt%!_1*pd$(V+U*nP<7y1Tm% zEFARJtPWOVuQG)B1{Nrsiw)*)H)e8h1wCnv=4H1X0sh*7S+qHn@0LT|LU-Jjhv4D} zU0#Mu^!zf&8wdLbc&INqkOaqa$@z2p;c+E9=k`bUG2A#8USALR*M3RdIRWN4H_CFO zq(1Isv{BFUqZnZG(c-h?qoZfzyy}HxgYUgWJeCTToRRDp|M(X1*wbR!;=;LDu~iX0aeN0gLpp`8opp=^ zuTopv8TIH)dtjs`z3HnDIwm)DAl7MSg43Rnww|C=(HT;kenE`q&FE^``GT*$mao1q z0Nf^gA#SFv1?F$e1ZO7gd3x`sY91gR&DY;AmbEtt7StR;S}qOJ{!%qz4`H(4)n%ioUX1oE)7HPI0R0cWNQY;gnT11uqBUDe!jm zt6H3(JTqr$4M3{DLdZ(1<#$X3Bpy2TTYzW(g3qpg0GZNKUWD05E5X7^=hA7vj+ z^pno8%}x^SZPB1@Oh9EEWn-aYb2O20l$2r9ADi)M0@5Qpx}S$0vC;5xB*mgD7U+$K z?+s&PNl1KnILrxHL&IoDkx%ilhQsj>#e^dk$ywCLu9D~wE{^!v_*LQH@E?9>*#4pY zeZ2p>)%W>_?f#f*46o*|F1%m(5dV&JhToyk8G#FEsE=or!L|*07<}D682D~skX|6^ zQ62b>WQVKk0{o(`YYVQA#opw~GuMR$&dDMjE7mR5pqN*C)(WIK0s@PK>lan9aPd@cEEcsEyDdYRbVIg06x)Bxv)F{AT4C4Uo zD|(}P$F+qXEtZv1SPx) zdJ~yC!>QQrpzfg5Ckam?pLAD}y66_|Z3%FTRzhX0w3@e~M5VQLrMuPqiL&)crLH-k z=1=BzC7k(H-EQ`6Ae?gbxvr8VhwjQ%q+@meBz{NtiBjn?FOcR+4<%_3G}Yh4LdAoF zDr%rbU!JIpmn$!ytKGrl2p&Qz7IiAY?ho7_&@0V8aBo0ops}sl2R2fmN7Z0)sMQaK zf_;7oRX<>qd8p4XLn5UD)F|Ucg_q-jTPR5mQX$JFZXAydTIL@em z_~kWTUo=jKza$pz)p;>ubdO|*sTUF~#a<&MN)VD|*cZ^}q(X}8OvQrbBE7yx%HwB_ zv6%kE8ik1+KmB|WW{!&&Z~X+xappv2oVf(4fvOc&8W?FH^GaIe4x2Uvwfu*ZHaN7h z@#7sAmpdGe6@(9DZY4~Ii_jU8ztrUpNta7F<>BPxtVdVPI)FjxX&E^?>oIQ|j3{Su zHLmT;mP1iKCeDh!s58Y03;;`Q=1T~!gG9=v6DaqCh#E2@Egjesai%jf`HslsGcNoI zX7s=7(`7r?eCTn785VLy9PNH1{MZ))6y(7yXmlkroG5ZW7udR*c=vjHR)-fnQPJ% zchm0Py=#%Cut=Xxvw+@kupkj~SCPrRZE3UMaFpgM!2U{c$eo?d1$xxmVacJiK+H92 zmP*;pA=RhgwzPzHdhaIh8hJ_+iI286;Tx^7leZzF(pFv$g~5)Eazs-K+Bla|kkTe# zqv(QlEbnaV>2b81>g=Ik>g?=cT1 zxJgM+^joR2s-#+2RnF?(kK;YQQu3&qpt!)(-(yNX*m!d)lR>cF336*X1 zQP~lau}4W0wq-W9HrY1)Ae?7EotUtRPRgE$&Ul)UA*$+=;NQS)E5jxuI>`{dZKO5c zKQ%-q_8}%558uCk8VzUH)8z_{Yh{bGK|CD&p+Rv zREB^2d%BNbU4=iCsb8z{@qP=h^UUl_~>`m1(< zBPsdp(s+G^yH#ATRJ#s7V-5%Dj4POMT)|9(f9Ke>8k`P@jbq-baE;qzp*D_>JvpUf z-Rs%l=_yLvtQo~&JpjblK<|KvuyCQy3-{98dYi8iT_s zF+yK_`$&2NgN$j}7?voD5i$mnPQxz>0>^Z+Z2Dxt5s`cusKL> z+(2Yyl1o*RlatX!f8&0#v1o^VM9)Jo&Ip6ncr<5>Ol;Jo@f%%q&b2tQ(@pp4Ph$>dq{&U%_E;!2Jw7@WCypy0&AYI8TkZj6G#uN zP)J|kURl|C6%3Bbgx1Q|)>hrC3A&XtS0%H?N~>-w_r@v#7G1f@#L=rS5nowZxhmEv zPjrZl!fuXWsXm1ZRi5(;_<9)}EK1wl8O7nQe(sK3irPJRNO(5@8Fhbf9euH+ zEY#m&qX3LHH-ly#+8nBGR;#%%WqGIBO|-*42z5hAy&GPCz#I-{f05EC+1Y94RLc2- z^Wi&Uu0g`%q&W6`_%b*g=MD}~7ke)5T=>}?q*ZvzA)Focpp6178aIAc#9`6swqFe3 zdGYvo@!8_Dh{o=b;;2U?#Vc_XttrJtMKEu86_3|RW{vS8(;}}C&ZN|hGl!!t5fcu^ zeAS6m${vOtVzILKQ=MRs(w+DNdw(F!am)19#k#-#v!IjzaMC*uUBE7k(--FxLtIWyeR@bdfSN7*0-~uyTlASC z2SIO{>Fel8@k}!<{o8u`3tCY^NSi{k6FX?1K1HCmrl`_S5IeG44Fo63wUD zdQ6MJ8I?}KzDDAMm}yB>rri4hi-L*HZy)m4_>ljL#75rRoPf(e2ISCv&b2%#o^shB zXE#q{a82_TV~!kyx4Wb=G99g_yadbql3h5CN8&zP5I>N=_)Y9jOf_g`%7PQ5-I8}ha z;pEiUU*VL6XslJA%Bf07Ma=kZ#vwWntTJ2iTrVQ>ML2CChJRTRNl;5a&X zsKKv%vF_OJ5INaiG`V5OVQ+1351_f|sb@K{Xm7tx5(JQQMwmCmprAem9V#&(p4+oy zbnM%@edD4G9FFyvdooek)+k$SytlCtv++#?RX#RO%fdG9$;RmIX#`e2jwQp^u`$h% zQRaJ1Gkk-NKk_RdPm#u$3=?lAlK}b16OU6jCV9VNOiCn-S1QKI*r|kx)Jn*W5CJ&{ ziUSN%QCL6s2m~<8s3_yPAecqDvTYMWBsDc875q7=?iV&lAE*4V?OgDX~wL{B}|D>yEyOXNpWuR7DjkwmOf5=m4f zn(p0#wt%r?WRJ%hw~D|4t7FzVn@s;uckAMZ`WR{+0#$p7pQ`7gf<2*^Q+%C+6R9E>^g z;Nbvp1Kv@ljpi8egu{UQ+qk-Z7teRo4bn7e8cafVcMS>^d1-kXi@OGzGz(37dfsjt z#v80IH0cVBf;;LJ5EcliSSaY%6thBXSmauiGTEFI$E<@Qw=|8#hSaO#kSkhMN*zdH z$O}?<&cR|MB@=&lQ1zmP+aumec*`*ZNb;?uPQkv|_ z(M;de)|SGSQC1zKIk01a!mx8J?{VbnHb>QzV+;Hno$TRIwUFv?B*jtSiZq4FY6?ElDy&o_v60174xV*j)8fS06)Ri#H&mRD&9*%%vz@Ri%icmP1;dW5 zvZQF+3@eO0CMM9BVheF$3(XN78_h~18ma>U6`Sbn37gm>AvVxj4`T=EhUoXCEn=Ta zVna+Z9>&MUvZjE-_`q8hAvT8Ff&7X+#E=8l4MWbs;u#oVh9CS55B)#H`^Ed`AGU-0 z^#O~O{NV+^RW#EtmX|REJ;wVE)3w!r-7EBh70AIjyo#o$tCJ{2TbGu9iY5 z&s-nZvV9cVLku^rT!EpKvnrmR&$x+Rh>hAc!5QU!$`w)DCdL>bZ+Je#^R*7^0nZLk zj=!#9vC`2HLl4_b;Ar^5{!vDeZg<3N$MkNK(6F{Aal&bpnC%Yx?JwLLYi~aQEXO)8 zR~qeO#@1mU6<-3t!L2GFbI4*E$JoEYVgtYpnJk899k6a}$fh@B{Pss#glwY`6AxHB zHtub3{I&7abl4=MMRM3iG6)=#Y#C+M5iuPR78b@G^yf$H|B)cFqWF?qe6n_z!{yhG!EIE<2eV5jk(QK5nQnpLyc6+5QJeZ;opcvaa(GT zr9SIyzNl;e8slj4{ zew0e7D|SUhSH5?-F^og0ocBOh#lu5)W&Y;(EbKhbH$;1zS}kTAq&Ym?nY`h#;(^jO z)Tdyp;_g25fSwOsA7bd2+9&jS^Uwnhz~VL(+#DJjDwGCPM<`ddp-n{sn!Bk*u?k*& z0O_&JQp3$n3^z(dt4cVdM|YM5lmmC8I}GHA!;k|*4NN#-2(7VuFoFU&O(<5*cFBLB*16|7Mb$0h$KV55meUUZPS zXPQ_?>LO80F!)wibn6zVqm1JaymLY3_!*84(_8$TKm7z3DQk|u|51=P*olEolpkC~ z{FSR!L}>~}9xg6tE`9G~J0o^1r%!6_r9xOqwTLRW(?-c2D`s;+LC>7c1zi;K!{IZ;1!`2%G33aFUsOa^(q>B) zQb;8?ceWJNuUzjJ-lZsNLk^Y|M*)gsxfHgmK%SB!?~<3(0b;RBHDHd)!P&5rf-0Ro zZFZempO^x1yUZh3k%#)&uU|>y1MpRwU8aq6wg>(T=6{iJW(CIT&aF`EEGtuXUCfL zTCH`5=Kvib7JJ+)G{^vSg%ng7=&=`c&dia-2#VaaOzoaYeG?P}4U!O)5X5aM(oQ94Ca98B2eK;WtTNyh zMS)5se*rnFoM|=JeWKgl-KzUC;U-67pPYC(DwV=GDt0YO<<*t%%H!%yI`+`@xa#pA z9w({;kHN~7=R$!5qq*(nJd?{;+=J=KbKbB3>p&F0;5*n|3aSLFulocKsjmkIlj?wr zLn?9$4h{|W8S-hSuLq&(=FkUfH=hHyp`qr`p-?r~!XEIH156G!jxw6V&21+%ym==) zTxxGCDcNEFMhSXiS#<~qM|k-DfuM1)e{^S*p469+x)|L*dk`Le_G}5yHO@*wmALxR zvvK1vbi~EsRUp5Zyg_=yYg7=h^A3 z{4PB9B(Wj-V=vYT;c@HMAO5@P&urtkST}O<_dj3!+h6}E#bWQAmU>UV=TtxuWgf@r z?*c*;xp7yl7>!&yO}`+Fr)SQ%NIWsghI*Qv9Qdn$`Qu;zj&lx9XgHr~<8oJ@E}Zo+ zqu25^;*%U*`|2>zL0gNl5lp4T+#f7#E&U60pr^+)Oe1Hl+h+JT*aDbbpsR2*x~A6G zzF&XwA?^)9e=l26mOMWyP6+rNFt|NIa6 zzkVd8aV_A-oNL!G+@Sl*Cu$?-8rwy2blE^tn4ZUKByHDVmy6miLvQRBrhx>OQ&^an zw`-BUTWEpa*j=eBw(zbZU6CdhD!J(|0Q=e{)uEV8o4tD%#8K>1W-ZX2;*dGZf`c*U ziW(S5@1~?>rli!QWIBA#E%in>eQu51!8;9hjZAn&G1|zjfqkP%_&J(tx=3Tl%jIna z@`4n^RWi$A)o5z7OKFmqmy-lZvFj@6w3DZFNuiZayK+>!K~FfAJNC2{V4zXZ=GfDc zHI>B*q{ZT3INyI=l? zLBECed?~bY=r>4;VuAN9Sg5=P?CW)Y;NZgR{QSY{z%^HHq<;pbQR}*fp86}Uj4^-w z7!!^wwYWX5t#v)-8aG#A7!|_}ctxvE0Kz@xuZ;VYx zoSNjM%KgdvBq63xg>fCd=!%_A4vPqjkQOR8K4KQ~2tLyL5tA@+81p{mzA=L~CO27d z7$+w~RA9Ohbz|mXG%PJ6Ltx*)kRxb@4kG6; z-LNFNvDqDjzStmh%gCA3Nb~O0RMH+Z-81Ih^jbnU>gYvRY-?*mWkTi2Lgfi&AuE-n zDP}6WTU$v~u$Gt+dz9`OVHs6^7Z^9^ug=fUE9d8RpWIY>=<0ghM3eLMvj4qV{PHZRWv- zkGlE610Nr^2hDEk(4FQX)W$N<97v8(5*Rx>Zqn!jKjChn=fjtqne(-L{-A`LR50q` z)>xQFmxYa^B<}pVxNsEIhcDAX$>_NQ;70e)?T=#CFVE6Ivt=6sD8lcpFi9w{m!Q2}vbIu317jB*q&=m%b=&>Luyts5yW zPUNOGRvJY>oa1+HkreqE)=_#!FJ1fzumAM>i??q5^z+3(T>RmUCj(ac>g zoo2TuTG|jfxpZ2ploj?32_P!mqi61jH4OdoC6_a-Ku(`PJMb?gB;L`tpZ@8e z{>wixe$s@lmG#YpNdRF>oDEUw#at<5Fpcy~v`kFQK#R~q@t2;CRI!eWW@j@x`2i4N zBw!P8DQfFDYqObAN9O`Ar+w*PX-jzuWHX%+++b+8FM~6+wJy+Q{%!QBOVA2uCRpjA zR7^|%w-5QB{vrQ2qz0&80oQ)_Ysk$ZcmKzSeq zTaYO)Ah{vWglSY>BbTSh<>gJljF#Ii$(srQ`dX3~bag^vG(l~U{NSO7-I84oeLF6Z zjL0fja?I*+Y-y1yR0@~{Hs#pqRN$!Uta1cZ)Cr~x+47k+howU4Oh^A`ed^;VG|oGh#WRskQ+~*MrYW(2aqG$=4ot(VMda0 zhdwlHe~5kDs z(DwwT5fs$ep&pxf&fc%;{HiK->Q>#8Uw*l{&v?F*eg1*F-))4o=9&w2y_;h`&lsP9 zIP4M0i9!+?Q~Bok^POUqf-vm7XmHElw=zdIg?_2fs<3cApFJD%g)Bcx^Cb_*(Y)2X z75t$szcM$@HPSn53k{_AK;8*oPYgsuB?`eVU463dPH7P z7;Cf+8%Iyz0=q+bm4YyASzzG84=z(NB7qUOJdFXya@m664^kRs%Z5E*jY4ny0GFf3 zunaP!2c}V2Mu+He@#83tKl&`LE{Yqm8kLeM^m6EBAb$x^(ae>I`(`WE^lO!kZ^E#1ib+d$L@y126ieNFpXlM(QRkv zi`5Dp?*<3=2m7MBtshGjyG%X{>K7Ep#a}L#?2zKX5Tk!*2a`%HxgIk6NZ(c@O8NVL{o`N$ z`rmE6j=gKWj`W3_T#=eUahDlgisaB{O84M$XI29lbcrb{N*6DnFBN6f6cmx-lw-_; zteOO`#C^J&8nPD;6*V+?j?Z34Sz$CVQ@8?iq8W3yuV|o*OwC4v@6Oc}Wn~}YUy-&SvVD^*9<- ztP4neWak#-lH?#MVp~ve&2~|IWctufE?`moyN>y}+Ir^F~ z?8jZI_N|AQcC^~3o~B+)wZC>Pq;+cQ%{A^)*=NZiQYzQX_hIHh z_LT|e`JMCo=0{VV=KF6ooi|2FiNRW;F;u*kfMP<#MWe; zAzWz?bQfGR(Ry$7vbZx3KR#(9IC3?e?y8s z6thzUHC%20jk#qt8QuR(|ndP7H52M!K5Hyz;?1$)$u)Q6kD7tZ-l`0D_|0lO%7 z$ol(-x{c|KxivKSqn;jWLo-T5RmSqh+!#8FDC}5+=2#UXD?I0@s&a7|qu=1RHr5np zNYMj{>?N$D=xLkfqzYHXPKw_|iOTGx%LOKX(INA{JTFX6x}bfHUQ7-!Z4`wn><)Ez zKkrEj#B-qCos|cw={>nGmNZAZCs(MrU&7b}bn}WWtWz$L5JB4YQakAjct$T-NGTO; zRQQWEhlO{=BN8AaF}y3X#5ChFXJhFL4i2$mxxCBE6<>#}tdcC`VI4}kvmAUGZC>Ia zTpcBLzTFPHzTN#;r~o<2O)5+o?FMJmt{-mF;QoGQitcpt-Xt?D3{7rb+}3sx2GL9i z3wqwUh~8Mz6-*OFIeixZUqwX-pLG3d+EDM>&QR~;X@~k3c9LeO7xne^jljXtN4jIA z$irjJgXM+?o%d+_@BjMapRpDB2MJ6rMPdILV~)QUb#K_bK!W_sKT2HDzlUw(tg-&@ zer;j-^Pm5Mv4&-DZ?UDLV_WZeu4%_gv3f(^|nlUE(iVYS4Dx%D zvchBlWR;?stilxq1(hogW#u$be13&*A$6hh`-l9^Kji-dp!rXC{&4lyt)yF5?=bT#DFq^f zQ5=AOCB43vvYC>SbZ7HkOp4OG6lun>8O^Qr{LS$5Xf(Mc#l|Yd#->EaeAaSKf9&j> z5-V8eG09ra@t?J#Nq>;;h;|-N9_Jbr=XjDEpQDqbwRoGtPEoE=5vC5*sw8KR8)p|7 zN5;?4DN|-UZoxn`<+m#8oxklwS^oL3F?b|oS zHpK+k4dE7T=I5I?Npz%{x>lOrOu(F@(p8s@qNFYW-DpBv+f7~F7p~1WauROlxUMA7 zN!{ig-4$0DHWFOn7R}*rcsO!gQKJGS5}vbiKe1Vl;pXA@d7+{ggiLI+-u-Z9nq#Me z^$M0NBsV_YfAWmYqe1ilg%Q9pSZ-~5qWmNTR1W2iaLhT7P*E05ZgU}X0Ds|+(<%K< z$7x4LNX_Y-a);CV4yPL4vYdt-*06;ml?@zDsUfIQnM+NzPfhIzIec$#-`aYOjzf5o zF@;|G_t!#duGNs>2$@Q4eZv`*{M2jv=I;vu8*POx6q}QLDF!Ogqhxqt`$gEKdHY@AwA`RpiEVe4sJx0?D zW$3T(Df?kGJ-sk(#8oMzE&$}{K^6t|DGMJLm&=xyr_0J<@%Xq*XW%C-^CQJ!$R+h4 zjr#cc`DuUG_FHD&m!Hpf5+Fd2`U%##PlUG55WUSMvVML(LlNjw(T?yLQuR@N>UZ6b z3)+0xLCR4T)wk>!y*>fwhU#@M)rlp{IY@ylsX|R$zwUM24R#HNaV()dcB$dI|I(6M zRD;f2f07wdg8wxZ8tNaa zH1oztZO|{cG*r!UM#Tl(@`Jl~?}}{-J4jv5VzBa@UAg;U76XpEvj=dGKDZz%R36-Q zdEf%@7sEJa-Jg3-zM33xe>LlQVc`7pf%C6`|Ds1y98zgmH94ios&tQpC-$ zXp1fC!khyM6?M_%Hc~{#+v*JC_|o@fWV>s7yRYj@pU1Yxw?2SINlJ>KSt212r;M&-|c{L6p+^}maYiya;5yUh`! zjW&!o9KShofv9x=s{eedsSFhf>nu4SX4&dHMxs#OijtQeFNijg1 zzq^x?lmei6te}f>j74cPHbyBnDFwk;Eu~E@t@IesMPsla(PE=0$QkUSF!{#RsyExJ@@;z@PbmX{ZBd0TElBxHs`B)zJxj?BF+La+vP<&ku@JOTEN z@;ZZIgWD$MX$jTLFE=nTshg=LiIG-$ySkZ~=Jf4aIb3OCa`Sdm<*>=GH7ZR_O_jfD zkDJXmnnB`Vq&X*5bOrKD_h!P4oMwm)-6mI@({EmB%DJhZc;g1_896s@tmuk)2EFOp z#3K%_P$3zj3ou6 zow5%(oU1tmZ4?`pkdRanjD17ei%dvu=cp9rsqIm-)%i_-VLRpESo z=SeAE6y_rnd+wyPw59M|qZJpZ)HXJjvQMAGM@7zOJ{_el?<#z?rWH){?ZStt|^gU#v9A=qW8*5V2USR(=FRev}0o z8cjnw1eUQ`bUM&*xolzj2TwGXrx)QN{n#@!4f`l29fr%Jqgc2Y8m{{Jkk(ipWnDpH zVssIK*U?q9!m?46o>o^$jSMkp)aN^C4%JoqD^FGZA_jdT;NuwdQT6h>KIDVGSZU@l zI5;TnQ1JNH>#eG)&Qn!*L??7~yq4H3>hGm9vE=6eb^;TP3AZS8sk|L)@P~braijij zZevk8sHOLh3ibDI5W(0nY*Lu|HKtkRhL*MlfJVp0KsRS&Y1KgJRk&BQ3*6{l1%O`O z6%|_+bkf585vCd>G%70Ey&s7h6^_P&ENXGtmn&7W_I&r4#DQ=MCsjD5LV_b}!#8X1 z5hO=<$z`C;_cppqx*dXpG3jt9@eS?==G>vbB-pN`q&rv)H=r_jw9(Jd(O|I7Non+F zW(vCb#jqXH92fh;iZXXVI1kInRL=;@L}z_b7m1Rhvq?r~SJ%iH!Cbv)$76>j2u`4+ z{tN#asgUiizP_C`csaI7U=(?%e_31W+x8e48QJ#muwl5z zIIB=$_r^bRots1;{pWxF2Z>+)cYpscuxkAC-%CovvA5W=7gEEqxOk18i!Ckb+mRmE z)*Pkc4Gmd2S+VrzdUHX;R8W+YyK)7nDZ1Rzb<>P4Z0J0t%b*!hRlsB8=2^HNd19|Z z7r86Ao`M`9hn5d8Bh-XBl7B%%MomLQMo_N!7Tk=f5V+fxRaD>(Q^kjJaw>{83Upg8 zr>LPIRge7sA%D9M`9Dw>cW&LgcjwO4q+7Rsx^?I3t#_}Jl03K>__vb~1fM zk^&j8w=SQFj7K&7IG|tgxz+{NOf%0dw`DsADU2Bu$3l73(aGF2NRR4j1N6l5*X>Ay z>c|B6IDp?s6Ix{0q|T(-q^>S^__oPRu1Q@2b}Tc)CNsB9T$_er*`Tjs*QQF7N)w?p zT!n>{o`+p;z*Xw%>Uz_)N!PVG!By9^`6gG>zkqq83Coom@NwKoWIKnxu4_)SK94s@ zS|ldw$Gt#GB_~H;5IBJS3fIqBS{ zK#Y*22y!A#;-oxx=%oB!T z^i^opd2&P=1MRW%`&LJ6=E%2d6h4mpMhR-((sBZEQH!uFooo5ZP*F@f8YLh{>A702 zcLT>9Zv)GLuKFM6*4GDz z(}96yWkw6r)63H&Fn*Lx8wUPZ=4$$~ABKjDJqt*!EPgaxTo^T6WT?5{@(-+C&?P%M zx@Z{jQQOeR5VH&>aA0>bMBm_=`yd0mDzpfc$an2g5*dEl-y^gmR(*V~M|>Yt{qE-# zF*GRhv8sclLcF{L>kI6S>!O{0@Vb}R(h`q2t`F*5fBW>hj@R3XF`Xqhou_Zv8>-`_ z;}!K5mJOY#pG8jgZ4~#^M}@k%(a%8?i3xww9R6+s$l>3hnb*(|>K2Nxs##IzmZ#|g zABPLX$3c~5)mW(fT_8sf?#fp+ObW)ReBJ|q%_XJbB3VdX4vCZh(63kS$i$xi-{&v5drDAqclYPOjt($`!xI27slsglOBGl|+b<2Y zgE#68=jfKea-4P5yH`|P`o;Z{`vxc+zf@TK0wUO>GYAKR&Ar8xgYBcNJ0AIZ`);7W zEvq8y5h;+}tjoT8S&tkxd>wZ892_n`Dsce!YghP38ERgVwP&}BgbElOBsg}tK&AVv zncmK>q#NWfyReJF^e3}l5Y186wj*lkFYaVq%;*mbDJTFp#kb> zhDgn3W_XNrbf^ELDy`~N?2MG}jG)CwTFy|v49^b@9<(tQvhsU-@n@3w; zn@8Ulob3AV|NiTL{KtR&`h`{X1rP{oTL*>!1Jl$N&7xfBoe@{^PHI z{qHY*-+CQ;k4a|qek*pQ@7^^M9cz zBFoo46&pFVhq&;{(7$2EHDrpYa0d+w?gmD01I_*Whx{!+p~tXA@8=jhGYcz8GHKuhcKORdMwvGMVwIbyYa9Ipkx zMlw$}*tG%nXn`?0R>mXYtaOjpU~KG-On&NmvM5NA8S4b=q*GNrFkG0aht> zX(n}*!wF5KIfhNDhv}tB+U@3H6O&3;liVgK3?6fsBydocUMCFSG`)eK3VjXVa7{49 zOyj1muB&N|uIr5i-J9Tnxsvv{aYHvp|AxN)P1lJqg zKx2wB*rNf;Aeo=mJW&qdgo?6qM-V9v3^+P4?+8J2n^TAKX^2zEoYRN7L*)?miiUKs zYn0B~JIy&kYS6b+$JFVeed<&VH`N2d0s2PBA(J>--%q8!A(c@B59u2&bZgB|O-(g# zJt~AR^bJ^}?@4k@<(qf%WTQ~3i8bFh=MEM2a_pnO&Af2GbDjqroQZ{_W1bF=aB%LZ zG2e>wuZ1mz>>8~-_bX?ECmn%b*U!}kmU2@)FI{R|N?SM^d#>dqu=Z?X$E+T#cqXxIO(escW6VA0GzWeyC zlKxoL_VI!GhXgIxX$oV zxHu-ft}l6Ez=0V@l)sL@+mg;i6lfc6BtP8f43kFL#(}qWG~G0HxG6y`BYmlr%=wSf2ddJkU6vKR9^BJdP?hj7~~5 zu?Wi2>)A<>Ug0RLi!|WiOf3EJoV_sVIqObJyJ4CA-Cbarhe3U0@IhA>9b{h2%*^N~@qo7Z zi)x!tyAX<6K7dE86qi+i_w+bnU^`^~ZVxOmNS&5~5b zv87|%v1OZM@!FbY?{TqZaWS2uzd^FY5<8W(UJN+s$5L$h`I-d_A&N`s9$7(9D@9g9 z4XveML&zzpNPb3f0kjbpM;i^nujHzAO!{SP7|pH5ZDR^%+=41A{08(_<#6qllc*93 zSSGNI0B_TIl$^;ag1xCCYlBQE-P~VM`TaxwrXP|!>4jyKo{|uWC7p5ar(3s#GxQ!y ziFc40DM|0JLb-QVSRb=VZ!qLM`VJd8UV?UxF1MI;EoZHCrI?tJIv$N) zSkfUekLi?i^kXe8@Hn)blbt_5hL=N2L{lDfGS>KI_G9Dh_(;+iGm$*uNEQQ+XrM=} zldZGMBa;Q^90t;IL{ti_Z6oVv;zc;6Ad=ytX|}dA@N(2wN5VCVc6x)lnL5coiq6O=Piaopx zK|#u&I(Wjtv6UdG5AZm!S&^)xQj6PrWtJK5Ym}XkQkm;OM&+=^$?5$ZAmy+y?TTV!eKwvbMGF){2+_@7; zRxk>)fz{bY)zp0UPoNWPVbiMK1x=~wLi|uoOrR=n$g?K1ol@3y8!n8!jv^PYY)#Qn7)ChCddOm!1C6BVH~HF%O6Q&jD8>0)?S3f7)3h8hj%C#cnoPndoXDfW)9T5;b_oD)hEK|J1Go5 zA1Rs=!O2+R7#*DO@=|>}$P*5Zt)NLo)hkN%x{jOI#C5?rhgE|V#?Mi1Iyw{nZ^xoI zEbG<)`d8GLPUu^IozN(Mf3&HD`X7+SaQhiR4mU1w3w1-0Td3f3zbi_ILDyer&E zcDQ4e;_m&6JG>lHG{u{fu@+nO?As0`$KE4Or(_8fN0u+_98e!wSvck3P(nXE)9^(% z{oY0}zx3@!MkRRH!EO(-qPv8#=H1L3-GiBf{iF0%LN6}bF=Vv+Vt*z$qM1^Q8w`%n?N4DKfuK>=4pH3j-dRfNsp_Ye77e#rlID+%6>dsmb0(9=5- z9-H*~&Yi3Gm?e4@nuGPl>m;U$CM6~D3v5$(wviO0lpgy!=3PokdU`Apvgt7|Q{KgF zrbla$>PUysa87ZKNlDg(l)?ox3@YYJLo6B0~;7{vxf7)a@eo)dCR zn;1BnLt=xJMS`wAW*z#jiTa5-FLax8auO4BbYH~j$LYQZZ%zc7BaWvZ_x1EK#<;H+ z9v&2g0=L9)C=NX=Py(La#}eh)vnO;eJV;NO-cb$*jYAn-v1}O)2VXSk1B1Vik5&Fe z!sGOVvT{ug36D88b2ur_IVqoVKWvSNrqI)e5T{dRo^3dV9NN#NesFr9+HrUoa(Fst zKS#&!58t$g*t3Cyl*kmmTkTU*r>3~TEftF68rRjQrb0#(rWVedccxBpIK_P49J>{C z#j<5|9(>XKBe7n=8z(S`9x-Bc9}+_%jqcO)QKwZYx2UuTnQDLt_Qnxm0_qZV3Yy%g24>zIHvu9q4$Hj1X$z@A!IM0Z8Y^#YKAI0;=c% z45G907hb(0f#D*OD)j6*3*RXBs<^wm3z;!1a&`m;xeEduyK&e*w}Tr{Ah#g+t@%#0>gKM`(Yn(G$m^nFC2F5Sb8{=1Um$G z?*{L7cLxV|2b1I|q2s;a{@_eIk&Df=v+KV|Z(Z!p3=7){6LirGBn{?c?A7QbhVf9nq9Em_26id8A%H)`i9FHMWFz`pd0jqzh|D5i9l7t1=YXtGN=f1;dW=?j_c|q(38PBs=xsZzs#oGoOYF-SQW;vCDY4_R=_#LMHsJ_G zGDYhp)4yWT85)c>`ro>5-`IWH1( zxMU?~B`0yEIq?O@VZ()qg9OI2AU(ZL_rssPSb=Y21@O@TG}k|S1|{+#TrVgtAV~R> zat&r0Bs4w*1q6jZc?KTXlP4H+oPOe_Sk@qOryXY#jt~+a9dk}0Jm3fkaeDuuBh`uH zv2$~CXm=B~4be}ZdU}ZGH#Mm>sjVF(K0q-?G6g1%YpvH(xidC3$Q%mTA& zVf}$93OB;?#G(<+DJQ>RQ0)z*ox{P<3};-@bX%{KEY-VwsSmvtBPET zXl`4Y(2;uT{RNG~pH+sxzs}E38~oY7q2uP}78(`$b7-h$l%}R;1IXq62S~|kjy2>p z{w{ktFhSz;SsH36i1c(>??GpxX2&K|4A~BqDZCT z0YWS;{3wsC3M2+Ajah_LejRj7y3;FUQV>OX4!4FoTR9**q^f#$bb$N?L7a zJBbgD!?sJ^D!_ozQAK;j)&>cYjjeW`XOQq9^-XDZmmqKu35n#t!o>Rpm~bY~=N zq^qb17EykLV*^tT(cJc>NcuH(F~{&If_H;NhsU=sYuh~Qpcic(lB)y1Lo&pY^v5w%(zlaK~@D_>xyT{ zDsg#JvB70m)E4~!5FD{7F-b`=DLmj{_*V-0 z+FnPK>ex(A{~VjXsima#7<&{tB8?H9oUZjag}Y&6!2O~(;e zwpKEF+v1%IjOhXHP}HixzL5?7m+?zus;56=`kDt!)U%CXW{g$w4pc3*ysk zt#hsG>&vUF%j-b^v(1HbqaJaTwA>j|6uEUX^|@&Vwg$N{i_#HkjN5dai!ND{>Piy> zlQeEGM!zN91!qw!qE8|8hNm7`gE|&KwuEIBpjI3#88oV01!z48XlB0>| z8?G;I2+6Uco8Wq5Hx8 zx81*=h%D^=0KI34dI5SXLVYyf4+vl~M^I2Wk2``;;ufU$3^vj5fbbgSpaA8#fG0sb z=y<{bm4FYY?A!>FP@|+tF!KmG4PfYoQ;kziNKnlw3yzM%kPzh#Cy+)D?VUh651F(7 zFh{E6&D`Pp)VVkIQ}(UcsSkZ8-sWwDF|0aUK~Jj6dk;E2bj&JLg&gNop9K15^|Ru_v{Gfu%-` zMn>!FJw~NHNW!+P2d)dOd0Ai?BS;qlffrriX16jU_KZ%0Fv_S==5Qb&JIzB5@p5_; z(~nWZ>E%Vg>BVIpaKO+3()p-`(eYf%KH`V z@AP5_^9>)LK~?xgeN-cSynF=KeAR1pkU{34aHxv!r>d%6PnU2s;e{R*j=X}zq2s^g zr6XZEmi*numRN91MES$s;l|?+%?6SeZf=@OZf{9dxVin@;5LSg3bra$W6&6E<7m*V z3PnwQC@YT!NRCkX2Fy2HT=K*i1G^L#W}MR-4U8b=1qya44-imc&*%gB3zIHVkIIEt zg2XZFdExBk1J74<%JV$(DDJ|)A>dyFm~(8oKfiQ-%hPiU3l>a1&>Xu3jZpy`M?1Rd zAvG>-u==prqZjR*gY|x7A^f85Fp2^IX3@@du@)A-7FphxExg&Ru?Karful#hzalw! zzEQI4%YCs98xFfAz8kw3Yh-l;D%#CB4pJc9c68{AqI$SEg7>gsu|q(`4nY+<&O}(H zpSxkPOM&X>g5(Gb>p}!JEG(197-8x~VPW_k;LOA5%*aKtKtWEWhyyAfJ1j!BM^M`4 z!4411JTksWoh$URZ=~xB3y$rPzP^!d_%~qSSo5I6KFcqC-$-P%wSC*}UE_rc9*#+4 zSQf*^v1ZxpXj$CGs)PL*-$-VxaUR97*mA8H{}h%m^p+$2n~rOMaai`shygoYxK!lK z5ZhEijrtV@=?+~6)#VB{4;6(efTO=g zj2dhgz+ce7Rg!MT;b>G*#Kn>BxHW_WL5?>EbmtCJBicda*r@;tH?Enuo&Wtq{#GCI z-(LT8_0FB2B+l37>!0qW{DEY})q5#PSDC+Y>s}HD8%lRbUa-bs21k05QWCPVu}SGl zn<+2TQ#R9KEXBYhHYQyug|{s)wPKKZO($6q>l_=4k%tz$Ig;ro_T}esEg?3tUuHA* zE8008W%R~kpF-*)*?An8(d6v$XgVj&I*iB1BaI_x;}UDIx|VH95prU_}orcKQx zD|AhVU3G85HJWqd1_m3tO>mFq+yIlK`KIoT8~U!Wi{`i{>c3cdk+_mT!Xi8o1CN}< zIQuF0ry|%xyIcm!J&6QCtOb-UXCD?)RW%$)GQ@Z0)jpzGWP2ksgEZ$dScb0 zthYj1Fry!yD2I1EkvLzcA3BsfKGlFZ+5uMwlCeQ^$_T{PkO(1-5#q!Qj>9>p zxm2eRr_{MQr_-9#xsKDh8vBkz=5ZX(*>}{C-hk)`d1K$&8gk7ZR1VBIuI9yONEldu5V{M@-(t44IVk>2=vF0kb* z_tv95rItq@f#=pyQh)AT&)1VuhHeA~u76!;+*c2&j}~l9ddj%0o`gjiFr(88Bt2jl z6--hz)%W};TL?4^>{$@}jp;yuzYGm~7MB;NVIf^M{6Ruv!Ega~(dCFy!?M+~(V?=@ zGQ*)oZBD2}Xb(YejFxHp_?2l#MEIeljRUdT99Lli>EOrjL)_}-4fU);B^TNw)AwuYvS#ao0m@1gqz@R_`A^oSYJ^jF#NszNsIhE zq2uKS0GJ!z4P9zja-;7t&4wsVBvhi@G{;zPENO=NFKJe}`6HQl}A(ko zMUo(QFFZg8{p9)QljrH#eUe*YJN>Fj|8|2Pur>Z);K_rR`WCH;NtC zFybH)a*UV9^e%n`GK%q)Ha4py|j%ST^VK7=pI|)ikb@k!WGH96_j?;rBF_>lkpPj{}~!;AwpDtB&iN9@%Yb$5 zh^yRVuc!n%Phr6UArkYBq{chZU{8n9N$eR#ha1l}J}bp)#l}R(XvM~kM{`}uxHHL& z=$Q1+>1UgjmlD?;2eH{&*^eK`MxPb5i9qb<&)Lq#k@3dym~%)Lj$~_NBvWRrt?O;a zt>d%D%ah~FQKAx=4A40yACcB*Qi-&+E}zM@O`EA_;MWZI(bpSrBo-dg-0~UQdV~79 z>T)`jP<`8A82gkuk{WQ7a+L}xjWm;A!R98zt|qRob;G<_;r=#+R7_`PDosp>hfRk~ zZ`hmI&gr13Nw-PA88~yiOlZ#G+O}p=8cZ6^ zxi1ncaRJQ%IV3&qC+gqNS-J0uamI>%B3z+y&2gA-e3E|n3t8Djy`Z?bXU_tZ^;lN~ zaTev%r=S4kj*b9~I-YQITS!fas8RVO%G+u}KF~Q#LP&Ac1UbD|c5(s+%;`OF=F0C$ zYLN7xWBd2mv5=_1BiFahO}(EYjd2)qNJ66q<_&xMnkjlGWUBT3Sv;11u94I*e`9W* zns1(;-&$BG3fefR0#(GNp}QVh!$$`+@476TUz7ni4d z0u6tFW4>%SWcY(2U*Ab;d|Y0{F&+B(!83|TEb7}Le7^e(p&gbah~KJT1jZSwK5%pR zM0}4p+pK)l24qz5McFtSG3X}|M^(8qR`u;$07eJB235JIo-G{LRdrM+Krq+29`#gb z$?d6^|B@F?GTE#8&=g>To9(g1+ z^2YK|*cLh#N;2baXw^ZLv{^aBiZWj`&qW?V*}K11!*U?@08(UflG|1!%=xPaqOI+K zWQP3gg;y@mFFbhV$?AiiFFeP=B>lqk3j^oR)64U(o)7Q}AdIpOXtD< z8aQ78!)V0-pkJgd+Pzt7cxEeS75(!xT7U@I!lPKiM5-kz_;8dB9opI;HG=m58NIjz7Kgx^t6$tvC$XU(mXVQ} zc~L583u7R;5mGImCax<*FWE4ns<5_|QvZI3mN zZzMWAM!xlVeEG8NVYzK-`K=A}jcrb*aAKvm?OWS6>5Aeu%i`_h;$C_WE)I+}dcPr! zf*A)K8sZBh5E3Z8y~P|$!IvXErO$0AsqoiAXy8r+EL;gk|^56g0)myi&azE_VpI$TF{0{xO z_YP~6TX*hkD!og3hcqmT=}{KD`R><_`dFp(bfs9Fjk%+g9uo}@X?hBbqu8@Jlkjkk z-HdfcBfYb8%u6ljXswiSAj~-t8@)L`{+UZu9w%!t-rPAJ?$XEc&S+90x$!vGm=2{j zmB$kMEBYlKU@A2(m`NVDwoZsK8`17Qh6UT z#~t->Zyb`&fc9{zaXLHR78N>4vlRxOPCDur=Wgk)hBov&r;#<_EW%-<-5>w|e5jmXD-<$=X| zt!SSI?7YWly%hYfT9O;+rtb-q+Ek{Q&Ozb>e`gtE4x_*yoQv%#D>M8t{n2n?8di

J?MJutdp2YDJP64B$~a&w+t(7B5%aDa#?fN|uG- za8!W$RdIO(&24*GyCgauJ=$>C%i06XJj=lW#jzwYq>g%&$RZ)jfYDt$bkT#t(cRt6 zu@pOD7VRh1K?fu*n7`5A-wnh@Kdhqt{rx*^>0q}8CLCS(m5eS#SBj8jDMEHdYE#LO zxT8foqFaUjc`IdbDk?t%1k^ zu|SGp`*@qAMjMHUwqr-jV#_s7pcEe?nR2|g_Dzg2*uH_e24)<+Z0EoTq?f6qz2I=5 z^9$8rW*WG1nc=1!o&Y(JDQz_5WFNA(PynnRK77cddR)#h$A^tT4u4sOW0!)aDXS*k zLi1HtMNtK0Z<&WGau0EBq`T@X!mVf~ai~N!nIc~=)nrt}KZ`IYCq&^^kp2Bb{ze}X zMDtroS8v_9M_S{jcX#gHy8{;oOOT|in_9fW{lFim@^A0_9jW`NimE# zfA^9}VChMaAxfLk&YK{PI%~ylYBAM(6Z;dPESx`sEQ(}`*5lZCHjz3fgA=Bet@Rl7 zu%F@A`0VUF9`8Jk)e74p}!Mm)3yCt>2w7<>LVBy7Ib(Y)Y|!o$&7TgV<#t8*t-Jj1YJpePn59DqG{awNJ{;MOPwfn#3$ zLai`*u#rQ!O5rbUL=}B)tx;o7seqW*GRPcb4mOLDy!a6qSXK%K2aFrj3r5pDfq{X` z%RlG|ix$xlyI@#WCU~Q;gDx)vyD?g}X!vn)QL>PhaYAK!Gy(>X?`4L7l}GqRj2d!r z%BnVK9FP}h^w9`G0|WLeVi0fnk*XL%E=AiX!UrgGA1{8!B1QGBm)Fxlucw%QcmckF zVim86>vYa*Nwm@P&(ae6HFTab=X?U^mZEgtMny>$(k0H!`U3=}sq=OTpf7(nI7pem zF{T-c#Y&!NjD>BZN`zxWH4j3oSb5wfwK2(NQJ1Q-RyG$li$377$Lyp$*c`&z@c_Sh z_wMso@-DNiEnN6#c6OEzf&IFB7kUF(mFMi|fU1~fp9a<}@QluiZWVWTa5%Qy&u^VS zzjf*SrGa*4aKOmnezsHj1%3_-NDcQ2w5R|^>MeTd(b;CP2hp+T?Y;E~6|!3xZ}^h5 z@V4-+$l814>&q?>NeQ^3{Us<-fsrG#U#eEIyI3M78etdr!ZM&dib!8D@;n350toZ4iy2{T7R^vs z-w6xb=?d$@EMvr@Xhg(Qiood@5mwR+PNsb6`of2-M2fcAL+YXaMYO1p<35Znjx9-Mut|fb7`>J}zW}bh7sd_XMT=o3gDp`_191yE_7iB(O}m_enGDy6*w8gnLD(H}gD<1NEKNYUdDdd4Mi*#?MVRp& zyufeE{{A6qk4A?o6&=VW`G9^YSJtmga2h_&rP3P!XE$7ECwO+n_{Mnfa z=gw$S5tfZjfXo>QwmBY6Z!*YSi)R_qXd^q?_$B*AqXFX>kL2pu$arJp@oeLGYwMTR z@yQ~fG6U-<7+{g{;ChktxE(oDpUaw~KCRxiz_vQlHkYJFIf`K;%MI$v>m{`@lfYXR z7)RMqTFqeXoSUvUn{!-sndi3o!d2gu4(K%++Y)nL zFyCCinbby(o_^d5y%#Uyn)UT^^f1l<-W*RsEBEh*b8CIv3eaDA;o;#y5F}6~Pecgz ziSj4*i#`L4@aPTfvY6D;2@!K%fO}Dk1b{htp|C2>p2<62bwM)YA@FNNb$t z?B@;-!QrSmv_CvdCG}B5D&u{PJt+$N);HHq-?zT!A;-0;YpF=9(9_hL!q(J%$dJ}I zK!2IfGsv808B>D!MNdchQeP|ykwQSt=L->v&Bu`A$ZB3d&aF=H5^j!C=6@B|mbMh0 z)V8pGsGTpg5_V9MAXeaTlv(|B;wsL?)TFSDccKvH@OKD&W zDUOz}Wwk~X zQN!uwvc+Y?GLjzC%O9uxIH0mz2IHvTs$qno9|;ld<&Q&)+M|mwk3wyHU*#=J#7FIq zA0xhF$l(X%=#apW`mFjz4EaQ$OXcb6Ae!likc;&>ql=2Cr|cJ_r`7ilLMTPp0!OjsCg>*_olqQLg zB@s#ac}&xvq=nmo=D|VL0b4nsK{&{wnddT_R|U_grhL`xSe|^I{HumnF48i^MJ!k7 z={Z|Q?@p2wnPm>?tI0`@t-OM+_|pRsP@yBi0GoVue)7WXI#!cqhUq7TnQ^u-(kyWQRc`9N?|eHKxB4@%T#vMyCgOT z0xj(3*zWS!_V}`0wB5J;rLRu}RK9F`jCe4EqpxiQ>6EoLkG8gL(h0DQej~N9&BoC$ z-+I3}a_EE}*Mw!G?HC|)MsXY;?^lvG>1i`1ox?0C z>6=)Vq(^IQYNgQAOJ~v>(J|5DtXAx}*5-H&CLUVQBp7-ixRR`89IK^e{P=O~W8ve- zE*MWX{%rg?d)&BybjG+d8r(4Y7&o@g&MwHd7G<&ZJm1Jpwg%pOCb`_&Hh0{*z}nWj z-nQI&rogrUSryxIh>bLZa)Y!;0W^=yjhw+2rJ6KHq(SalF%>#EacP@-Z&aw9FR z&O{hFL{}`5DYr#sY_98Yo#f)EOsg|(Hl3+Vz&hn-nu#f=VoecBK`*SXAb#EC`380< zD>rnT>1|Wj7rHle^K6$WpRwm4}y*NChNu;%Alj?Oi5B|Yb2 z8%q(9ZN!kHl%7a!ocmf!BIHCQVoOPVtV<>i(G`oS2I&rHjD_`{b>ZV!2qdZT!{|p~ z8D<|nWr0|%h^UIu!gS!Yl!Tq;?}cf@#j-%dAJdD5i_;%DmoiP-;s@Xx%hUYFb{77?vxAD=3^oF00AD?By`Rb~V_EUQCRMkh@i~XWp*EYxyj==~o zp*6fnYP?l_>-ChgDo?#srL@XZ9kkS6my)p81>uW?#gdl}>>5%2PbKh1l+If>9T8NC z(s6@7G)fbd^!RFU8~YhP4h%UOG^_j%LP>Arx#W#uh2jFaF*fF+Ijbp8KWFH2wrJdC z;@91(GZV+`1Nj5_1D9Dykh`Dja5(iS>YIHs~y;aRbT6^bWD9$Q=9@NLkK zx5cGPqO1)p4)$(5I^&spZ)I8RS?r;+?egV{J@3m|f-B1Pu)bLW0*1#DI#F^Nc=Nri z%MQUMyA0mQ3J!*&!?&b6ILjfJln0#;CMn{}yOjQtene!0Gf8uF+Xa{OLvLh;W%ln1 zozZ`>zduZh$YP$Mjzy_BN?th36Y+ zj)jAx4+xGgBroV`WCXac5oA>?;S}xrLQlureQdv2>s|YX@Jg}eF{zH;-ebpamS2k7 zijVm$MjpqUS2>s&`C6qQ z%fA340dCgK6s4f13@nbA*24~P1;2B}MonTts3}UA2?aG-IUBy_$s)2z|GF9gn6iB5 zkH{t$i^<{tqzkSh09P^(<E;pr^*iFnf?l52!dz4rlq`!_;N{LR7dB*}I zCYFBEonxa(JvhfW$Hu0|L_2H6prS1%2HOu#@B}W$U5TwAk-nyW`dK`h+cj&$*wd+sy6si8+b-0j_i+hq>oD;fX=_No=@= z>%}1fi`Li|aVr7M&-7LT?%&r-j0=xTGixb}r=5se@EU&7706Q_Xu!I6h#?!7PpssrDTqbBFIqd5{!IJ+z++ zu}=*_sS1pvty3YOi>6*{J)BDA*4Q_#Z}#7(UVF12Qg|)DHMKP#_R*=%dGmeDI3(|A z>y-I^r@4TP=A&Q*Po0JP`2zG+XhlayM@;?_eX;Xa`7Mo@bkxqb05Qsd(UWt+DSEE) z1Q^lAz!MfAR-`=wTh7(i;=OfxZd?Zdy@mA0`g$$5#(r)58hBD`R2EoUYqZ|jv(C~3 z@z}t#o>+J{0*wNvjRfo*_R;ADBi^l`%*}8iaM>^rI~9<>41wb4p+5`gax+|9E`!P_ z`vFzL+ZL=;Mn5haF8hrxem7jy9yMI_8`bvVWJ&~UjG<8cjr2`a4gU%R9 z45HMG^1nhx@7`r;f?^RE~%`kW!75FO745Lxkj4*ckO&jHW#-tLzMDh8PR zH89}m&Ct<-OYWZD1Kb+B<-XQ#b+GcR5YF6q7)9EPKdVHqStP@vKumRZEGQQ_mrzyt&<7B-WrW6@G1 zVy_&GEdqI?h#Tt#ca)BHz960g?5O%0{pl+j@!0klDeA&(qw5=cIvB%24{cwzJ+}MW z+P-bR9wV1RZ}f6drL9-u zaY$c058+lY_8~shurUMQK~_UeLrzhHYc@DZDW|AtBZuo8S9I`QvaG;IsAoXkTTTv6$MT|+Dw zY_4EJpdp8BsDcK)R{8xy{ste?41Fp8lyv8(JNItgxhkXwdSa83{&4SVQVRNFlQxr5 zNMzjGWDo}(E2Sd_OZp>S)ToG&2KFu79jm1DE+w4=$7Zb7rV^$ZF)`^{DOy^OV`DMR z_#7P@bFI^?bFB+RjS8M1ISl9pPd8>F zfgMe#Hi%@fd0kyDXkWS2!`QIQ+#W7BFiA^G!}=sQH^HO{Q;nH~8IzexoT@V!ZW=ae z&UH-_g2OeTX(oXL#*NCRn@uLJ!^p(aS@gtKRyLdJR^EhN!!_Z?O;^yr7{rm&l*5{X z5u~Isc)B66zI2)MrLW(d6PKtTx00j35&+w1Vq%;8qpo5a2gL z&>I`n5p)XdR}d1h0jEJ+)z%S0YGcmn)M*Z;Q95SMsFRa@hdoIQl&RRiPeqAaO{#s( zl)Zhc{o!FNy=mW?nri>X{@VN2)Tya!Z{EMbW(Dda6-$=)Z{A;fKb5+l3hq~Gq4`m( z#1u8pKbk)|+BeUinkpob0S^b`M_GU5qo1vC{|J_id_Xz!=krBfJw_h+R`XC4BpR%0 z&pK2}xl@Im9Hl66LpME7Ga4H?pwfb=2H>Qn>w)LKe*Ic|uGWau2SysTMki$_Mknh? zqAVEol=c|?SXd|%a$~)$^hX&$9DzMOrGe8u+^T}6deR>YKLQOGdVm&PC|g)2kr7xH zDC+7-XrNBTaDnF>qEiK5q(aagiyg{P#G-b@Due_)8>6EfP>Jy40+sI(Lq0y*s~?{Z zYWrzNL=0&Us%j5ve_#Es`kfnMBUA^6{8XPtL_8I8BVuXrEe0E2UW0yvUe_nsSBj>3 z(jRVby>#3rfEIOIa+`3A((w`?j_VWttS*)S9u+kzZd|8AY9uO3)X%>q&7tWA2S=!; zrkf@VqzJ!qflBB>RbFVQ=Geh3HY-(Cc$CkRKN!Op%UyRkH(Yq&G3m~lWA*|)!#L`G)}b=Moht0) zKu*@Zec*gMXH`5sIiTW>sm7&Cd+zO?-j_DEw%RXMTyl4Bw-~@wV{c>6VvieQy)7QK zV};_qAthBd9_?*#dgW0CiHt{F4@-IjTElmbQ?VPD9eg)@v!FmWe3|^!O=<&mDh?$) z=LqIb6;#E7a^7u62X=eiXpQa95BdxUY}%SGVLq#;!gjL z`c4>~LISocQ++2RLp0CB5W3S9rk*h}(pA*uk&&q`q{p_px(9PdGq%weoAIS@JHtbL zM1VMown>Hb!5qqED#9~r$(S!o59WMXT7Ki+l(k|@bjbEvLT9Ye0j3(3j^9woW_kP# z%_)|>y=?9%w)}RCF$Oj&y_UuFH^23^9ZNx#HXymo!x9(tAab3rrvK=74@n0XO9Mk)wP2-#_I4?;rBt{t3oVk`;IE zfozU7%B@X^j$0|fa@=`+@7`;kZQSFzhSDadRIp-^R7UJ3pGe1>N-tm1FQ+TLjM+?i zDc&Zj0lVl+t(cVbbm!>Do8wrWKx4#5KPK6M(T8A<(!)4O4H!(ZV##*?oNesv9Bu3z zEs`nD#^c8E1=%mLPsuh;h8jsuj*O4AwvHSxD0pdI52$%T0b1$PfHeo`tGZlL8EKJh z8s(hIc-o9XWVu0Fd0IVJ#u}iLKF#1Z{gg-6)frUhrjhWdgp0!@@^+npNkUy^WhDn< z5l=}2;M{l28>7)BnKII$T?>uND>+-GBmLzN)}L>vam@?5(A=O9<#REZOzP& z-BbO;)J*kTQ*XU{?eD?&ADHKP=7+g(FXx=A-ITwab@yKDvoafawvh=ChmKCPX|_nG z*b5Ape)^+qfi*`z{i8>Z^y_2vAI02&eS=%w=uF?9p1wgbF?s`$H*R2`0*w(g5b3QK z6zS_LPVw?c?*T>|709djdN=s$d6WL|y(4Pce7)Z)S9trr5hZPR-eQm9-&R4oqvDP4 z>>FhySiEOvk(GU`%&t*k7iInNmM?4_1Md9o+t)U2vo>$HDiXLdmb+A5*w7W5$u?3Q z2~965;M-u0;or3BP|-9wIVmM$6X-`mg4v{5p4q0^WX|U69J9^MJhRC>kxT(`G;b3s zq#a+|W~3a<(n=Kpf4%yn_cF>?XxN1z(kOMZ!GF{Lt+ux@EIOuO3f|eRs9to13PG2O*CMA@#xIVmYLDM_biJ$X1#RKTa}D@6c*O#{D3eDQ z$3@XfO@Ma5;^NDI8b^)A(6O3DE^$LjWlW;U;x*v0!a+;ab1InzyoL^x@uCm%Z0qTKlgF5#oYMSSNbAw9;Xz)6G>z zaP(VSw*t)33c{DQ_02ujh^O2HVpIUX=rX%DN|8~Kb}OrUuF_Shq>V*K>Q!Jlu5OU< z0CDs%a#~RbU`8Dq?987}%lK5y%PyQw{cxCa=TK z(Q$i(BnM_2BgrFJu6%R`46H}kMV%%42A?fEJDY#}_>nY*^T+1p^~aDP=A<=9V|1H; zG;dy}!vYcy*r$-JFkkNO#=7Jv%2XbUc*i+5}%)$6NUA_ZiU?{(xJ@pT?&fFRp5J+lU0O!QJpChipW~Rfbc!XEZF!+w@z7g z6m}@`%wI)O0clMcf#U{uQ%;4$6T9W)=uL?%K3X=c)lKfhX z3NN3yESO)hJmM&95i6GX6VETN6`nYu1ly=mOYG&?77>k2>Q#Ex!W=NAh+ZY7Ft&26 zg)1pN7Z%cpwTPs6&9w;4q$GNVhs{LT#%n@(V2uKtx$R66&|go%F~7vkZ8Ns)AdQOC zOp6NBByI6DjO*!*Z7+qex|j(wjtkpqsgJ^hgUO=%LiSy%z9f<=q%xx7s&k{_;2=Ga zo4ap1>Oe>ad>pyVHJ{EfNIYwkYtEz;JBf8ea9u$sflZI?~II>Mm5{Fq`*blRLL$uV_>(DuqMRkUC60@U4QU%&0 zJ=;Foo}uQXJsRru!RydZ&(xRN8Bs2W| z-zLnq*;FLh%+Att%fBhXroyI)lm|HH36nN94o&ECb9jLYw--$g4mmkZlUSvoE+q$* zDckG}g`Z=S4%)Xl9^0O`(^8N2`aH}z+Rb1M-4;n$NR9TqoVj*3c98aXQly&);|3b& z8`;V+XNlE{Xl0wLtSi!;tJJM)L~0f+Qk2sRusLw%bPUvb;Ur8*0E#5$Ec{$ zXobz7Mqk)2ig8946Ts%Xe7d@XtAho~6fW}qq!@b>=!@&9P`N%|JOyLNg3r9+yDsK{ z8D1YHwXtCMZj>uk-hskl=;2Z90kSCO7xWy(6a%V51XX}DcMIk&x4R@Zf|r7|ATI>; zYbcmoRaBQ$?=p+yqN?lNi>e-rs=PrN7Yi1)d3ar1RE2e8T>I|$qF1Om)L!%)cU<)J zJUK{;LHOcHtaO6 z1q5i6jg`r3*p=A{RZ%l2Z#QuPT(5_07scTP;oP7f7k1Db&k%%VvZ%#`#kC992IVby zm16O1aKi4I)1U=r9WE9RpIP){rPA+0YQq9nj%U1DVa9p?&3>2Gy{-M$PP{$AYb&b` zct!ic7uEudemrJ}ZNb>~TMG zr1i)V_p2;9j$A#mk-Cw37#0pRtE8s7ucn@Z>LBg0u?oQXp~I;{du*Hog@dOaXjuWs z{7_%#p>yXt5ta4pv;qRG?@-Dit3$wjStWC88<-r)eV~3NCkLuosRhCh8kmxzrq;(j zDX?Y)_NB18NX}>ThKQx$e4QnS+B*HokD|1Go!-b_@9F62q3g~gz<|_Pr+jp#V!|^clWHDcQixrDG_qr9B zH=|nRvH4>TwG@~i#USJ{{R!OUi3>WQpFH}Qj>^s8NNlyRa2&`8$;}AK zz?|a%C>#a`2NDgcYXw&niP&j_swy@D>nr z?>5x7afzF^|6Ap^xX$XMqODE&&Mds66~`*T`FdS}>Xf%aZX83iO4}@bw_&i+*2X2V z6>x7%!a#~d>}#8?CI?=lykOcW6GyqYEdiY>O`ACmIdcv#avbJfpe!ZQfgSk9*hz_+34UtV5;(k~U=6j8TdQQhn9v7u+TAW@P(i%dO z8%dBM)w^!07ng!B2CKRb-Ca_3RUJ|dzN>oi;$60kx{8{5*KyUCUaEH)$>Fu=x~S^v z%CT3k#qn`%FAO%kYP2!a7=JnD$j%O=RQ`;@SjQUrrk8+F4Ny2V5Qp{jtlZ)*)vicyo!Qvt{_3Gi(PWpN8s{1*P#HJ#^;&qf>VCkG`95~xJ6zFHwm%?4K%;Dg0Eb_1&V2&mSCWFCo8+Ov<6dd2~VDfo# zpIS%C?b{re71VO)6vz>K*7)cQF2_d( za;*3CETgQx*?E1v8DiwI`FhVf0AMUbKJ&B#Ym~=K8x{Ezj5L-3+(2nc^YYQh%|{C! z!!ddkdlbCa{rKo|0UZl?1Ko*)e?VX&WE6L+VEC@6h>lS?zO|vud>6>usPvHGvN1)G zv&1_p3JSZ`k;nKRAK|6{rXmBb6zFEUUlE}QEDq@Aslv+u+*4&$q((A1MaA88Lq*hr zev4hW2_v~7E2}7XoId!={pXkbSO1c~92CF}j-$k`754rZdm^^5uq9T4aKJlC5(HFH zrRSoTK9<|u05vCl5x*9%6dOUxBA(vn_Img^!s6p2;+583C536e(u6q_bB-_$qu^vl zRMcavu@)8)E^)uYQ0w*-91hzV+eJS3z##2|4x zqn1<#+Uhf^4Qguzjsr#Y24ZbO0wl4#zG|AcDfZ<6oi}iyN|i~py$Nt&P#7ll_D~&S zvH^jSZQ5|&)ZQLEQBx#SOi5E@X6rNzgSznu+1Pq}tWq+0$dPHEogG~dwt2LEHh5p~ za_Ge9ko2fezoCDlA<`aFBwb%WGF?BCiC|!HpdXf(DBh8gj|PHb8U*+2!2lPzVVM%? z9Tche-dj&u4AdtYUL-#Zn)5`@EhR@wWG^6NI`*Bo6rrrhQ~ zs|@1TF<(xnw7soBfm_9~Hj*AT$F`1fZ!BF;sIYnQde)y*#$5JNgk-9T&0qiBem39=D_@!w_OCbIbtaAjl$AV=R?w?sIo|R?g^=mC%Q0) z)>Q&Q`bnq^0R*E1pSrqFm6a<`c$>0P3Ch=}MjwzyeTp&t;M|Il4~dat=6#7y`r;{{ zFZ1Yb>w;x;%FqaoQ6FZQe}T5(+O~Oab2A(rH5{F~e!aNMaOyf6O5q!&>kFd`S{_3~ zaBKV&9IW*dOrwB$1qbuq1g-VKVt;}$$6b$$ZV0U`sk(Wq{h6z~ewQy*3=9u3p+N$3Y!k)aKeY5Jz3bJD%g?i!~^4Thy*$ zSrV$D9pD(M;aOATS)(!bXGc$sKWm`9jSk9~$N`m6RwgRyG3FrYVRv6cUSmwYEDM_x zbkob*mC4tX$v@oTj81@TZYNnsNq!7s*J3v~IALcYFF+i27Iqgb9)c=L;sc0LCnpzf zsdqtGWn$vmGr&ck?d@mL;Ub<+{r&x|dv78$`*1JX+c4*F;=Z<0ct%AG@k`p(jX~(LFudu?ndKz!F$0^dgC+aRGOJC`WXBTpv>t{9~J#9N0)Kj4Gza~1(oCb;c--vInE%d z#>ZfeHpr^T$>Jlmin_vX86NTJqgRSw5Z5)bYDz*gqlnkhgDg)9LiHtgzW*yO0q&rNa zqiYk(P0p8-`ltut%gMC;$skIB@JX^43w?Nr!4N;<<{ zKPD1k6*xL#^uYb%rOJ&MU^s$;B5w@n`SPJ&B=^G7dksN)4;lakLv8(o$e{P$%8|Zz zl-~v^KTrmM<2`ALAZ%5}8`LT&Ux7TwH z39m_CylzJwebd})xJBDh9Gl1e^iQ}u7D6N^FQ=Ve#AIU|?5|DBxxAch3_FSd8g0*M zL|v?9kr-sm=^_{Vr0x^O7{Wg4gH=l-)CGObReoa7s5I*+VrywpQRAmdT?UMzxow5b z97bP?jJlqX(0GEyN!NVw7aybfl_{g*VuVzRyNtdtUUVLY(Jr4Uycl7!@eb@SA4B{N zhz>Y5-qE`pnH}*?sok=YtgsoO2oF*Nig{ek=1jv$J9GGWxwk-y;{ z9TSEQu}~Sa1BPR4jP!;EA7bBPhY<%!jvD!_`!H}|#xXHia~}~|k{mVCcI6?dj~Xs> zv%6q7XlIA&l)*nuJOum8#o`(Kqt66r^x>e36ZR>4Cosr(MqhA_K6JSz?NkJQ6jFrL zg^M*>>KQaD%{o?Huv%Gdby_X8J}v5FPg|dMVpZ{%);*|;ZR)gjRq>j#%cUte+`{lsCXFl}Jha=Sw<<+fEaq=!{U zN^-~TK(*w+WE9ZDtdY;44IXKXB^3j6Nbov=Hj)LyTpGkkvTaO}v;hiryfsT^0A$~$%|s>P`>;`~a;yCnKDZ>WtHr*)6Ad zToJcnSpcz^f`$7ub21h8tH`SCg4t40<^9nV+|o#7L>Wifq9ZmfN<58?!$S5MKMjF-1F4im(j8c;gj8d~0n8{9&Yxx+h)WEa z-gf|m^Tdo0w5A*|s7^GPCd~o-7nik7S0k|k2~tY}Bn18qpuno4GcpX$my^bbHkmdN zwY4U-RSg;TwY9a0CMNb785t(E!x(HdnAV%to0ul@0>$(^P@_88!=^e7*^lgb zlA#kloXKUe!`V7gKRq|qH`wdgN7qYT^p7&@No=GuPxMiP4haqW7<-UM8`7Ejh0z8H zkRUxwG4vjJM+QYkddCF$M(W+rYrwE$07DKCM z^knWvo3iqowl`88{o6JMfGMNd4Wo@?vu(n(am>G>;1z1ZBuP+(C`^hah*f4n11I6?|;mLe2k5T9f)U|27a~oX>MlRM3 zQ;(nMvMReaf`jS%Cz2PzUUxy_2=?L*w_vWPzvzmp6|W&zRo9EEBBY{ycl@pwPcleo zh^5N7m$tBpI*wzDa@SGYb8+mY)F10O7Wz`#Q$w4C#r>C|P#K|~j-DDCf5x*W07Y$% zygGsGxF1k9Rz~t7D*(2R`x-U!8YCvhV9_WmyI*EEI5>z<3jJWN`5=i5)Tm(QF<3)y zJY#dmgoXUW2}DwmO1Vb51D1{nsh-}#X#&HKhfWrQ&z?zL^w(UTxjf_&mDarvA3|ur zxZ$+d`ey%L>xXcYT06NwS8zw{&C@QePEMy!_n$^~<(_pbqc>V%7*@APS>($brf zt)rByWAE*`y637p{Wx-Zb)zE3cR?B@p&^zkFmpg;2*UXWs2i*IgmHAk zz0+?4S5x~AZ*2IT1O5wTZHMl4GW83i4dB4|70wOzilzi6TlHDpwqlk!9p&d+B`4ob z4$RNb?*n5a8JsT+Hdtf?GI*my4S-RmiK?mH&Ib()Rk0t}M|i2SP6~rB^sKA((B&Sr zo{nXyi~eKJa`Q)&sH`uWfBf7`LZh2rdXQED_3Nlq8M|!G9Vw5GHp4L5j6BL?^Uuhy z6!4qKtdP*??k)h2lpahX72c16qcVze3UcDEC+r89xzlHC6-AVINS~hRRwwS70tBIc zTmdcUbhF>aE#j`(;$<9w#d+t1f-!R+LeodmjQAs3ybTR#e#OVV6N?ncQw{7-Z>A zsDR#d`upsE+;v<=eZL%TigIjHz{hpENB-xR{8#>x$fq2{NaHzYVPjhgPZSpNgachF zoL!;oN+*CBrLUHj_;B8xgvYOOS#0<#rLb2@=uBym0xL;$7LQ=;TG(1R&qC+~I}+P9 z^r(c}hOZ@QhHHk$g@;8XL41ULn9+Qy85VX)GwOqFoMu>rEf>dL3ezOjVLW3SVJk=+ z!pMPzN>rS2Tw0VcbA$;lScox{2I&s^3W=&N*_X%w4kT5o4?to}9|$SQ%?*(PD;aba zl3Q(XAh9;F%Ah0!L0B=@KxCzg#6}_tRZ410YRju2L29Q#{wlAHHmRQ`(NQ~WAhbuh z$@$t^(<+l;6A_G!Ha*X^DI_*(P3=+A#)T>n9`>2_JjFo#RVTClMmC6G={ly_(V0SX z=%hF3)W>9I-pCZXBU2|kCLO`pXnTN0Z}2WfK*03%y<=kZVs6Cf`Rc_C#0;cIqAPab zeI$}8NX9mJ59mDz@)p_y(G*f2-fiA`-r#TqDf_-*LE&3*N7>)Ef*Vx?TGZdSO_bC7 z(x3CKsNjqWdh6c;$#G2iwX*-~V-?D?vlUxHcg!AZ+iII7jq%nWpwTuP8=TnKOk$fd zxwU08`J%~T(qXb`%RiwBU9nBCn>d+5&)1txbN~~ngV~EbGg2L9?Wj{>+s2&P%#l;Ep^l+7p*4;*0UC>*V<=SNT?!925KY14V+>exyD|Vb&=pJa zLgN~G-85=Qg=la^EVM@!$qn?UWJ#`#F&H`igoVnVNQ4ajNl-}HLTYC>Aw%@K{Y^6NuOENPLHPCu&@`o0~=$&@~;j@zU@c&n0c1*1nG)JI^70D_U02o&w} zBOS?Vh|7*htS^!vm}a2H4QP&$<&OLjH3U{h)<2G{JLfx(e0J`+?YulP(zCo1SaDu& zrY|8toST=O&3o3r*0LRK#;AjyN1MBw3ktf?6$^QRWXfYP)!2!s@ZbYekDYoIk|^xq z*e?;U>0YFO!`u-Pm z{r7+V^2v4=b`N{G(SK7WVeW7Zw(tK!-|iFHjuuNT=YtB`FDgZLBt4iAoiEjc8F)VzBvB zr4M16O7z9FQM9;)F-ms#5fYNivybYi5@ca>S#357m81|^9G(~uh&Z(!JAP-P%q%MGe(MNQlJX_E{SlMDls`eANttF1CYg-SG2 zN3%(JG?)mVsQvljVN+5WiRfx$02oP)VbU3@GBAvv=qZ>MCrEh>&R0joau#l3z(9_q8(M#8l84&HTF%1BVM#3h_ zQ;va1hK(}x%lE+pJygN6w21VEknmQ16C}aQl|@yo>V9AdpP z`5L|SO_QWFDjXaV%=~j0Hah13=ogPL5Qc?&V-shaSge>mF%wx8>4zn&j?K-y_V#v5 zz|M1WHs^Ba2uu!L%SOyS=167e3i8(+ON%)G%jb$#z%gegn2(^1V#k8h71X$Kn~IU{ z$`hkHBcr+%qautnD$&)3uVToF6%02%Nj}ke4ygEGvBKjHBN1T1CDI&*hSz<3x~@-6 z8FrEQnD_B1o;SSSH3e;9I90r`us~{r)y7Z4TWW}0iWKdiQ+qwAt09XX0zEV|mp5Qx>*79J06Z4GT~Pc#+*HeclV2Cq^`cLW5Ku}#!b zBP+m;T^t%3cK0>PP*N{nGckrR?1eIhjn)X5`2~6ThhHmWCs=!2dxmb632_k|4tX)p zxBx_l-5~m5k&L}&M@LQ;h^ja}b8!I%jMc_NQK>>&qn~sJgUugWKXd|d6eJEOdUfKA zii>r>{++4p5>Zp`eVfv^!w1~O%Fi#eE+DXGbBHL`N!|S|N7hi`oH9_2M-qhq41zk6(`un!C-UvM}^jCDSS>x zu}UXmV<9!3$CBoV_}k3-Gl2sqHWd}K{t0cfvkCr9HZLZ(Uf4J^ zRRAwaLL=dY!)q>3nRM_+zsjV;W>ZtboY{*UBx9R+qCvt#6x5432eZD~g@2SYDwcC7 zh~0sjZ*FeqwC7p2=T(ZmO5@y<$~wzDT}uwA)Io66H5L`s=@vDzykMt>Zjo-$9AB=h zY^=j7g_~qai{Ot`e$xHYNV0LpRW?exVr711r@dYpa?Fcl ztbmXfPZb-@8%`O*uHgfzaUBNH>%tp4-}OaQ(tlwGDJ-L-!AnD3`9+Kyv8mGpcD!4c|MQ{x!m z=}Ec-I~0u?Zf_fN^vtSpboBIe)EEnJ977B?z-|o2(X26z`%oT$j^3wJbg7g*dnSL4 zWJgU_8Ba9i<;&~_chV_bAS=)L*`2Bt`D=s7s0>=jliYA|0sL#iVi39m50_^T`(5@< z?6u&fiVFmXi&HCI>W6PbV8Bo!+j`T*37M6Cm_}Vt7VFY#?Q#k$IU2ZAvw&}O7ZeoWV)Jsr&UmByD{mA52NjW#*w6QMStK9CJzVAZPS!$HrtfAhrn=xv z$m~8YBU(WCkL+R^gv##yUy~IwvI^oYIT^*>yLPL{$?}JI`mn9ATR}#V@es`QmN~AD zyCcBgD*S&&yn_CBin1#F@f`ohf6HI;uYdpNFW;Uzd-m+9Q>V^cxpIaMPn|h)>deWX ze;}oC^3>Urw=m~8L#pEzqz5iSc>MGC|NHCz+P~zl-xdDhAjHLwY}@z))6H3E6t+kS z6+XXwf>g&^FOsnFh{rx}Q9_@JV1Fq^fGZjw`}`G;H__sWpH=wz61M0%BQEfF}4a54xmOe zc(9R?Se{sW0Qp!LN299~%MCJ045CR`WFVsw9b!^D%~sJ2sg=H})})r?M{PC+9YBDc z&!`ny*y(yEf7vr)R0_kA#^4D@CVEs7nJ`+Pt&%$`x(Oq&JQods}hLoBLEmnF>6kZT@dw zf6b`aY$Y6H-{_>x?AwGF{>NsU7&U5BfsXp7Nt-r*oF~}O1?Y@PG{icfNd*PHqMj=S29i>}&Z#(01e0co@1T?j(kpPBWo;6qbxw9200bXI>u@=%I<3fl+m|* z0IyBTE|8)q3m9Z@Ic6GmWrIRo+~=VNyrVm;^BN0zHgB+n;{uYgY#Fs+ILEbT7A_VK z5nyq`v!9XX4yDoIKJZ73H`wfmW`A?c8$U|dZ@3nFNG;!DOQL4!2Rm$vr4%KwSn3; zW`8B8SmnbZ%7YHIlzcVNIRNM2$p&nqJHri887q_sQm|?tnK0V1E_h%cm(@g{3T7OF zX1?C@@w4+sb93ir%rAf%g+J8Uy!&Ip;~r;BGrGB9<#7Rw9Lwy=!1Q9-yx_C>(Qf`m zK*hY9$)lt?xK0-Ru|R|2_%miB;7`iXCu@ukRMcT=WF{ru*}vx#9@KM~QblEF&d(!! zy0BZ?5a17~D=Nr-uOOqKsGx|{r<~%>d-Nv!iegN$i*BRpoDbc#U;Q{72XG<4D=DJ@ z0H%tp!cIO^?)%*eiegb9tMKplOaArmeee5xoF$z>D&yNLr|9+8&!j)jo;mx& zAJ6iP19Ic%TemRbpeN)8<{dcv$IIB26t+As`x)e2|nT7)Uv@jSrqCUjUq@k1k zX&k#YFxQyAbO}?AIBD>)FE=eNS2V>!euV7XS5kfHfY`NQyb+QSlA9QUAMlBmmt;t0 z4rF1=%fa~qh68nNAtA7fmQN2yvwajqjtqlZlVOANaEg|r&CR5$%ETZ#aX7=o9)l12 zVHBoBPiI%z+nd(bH&n4}19Odr=xBS%!ogFHOnU(oZD6`+Hoe$K>+G0Eb#&@8>ocS4 zb<*wA5lBhbv1h5F!|~UFfk*TT!{`7BlAs$g`rf24f^P8Q1VoNVk{3bVzN9i@9_V?; zz$U8a8}ukBQY2S=A9!QE((oYC*Vp?_#hW_<1BOP}ptrs|77m>I`U@868+tNmRN4QH zu#Pf+qhhvg>uuYyw%N8>k{0y6h1te225IU67tEtmU7w}|1`I)#;^KKx zuwrN=SR7Lf6n(ct|5}(T7S>TNbh|!(eY6kV-n_VBv~l+$y`m$aGI&k$j_w^qNM*w1+64VTdz*_W zj@_|yIQ1j30<<~m+FDzYQYpPjGQ-IQfmlp8AUsZ&k_)tjwOq9XP*>yFmH zlnV3c%~YOoVBB$Ub=6(ewMpIdsi~|z?j1RN#QiX|MC!&7VHAbp=ydON-{>UC(RuFN zAr#d69g-gB4xKxP#0p1YH~w;{6H6AWl)rTLS)KE<3iR`{>hrTYXO$8dm|}GfNfohM z3GC|wf+HV;4>oXg+|I|0BR>W9P+-8&2;0$-yt7hqRug#@(G&}Xu}&BAKeBg}9UIQe zJ?kS}p0bYC*q*QSQM_4DfW!*AVhcWgHeX)`=&RuK@-nH8qs`6D7-aAi1HCE*j2mV0 z=;MNJkz5fyvCE$e5K<{9c-+nRxc&QO*;#m8<+zNT+|I0kos9~|_shub;zPxqLBes| z^LD9-yru&DjeM6!{kj4pEq8IaLV?*6yP2~oBe@qjDk_JFhU|V>0jrWzIF6g8f&%xW z?x+8XoC32q@%LnR(bw*OvtRPByZ`pfsZ&?Zo;`Jiew;b=?b&b7p8De%k`!lt{`u4? zIyiaj?5&e$pf*nNWaBKIoW1okX$=-3XLqI@zx?C(-+uj{{FnUwVBx`oKmJ$<566j? z!j==gz1W_xc>~Jha?7g|y&#R^A`}QIjTSVw6_VaSOg6qZsYRGKuw#i2M?5x+V=57` zvGL)6a$xrorWppVs3xGJ;b?{p|3I(6euYO|(&Rd~Ftn$9pi8!4Bsjt}HKR01aM;G# zKGh^iq50uMnk@(%QE^emwzgc=X3S-=xey`7wsCA1-KmVtjf&eBwXZtu=|03{LrQW( za&wVTsScrMNk}ySVATc(s;e_HNLx%Bu+%_r8@4N$YoJHgplX_4%gap+42Gjk464o# zmzS55>r(;K7ZcSw8}I(JK7X|Zm^4TIen&Sc65V| zX|~QViH!47Wo+gRG`FSOKhi-=7S4{$Onq)v(brFp$woqjV=C$P_F$Xq>(>K#u0N3O zEu0Y*6N!=EQJglJ_Z49(i-yW}EG77tOI0;EkNQcFQ8m zc7}nm^Z=<`*HSkR!wO5xEh;&tQrXx@+Tw}ui9&WT_>126S<#)-U3uc8`w7@DA6PwB z5J;&cx$&g3$jGO%ZeFBSj8^EH&nE_NeE9+d={&Dad<2XXixt>LyNbV%)?mBnJdZNa zr-JjTDI{M>dMqq-jk0+IJC^GUfR1`_OogP!(1O-GEg?0uJUEvE=qTz_v_^k&6WA}e zi%WFH?czm`izGLGx;Uie<`Eo>w8{`^4KMm)gt_WZKe?fwO?Aj?NjOFMjFrdu#h2i3 zz%J@2ic+*)7d^Gdw7nefV&tJc9_l&G< z8T>T{69=ge$NK>S`xTHiMk2#5E9*j5K$iUdtY`A_StK~JxF?0Q#s$u#khr)nv_;L} z*p8h8vf~2%yikKbvSSyOb|`}u&nzz3J+wefb^=aO7s!qYWK&#F+}6)Am3}8uAK0+; zbBhXQAZU&)1qKom1W6}J z53tP-G3or=p%elB>gyDYbH96-b%4Q<(gzMl3Oz_}+y>D+B{|tD#Y(NiN-Z#-zHX~o zC0nW8zC99%Sw=^Ghp=;ui1v9dZ@aDLoX>XAoyyn|W_INFd{p}g=`k|0EY!t1AYdcw zJ*xLRS>j`NoRdr8-xW zDah{Mts=*kN=8)t8~u{%zyIUgE2nOqBB^oe=RdOMIQ8uh7;cnN`EfO7&7-xj=z|Wu0@Th|X$;ls}KYsc9-~akQ@-O+35 zfpkS7sf^fH^!loW)rB3Nb;7)Mval9J*PhUHjF zQaqin@q#5Hj9%ABSR{pMCPmoV0%abC#YvdvOjtyGn25vL&P3T>qIV)}KU|8ljncGD zON-Jp)x^QUd%} zHLlQC$brO?1LYxbi`M3rgaE~H;6O>aK~>0s@(gC2msFQ$AfZx%vmaDokSUfcraGqe*rw<_(ve&oBtITyA|tCGt#2O_Q?C=(VG_XucFEKUaWA_pD>4G4kZ9U0^uL-gjrYqFhlSW*z>=lzGHaaqR6Ddfw>3e`|{*2rAsR z{E<%apM8tH%2oneVo_N?OP3RD99}rs04laO%a}EwU zlh~MS=b63Eo5N-WZS_se{K_+XGPj9ciU5I$YWkcflH5RA1^R>J#!ftT&T_lX66#`3 zx3R9S$kLMAVk=?NsH~gA%B8Ln;Bz22>Kb|1!sxF`dJCtOMJ!QBYOE9)6_JprTzOL5 z=+lL=*do|7DviEW7FTwmG#1GeqxmoN$~A6AJ0P$r%sA*ehhyg#3>Uh(VAm*Km@*Xk z*e*I=n7Y1TSZp{oIy$s4>fvT+h)Ktgp~q;kVKAEM5r%!IwScq=QnK#^S`=Tw9v(yZ zi8eR4Auf5l==RfHFSj98SGqvb;;t8c4|#~**kHIeUV4qIzFcxeg}tK~aj=&I1#Z}^ zkhlmPAH&gNsHe8b$Bxl)sHcdogof6HhB}4@XghiaIM!%*Ixb?b5(?*r#v;}$o*IrM zGwuttD83xCvTCw;wW1MFRufPY5FnowaQ|9a00f5}e52Pc$lt$Eb0G^FV{njEhTVO7 zy?@Pa$H9S?dU>o??n}!Rc?;M^U4Z`@ltvuSK<9v=yuFHw9X>)Y{3~skF2eM;LOrFluy9>FK}RbUNL-hm;6>qpPKRNQjj3R3jBb z5BI;EzPjh$s{4+EL$uV>={<1);^OL6SVcFEkhb6<$JN6d?uXB%`hjQe?$_zZa8YnK zI;H6b9i^NDVbqVwUnvNyFj^E}q&2v_4a~2=POH8^xHxY2X) z;q2VpW8MrF*hlAXXY=Ouf^LR-nRoZ_t_5vu1z4OEn0FVTp{;;xRJxa86g^sS^z-8a z95pxNl#V~spD7T-kAkD<29p&A0s5egjKgsND(OzOTXJRM6Sn$(!N`>T8nuv<*qACj zY*4@*QI6Lca6l@ktE(vPR#%YOEhn=}MM0fDW-IK5xdQh~z^CYQIqrc9%)7$hl|`jH zm$4sL7k_W}u3bV%DB`dDoBWc0{r#6ySFlOB@&k(w(if*rou%(@PhB~C=8w0&J;Rn! zST;`meDc=Gv#^ZfjIE>i@x#wN-8ggR2Wi1_^2~Q9Ptx~4{`uQq|C|3M|N6`2!h_FS z3J(^Zz=Wga#EF9l#0ATU) z@nK2N<0F)whAV}UoQMt6jDNbO8U7UWj4)6*)@g!2p)Tiq|($OEVPp`MP*H3@+2x%244}JY0{Tup0 zdipUC6ORV;=%=sV0CKQEe4!uq1`6FCV6Z^~!}~#`-kUevMz8FB2M7+(IDGG5yzy4D zjw&m+wN<8cYI7^S=d5nH+`LpC8=d<0c>4lr z6`RQfe}K+k&=bPL#^yy62&9wXZ#X2tKnnHo0)f~(3^p8^awhYd%(k~DSy^m0A+Ex} zFVv~z0mvb&q8L zux$7M`8AKU3OB0=+vxSKu6J0Q82b3Ysll6-g#{ceEX?Bo30cx09(?G5;l_23;Nl?< z!*>gY9*`5HC*F~qAeDjcc~TZj7<(Wsi+u{I3^(R-cm!*yE)5M`R9&K<9v-YBg56v_ zNN+5;L0qW1g3IBGU~Dk5D==`dbyQV*9LO)Gjk=C&OBy3o$d6DG7`#&nt>GdS_&DfU zQ$s?d##3W4RKwHL6Lj;M`y?mO;^ygzk;hm-jRr_x8rY{W@v96durhg#nrB&AFlbyu zHl++!QTYpl^4G@fgg2DA91rE6iR{V+u8xJ&u()tx;vtI*d9G`7;V}nOMoCq$;^?XIyvp_zj<@7%T4ROn1EQLDYn&`g$MR2r$PI2vWCvE z-qYIJTDoV|nnXq^2V+m4MoQ)8suYVwm&)mTM_P}#qfVuCmFc2_#&PwYL>o=rado)+ z9f9cZI|rvIlCi6F>?Z-hIx*eY=uAba%DHnmIn=q)$LUzV6jmF39FOfhB!aJ?eI>Kb z0OG471r2U}DGc7QN=^<;NmlC<;zBJD&Wu3BQ z%+1aDy#mr9kB^G!M>kii(B;QG<>0n_`avl$Lb1- z!YHvHxANU`5~o{HG_T9@XKWRA5{N1gIlkMg%P30C>hw{ZIaf0KckRZ9b&+dT=PZVd ziiDg{6t!i$<L;7+Ii4!H+W7X=A5UH3LsA=8Zk@UUopJWel`FUS zr}ThTVD znS%!l4_?09LVDx!PD+LIDX}LmpNMU_Tv*t9;>22S%ZZlDu_u`J^$IfMd2ehBfE=;0 z&>1W|us~TuQEaS|(o+F84^N7x3-J<{BW%r9$u{2hL)cnUylt3mxb0dz&oaVEW<(?b zEQ(yLW&}XyQG7@*QBg?9YHFrwhJ{7NNw^JmipFt1B@ATIxVT(n&!P zP>!X_utD^BY*!3w>#Mj+h17=0FlyZD`H)`fqfN>oI_yoev!iRjdNoL7;OIP^+ec?- zXP-AUjW($_t`;-Rgjp$4e&vj6!VtPXq#_FV}r$_79XJVT1NO(75V(RtnrH*?2 zm;qQgg7g~(9z_P}59s^q-w4tJYa@naMo^4CQYgr&1R=o^gsW^Q4bppn(TA_zg9kw* zKJIu+i4=$pWnbk7%DzEucVOFi>s_H-(I)Lv-ZKD9*}q~ILyuYL491%)`?vXHym3sF zsz8KnZME6VqEH2oS${z@f9?N*E+yD(*}O<_sF>us*e2)<2lj42a=e%XZIsXF<{a{{ zP01lSG1s&cf5k+jot>gw@7CU)V`jO%IcG_SVw?d72VH6BdU}{T<|O@5S=os32BsO? z0&H$+36n;V#O0`*V+O~Q%1?BOG=?q=A3j_c+bF?1>U52CD*-YuGU7PO3W*P+E+ckz zRCX0t8ttTD(GAN^(JrI;;x5=pQK|yAC@T(d&3z1ueWvJq0fj0H#S+H+`t_+|6vcY5 z%LB?|f#(}gC|ZVEqaI6)5&g-dm}CaiMa3)wo%KVb9F0XzWe7EH&>yN=Brt}C;2nio z^rFX-m*mw@-N~l}tB#Z6829pWRaISdy{LL|+)LF5-1i*A*h3>AV627<>tWc)D&s$coJv-i#x>F+ zcJdlf8#OgZ$YO|b;X=*ej&Xy#VR@x;LEesZ$sj3_Yj)4%YbNaEd7N<{GUJ-v;GZz= z7__r+LAwf?=}BT>ys_75CltF^%s2X_f9f+~6!>zh2wKwYd;zspzh zXseU;P1YZKpf*}tk&wk6rL=Ullou=Fk;>MNJy(yMUcJh;jaB!nM^>+%6K;-sBsC5r zrQ&}02nto)S4C9iaBAwg)x+o951%`H=xaG_=b>}%5`Dw{Tpv?MH&XiOatd2Vd6wZv zGNUt4L}TemhqnX!_|PiZs-q7HS-g{y(vc4^$Bt8!Ln>&6?a0rEZKGqPL#-oU+Na!x z-uO6@Pa?%xEuVBohnh1t)1%G}#tl)bf-B7yy34Jr1qA+tb zlaMHAmQpB3Nn0ExEyAEtI$vI9@+dD{mJ5!4g>guZ%i}VNa=SpM+^-Hx!SU~9Wfc__ z#9dkrUPUR9D$A8*vVcqpXN4?Kn)}&lAdpscK~bbS6%|Fq6E{32Y^%^?H{UIh)Ktgk z@?8qYasQNKpMi>s9N#fzk*ko|&nZq>`ctwh{|3M0Z+~OQ29hY4ah&@0?5S@_cw9Nl z(&O7J-<}mx;}#Ug4_KU#%D8p%hg+wRW8q;2i4MBNV-2xTArbQ3H{X2so|y786U6fdHjAQ2rM>~RZP_u*5slH|C<=Ys z+rz!lAVMn`e%RX&*gs<3F%X2(*q9)_fpnDA>*))=Xry-#q=xSR0;rqb% zjvn{L1}Xb`qb>H%`-(ev-YQoF`O@RT&Q9gdogLVyveaSkf5-QYGR&gB$|O48AQjuT z1@UogOQcq?JefV#hN3nb>{MnMG3xLCmUV}JLIS-`(n~@`g@X+V4I2koMkf;>H73Cs zHG7dqii1=|PC`x-R0qPaIhzQjv_B!0v28ZDJvrH)lebf@LYH#B&NcA5Nm68+#~Zw1 zd16_~_6=Ru8KfLIAKS=A(I+CbQpbqV#zxVi(pZGj*p(vP%F3c5(WhcmWYk!PwiFmQ zj8;~RzEoBsuVS>)#hDe+-sV$TL~`RxSJzarQE{=4&-}b_kCNyhF(K?5hCV(}96ml% zQ}e|O&>*7=BsK(EbfHTceT<5HEXj}ST88LH8P#$_TqQWzBlw;0a|{iU`XFWD;jy$N zVb19!*lh?>!_7n0%}w?0#bDPV&^Ru-c?qepr0V4g;HcNji(X!LRfiT`UA?@7d(;b^ zDvQFtq3z0U4OdrBSUBL+a3p0>bC*3Ep<_Jy2-S8J`B=;~G&Ei^aU+1MW6NM4MR|&6 zO%1&lkOk5glt_T+Z7U0y;H2vX%r)+3l`**-+;U+H#o2{tq&x0UTo}A2|LmHb-Jsos zyr{0XxNzZ`*r%|I!^OhIWpKjfAzEa)NM+)oMZbkbzsoa`iIDKflB7hdHHk4pxps_XL1L zFgUPaL5oUCXD68FbdZ9QdQi=+I;~QY13Lo6S#qG-ZRBA=;{b+~rACKZGEX@2=`H%% z(J?ZD1&dUqlFzWworQ|C8ok+*{LvYh(UB40zfjw@P6C5-vChpRrP9Mzj?WA+#})IQ z?(QC;JGz%iZeX=iQ1BSbm7@jbyjwZSDAJ=ysF=eux(xV6L3065H_RbK7}LSg4p{&$ z>8?fh|yXj^s3(mG8{ZEf8(8XQ&sE)P~xEQb`0P5I{62B`3|Fwf9=2IUw=9H-9di*p|G&`#}k-xyy|Va+`@j* zUTMlvc;b0%Z13|EOamj4aYE^Y((~TJodk-~D;POMmkOzjaPEg?je!CcgjXWMpGSO% zkeFak!{d`QZNtN#+9rjE+s?on8Xj>eB7O#TjwH<)xJEB&u3@7B{#O)iqqdsHw#M{~ zOSAnzy2Cc?gK-E{M+mz{(_k8nGk%%|>bdcyDAE_Xi4Yx$xz+o29@8*#h%OZ&HbCS6 zkfS6Q!owh>x+JlB8s+p%87&FPKrST_ttxb_Hp8G~IJ%rJmY17UnN$sv)QC1o1n&#{ zuV@%M&V#{WVtT&T-elU;-t_!%gXu6fDT4WhDaZM2(+2zVrVXY#4fP;@aV{lV+N1Eq zBU;B^SUG?@C$W*KpDr{;42+`s_31H>(i_qNNrTunilnT!o^PbDcY|`IvhRcU-W5j z-QoL|2OQpC)yJ{-z?gHFO4~8ctboYz2D*b(2N327TW=FyqnsXdj<;KIa?EbMwP~8% zvT34+!&X8?LITL54owbC36uV4kcFQk;e~_4oI?UzLjf6`Y|7buLb_ryZ`&*ns)O-g zJmuJIH`~t1L0Q}O93~pu(tzXZV&%!UB@!u?T&x0}VX2!Z%G>I=ETyQ(@>AuLN(spE zX$8ZKx|L5Hk0tq0r@K;FR9RGKR9T4&aE%rjeS%vQ%Mzw-tcdvQ%KVDY7o#a7pRO-H zP#Z>!|Kcf!&y>--`FWprMux@3MusFZ80h6QWw`VBQfxRSW*@+vW7M%z-ZpB8E|npV zg?HEAU6*Rw-YpEl&#~}MYt(I&^hauTrcs9e*cy#% z^pwvUgTTnzfo#az4VKB@C*5Ip;lBI?HYpR5ar6Q*vJdU-WB{#0OAdE{MW{!QsMg4)$}b_S3~u3_V;( zT9C%L3AZR#C#`H76*CPaRqjE4T)nsFD&A!L7wL*pu4^OZfxfn@kRHg%9@%hDJ-qX{ z2jD25rXmOnoq_VUL!vRhzxo(93jJZ_*V&iShY1EcRNxxz zu-4j^`6D2sOe`nSv0SjsWKpDGk3w=_ z#BsFXF;~YP6~m6B99#i{6rhd*8Q$+J%8J=L-=!6I?bC%+-p z@!cQ3qsMXAfA$zCc#E|pAr}MH0{HO+_bdZv`hPpbNB5F zF-9H*A(iUf+}ummB_;a+yD*cVBmqbppXXX-MOQz7YVb5 zw*=w9%boYW@85f4$nl09d>{h}Si40uAtOF~Q9Ec9q8!vL267ur$NKiPKnVG#X z%WJ|6BPU0YH<pL;9L+P^HWPl0_MFYl?RLve%RI}xqB-nS8gn2r zDtXjVSH~hkm)9yJIx2M=cW~xx9cB9lcehp6A%7x;Wh(``tFF?Bl!tDyQBl##N>?S* z&3$;V0XxS$rXB(|%KMb5sW0f=*9wctat;(Avxx7Ckw zKxJWSVPU~%6y{K<4pt^^*GY3|(bp1e8y;H17^=0veJY@u?}TMVom=oltzfN-T1%=! zOK!m)^pLd1lBx&19H5W7N)Qe#RlM%HzI^HG>LqL)+N$HBcLj+<+so0_bzC*=2EqifE@7D-{wQ@--S` z^3TS~%CfSuE(Gj!(8I1VC^%mN!omG25AE*X$Bcs%hP<7H#RMdWfN|KlxLg}_aj}@N zn0WRK`oqbE)W$?VJ45N=#6}KjtjvDU5Ddt2#{=NNmryRF{^b=wVNNA+GyKnGNy)O~|q zj*!f_x(cG`Ip9S*Q&A1;M{l0PxgWzg0OB}zSi+h20WO+y$f{2aJ39URQv5ot&RL0e zdS0P$c^mv29eshx99mIR!(gM2+fo2Bmtdp3Pr*Rr_HEK3BO|vtv9d1uVUtO7^w72Z zkrBGIzP$Vq5!oJ6C+nPp9qAeA>7k$IoK1mKl*{Ph*`V(psV9Zm9P7=TKM}~$0%ni` zzCq_lADb6EZtmvLihz?oHWzk|?q>1lj&@@mv0qm4dr=vNT%`=(zZDeNuBo7+z%3wh zGAbZ!aos2+fC5KU<#=9jToKgm<4CNk%gFBkf5g3KOqF}u_Duxo2nbRl#aKW>6KqQm z5D*aQNbf}iqzXt!A_#&MK|vHzil7?}0yfx)SP(l_Y`86%nLT^&WaiF%xHFTv^W@2U z9_RnUJzw6uq8SwGM7C<@99338g68a#zCRo293AOBCCqpav-Um3wv z_>^p zIsbKA_SEoKKAa-(Ah_^H%6WG7w&83MIl6_{DqFUE%?5pJnc4#8AlC2?Yz+(?E}tK6 z4IB5U4@7fqC14hK4sBWZtlu>)YZs4-61^ zK*oV_M*&?eh`YI&bG{njPf37iG%kXN8VzwT8Y1X&95TU@-$cX-;Alud7+3`A+!An} z93fMgCA5f$2#*+&h0A1-vk}Pvj*kd2Pma)z*6xjzX@?I*%ZAvpA`LiXLj)a0;U8r~ zz0n^59Ffu7;%1a%6dGz2pPCvP8Xqd0Dwgr7(Uz8j#2nG^r-Tx7SeDTPJS*wFp*A+r zHf1)^p*A@viDex}Xy`#6eI%uahB9Xq32#bKQCU$svdznqDv@q(FjxYSM~QKbaZFi> zaama5wt@{R8!MVNKAckpfe?yRG{N+`(eCX? zQxgG*-N-}ORERhF{rcPXHXl^FFK+e}F{3*FYFv=U2(JYKVM%Nx* zJGy__p=WDFZS1nq{cL(&_6++IJb$P*IPxuWuJP zx4h8s`p&Dw8asKYQF!J3aUh1LVPThH_czQo@TRdYPo#AizQ_Kg5Q7cjP1#PU5yJTK zzMG%FAeWjbi8zWs&w35hhfWdjV@Gqc_D{m#;K|b zoZ%EEyeRUKDTV_pN3lk6IDPr~ z3>5ACr`3~GQYIv()#tQ_8?~zX#-1B9VjV96aDa8khyTR(5wBMZU(86f9a8G6+QW?LM0#@t(bwjo&KB@0Ej ziS^31ZC{BoUS>0VKqTsqz){wVZkgijjV+wHG0Ym#E&juFq1=DUpBF0gTgtbze*Mbf zV6Cl?jbh3HHAgwY2Qfz=-R6e_hyU{5fBWnI(ZA&1%I9t83jzxu-xvUNGz{2owuN4_ zpWMwt^l}k0-Kf1FH{OtPP}=Opt4!zL|j~40?#laA=XIXAH$Y`E;d<_4n{V`rP z6i@Gpk2lhmMMp|{(dc-ioK$wg#v5^^3hGo+<1G)SCgtSxS_&_0&fp+?u|<$_aEDKkhiX1FjuQzP2x z%La{Ws~JAf9&2n|!&{W1kL_aMJ(~KLj`cWcrR6G)O=;`a-2-ffP))zAjOgbKT zKaqr^51`y24tY?k_aVwB>W;U!-km#os@}m5B)w?x9aR9AKxn@oj5+S8J^)#;Mzo18 z-0^$o72jgJM_lkYPh;Ip9mN zlkzwSFK`5dEV0J@R|~>Xh(=d#oy(nDIak-8Rd;{u%DTFhEANl8X0)z(X8s*IjdttMlJ!7{@R#tN}Pc{y(&Bg2j9#^hWd4>eAozIpXU^(di zwacU!Fd+^VO)J6)E8PyzDdINJ*S7qC5RE=N@ND@vZjvRkAOhhMmF3F`E($Lodjn?J z<6sg{$j;+eE*M_FQn+&`=ZhL%xVrP(dtRVixpLt;dseQqhQkoTQr2<2?=rmZiPG5X zkb{nOFAcfMvQQxo9a}2pad=8S^_X!GYuuHDqe$W)uK42lMg4Bb_K*-aArj>h6+Rpz zws2z)%MLKfYfL@*oaoHynw#5Q9i1@PRoq^`#sb9+G38vef~`tepNuA~;!wP7e$3qU@nd$YxH_CZ#-5d9=I6}UoHR$=7wb44 zKZjXm5(^bNYjlO0bd42jIjq*OBi72Q@gx_jh+DB$W3fA3XShHT2SUAyr&monyL0px zk2o4R;hYX9_N;gJD8?|Ocs*CDtY43UdTdj8+`(R0k7I|U)SNgPKp8ZU8rxvV+sZy7VB6qngZ77V4*0T9L2lP{76T3LY`cB?7Ara0+a;L?7Am)h zINDuU$MI?0Y`nexlN7x%Ue5)wW@b_^{ZldZxCJMyXi_oDGaJWfgY!1b^7#6O&%_vr z1QS|Pvke<~^!o1!*k zlPF=+loO&yWp=(=W0(Qh_)qrm`q1zDBQ6V4A=1t1iDfXiHZ+i*- z268xtrQxfrUTl7|IpKuLiG+l>6Dn^`#GSw@<-`e`u~y^6iAe5^y{Xa=$yV8fS(O)W zAQ~myfOaDy0{)ap8Noz&1fhm3c_<=W`(pwKBtkUPW88riOSE?U&=3cle?)s*yv#^8 z`_U-X2ss?ly+&f1fuh*xa3dq`j!lK4G(5G}XqJ$|(&ixg+6Hsti;z2-lM|YrV*?FG zXioaUP#aF-2t5eVs7>ggO%m$p2MIUmVx>)y@Tc%Tg}d9*>1+_8U`dstnO+L}V)nJ$)1hoPWT!CCXdR`$H|PDylxJ!EdE@Sf60J-vL|%^G2mY6%x_6 zV$jh<7vB2#srrr3YY&@{`Gv8^2;id$u9b?!5r{b|-fnz12hk|u#=HKek^X++Pl0%} zu8trB649-5O+*;4`nS$)tpIB9o#mr-9B@AOYUTZvb@x}^e>BH(jsBHl$kEKz^o$_f z9h-$v^V}??eC2Wk2NA|IMi3B3?qMlt^w6PP=s?$w5;`1)m}CF5Wj~>g{@I~rhemhq zKeY45QN(hHe)_|r#_fP8tpDl9WzTrGavai81b!Vpd}w*$@_~oi$cj5M~?(N&h*q_4t6i=>kL(UgFW4AB;!WcrAPk}ff z?^ud}^Sfi}+trCU-0psXRRwvYJmdg>jH$a_>qD#CHO}HVrQWv#DPMhVI$=BdoWgYO zIyt#vb>c)Y;-ataG$e+s%)xUOEy zsWc!%YX+fUmmSbd&)mT)mgkUnaKQOYm6Rj;c!nSZIUH3SX-=0Ms$fyU!ljDmAm8_zWM?IuDIgN|Et;r7|xF8k2icJ@}gq}71?71%M3Lx?%XKe>GR ze9Oh`bG=Lbr~2{w`ueB!Zc};_x0@hTTbT`+A~6E91_Wp*(TAe40lDJ5IQ*O#C!8 zK!TjL5`B#RhknU_?mDt-*ZEz$SRr~Oo1kMGt2U0DA7)c5F$kJe&TspA{^d6R^K{3Q zV>ml|SY&+p^AaUHd+MwImMK2xs8QHehW!aRzHa%K+whiF{=?Ic!1>nJz;d2)K+zFs z+iF`5De3>$f62f8#n$!(L>q)1^8+seZ3hBb$g#Nr@f%WK+X)Og;%>e4EQ?IO zDU(HLM?}hGtPmx%h|rb|LCi4}-J2>6LgI%AK8!v__R3`8GBm0XR;0#bp~5vPIT&)J z_I?CxpphO?qik1+E+XI0Nb|~c?oJ6cuqis2Zh#90 zl}QE%2`?%+4$PnuHWg!o802r14OSW#Rh}WdsH};pl%yM&Yt$GIo=GY(K0|lxOwKrp z_%Db#_F~B47*mTD`n{ZFen!{Cu|(Igh7g0(&A;EI%6eTfx_AO->Fnqu#Nc%E?>A(ShKXzrEL)tMbiCNDqU8jkD2yrrVO~yJ0WhjKam2ZUQ$U7OPe6c! zRzL=aj%sC;X3Wr8X@-`AmP1vQRK})={*-fyGZ~lBAFG&woyj?e)8`g+q*$VycDVc) zU2eo2Ji?$0=BLlOo^v&SZ2m7dWPtHzWsUjeHES+AJFjtmE}XCsin39K(?>DiaGhL( zYz}9ulg^XwR;~y$XT&IWEuN+`=VB-|8XJX9baIumJA|XtmnMld=#EjxC3lYhTJOG| zzKYkc+T$@X?XiA>14cP&6n2$Mc*NXey}guc-omC7U#^Fxdw#xsKD$*g0O8D0##ZjX`}?9oj!;~9J`OQQ^jIQ2PbSCU2+?u(tSZJ3$o02w=dZ@?sBVL%soUL#}b!YOU}BSWgjf|D4*Kf&DzJsID-z& z+F7)5s~w7tdb74qW_e~$(MjL-6tYpQPB8GuBebYDBf!9nV|;vrGze+q$d1p@kLGP) zAqU=>XI3wV*@3B=oU)R&+9pluvsq5muyV{L78<7F_5aHLP1$vS7Ys_GA{-&eNa}ALpd4Vrn%6B!fBccV)3y_P7?uM%4%}R(vbhp z{E~nD%dVI7XV(+~#}Tns*>+@#U9mjonA!&Gil|cA=6?k0(3kMUe$Adjbu6I6U$m%f zgLcE;e@i)bDgR%H+WzBz{q1l6um2_g z{tx1ehStCVAO~D31#jkU<6acl77V;8XlUTJ*f`Ok5+`!MP7rQj^l_p<1-a&YJCUGr z;>8Im%v=mQ*a9mgq{%l`L_UW~ID)~%^_#OY60`*1>yHob=t#M3)wnJCf5e02GboF%^Ivn+N*&0hnHz40|rJxU(%bSnolUxAz0o#RlsIBV;tH6Cw^hpAWU(J|CjIqk=!&2}XNc#X`iW z54vGh5p|A4Frg=vZX->qs&7X)aTLpxhdxbyemD>k(vb?f#L(eq_mGK$jw;?_rILu% zN}ZkETpflQueSC#ZQWYIDIA=~u{E*oz8G`7+B(YJDi~_qUzzo2?tcHHx|MagkD5nE zxk)99#~g6Ya&g=JwOpE#^}97?Z5H7NjIt=Ir~9F-{f7@D`D+=hu*(jOu3aW9vWJ(g z6|&G}hevGIBQwASf@j^6Hu4G!7@RSoxzPXk4>KtX7yf8u_RZIUFK!!xd>?7-~!+@{2p%IFEx)iqRK~4a>x; zMtAp1s|13qa-aMYeHFu^QasVP(4<0l0uRppawp8V$6YGxzu!Fe*ss4Q5=XgJ#iNBS zu#k}E+pqIL5XXe5QSr?eLyi0n$T#vi-JHJul<&(!4RpqKl-_7-U7n=x&kwvJ218lN`6_d1Ss3ClRf5yK(I98WilW1`W<;i4Pb5NnS0 zO5W!@;*kvqtyE)$gEfW+{CRB3&l*1AqR*1 z4LMdKXs~EQ`9JbYn(iXn$bLzDapWaY#;zkGaf3MH$P}AoAs>}A9ogAG5r-}EHwZck0vmX-GO+nY!9atE{(3RM z0@9nC-wZSqym+(uO@b704)9Q+KM4gdkaQl0$gju~7;iK{tr3S%4$L_a%fZUg*|>=0 za3pmk$7v_4pm`on742j?`lua#Gd!6d5cUOk3LbB25A_m&gh%#Ug|Y-1sl7%+ zyh)iI3Kw1VL%q>bSL|$jbn5J^(Jb;si8tbV7p58qdvgYJc(h@8um~OX(Fb!ZZ4Rav z*%WbG>_N1}+9Vkyr5B}_*%%ZdYc$KL_OfTSk(bdB*W><2%2HBYqq09wKXWq<;>T zDp;!2%^{z|?^R-*UEM3YMDA{*uPm%lUcG`zHZgbW{Z~vD&~Ie*KUyiO=pQxbimv+R zN37_`eMFZ=vmVh??pn?_|K8S?3mc16t#aT#1{|Y@vp^ddbv$BCh7fB!I=mL^lePQl z5uy%MssK5jA)n*$PsAUGe_D2cD`b}+TFz^f!a|s2_a9i!nvMf(O8N1}LTpj!?58Wo z_lxMSLRNHKFI@iZdf}DAZ_B?OH(U;j3YWz4RO3S7m2W6lxr+D=7LKCX?E+%Y*`Ole zq|mU?^J<9a_N7<5x>3yb9vTharhFmhAP6D;2pL1ZC`x0u)8iPOkAjSxv6us4o<`xlagn$Es-#;($wK@)D?`cw+r@@gop!ejGB<`p=kn zU{2BY8TlM}PxI={+Mb#bWwdb?SlfoDbpEv7jI+*J(*b)*n|Ohx9#7j4#DPy-wtI-F z0PLkdsA<4^YQ6B8EU&DI|2O|21zT8au%r_!4oxZhLQ1!!4{Hb*@f}L|;MP>LR#QS! zg!S*s4QLI15cwKPA~RG2U8YFbP-b92NSD04^1sAr$f;@mCw|HQKEG?%5$sdwDf>LQ zV;g6EVYAd zV-pHg8VD|KCVX#LTvM-FA*FA;g^EMn0KLm&;FVMv0~QGSo05_&7R1L32GmT{`+&gDCQq}$=;-)~p;V`(CZ zMn#tS@xlwo>5Uh@Etm2~kpQ-QCozSfjrZ4ga%t@K3x>zv6IC1+%N6vhd^7A~{b+Z{ z^{ZbH!tvhodg0P;kcQz>Ng?{BOUgADdX5lJdK357Zx3M+2hvBkZ$~ezFu{Te>!ZHxYEM-Y2xA2$O+aH6o4I7;;Do(LYuRi>#B-jFx(3%n)%9Y&ev%g2N#I4YH*UrHbEg zr>hiuAmKPC*U@%p*EBjmc0M_|h6~*IX0^r&38Rys4p&AGD`)58$&=hu@9xa`U#?cDa&sok zSY;*oV%-~&`2|mkI7MSUlF!$B?7`xM$pZ;s>pj*_NF$DEcBo9-x9qXMgeA(Q?~Sp( z(vHxs3fyqeeyjd7gdLySZ+$YWclm7QvY;3>`#f&;)QpSk^Mn_cxC0618|uY$0~3w< zHuS=NzvUsDgGHla60!kW2Q^a;=M=4?SUe!=fv#vM$!*%CNgvQ^%BE@y&Ik?u%!W2o z32F!h1x@yODEt6P1%JvbC@h4K%k!9G6WkU20gq4qP2dUs@%X}bXb>E5xCA;vG`WdP z4w_M9TgkJE<3I3A{?}iQoQHq|M1jZi=Z~-ncH7HsM_!)ik;b-cmUaL-h$mk9e?^H3 zNJGd&w+v@PumQ8m76Ogoso}4<^fFuOP{E`FL15*rIGQgH^dBblXx$Px)mpx#wG|c> zNJe?YVcTj;tYJHBJB+$l^u zuUA|9U!k7fZ|h35#okY>L+lr5;}J$0EAQXu+SpOLH#gH$Go&2NxhqFUN1xH9tViD$ zD$OF`yqT9N*sCBe^w2WcVma+=*)v|PEVxoexjAJmYdZEXTlNzX$)O+7>$Yt9FTecq zE8YG~x1ay~^UuHi_VaJ6e_OF=@o#+l`L~~O`}3cF{q>h$fBfZ_ouVW50A!-e_Y-tr z=s{QDS}8pKZTayFD2NpuDiT;2UO>VZLCDps$6-bhRc`EveUEk(0Vmyt+Y6T(jv0=@ zq|zNinDM@A4542ld1L!_+_^?}Oe$}?+XY1jQa8HPx1*k3oeu881WRn;CiW<&SjwRe zEeBR9ed?#WPQBJSwZrM|4o)!F;Yo+vYkCU94&@rOpfIE8gk5v<(&1i}*Ld{O(bCb< z3gh~e9<81*LW?kTwq2|0Apj}OVBbnW0IyPxWkAS5FJ9JixLl>ESjr_Tbbxw##DC4e zh*El4QId;3r~B#C^u+O_$IdA}fBt;N{P}4I(1r*bJ;tR`%9!DvI2w{;w~ zu-KQvvF0d^g;wL}5@heg2l`GQkEP&dh|9f!4E6S+vr_L=h){0 zS;r^nMVGYiJL}?d^lUq)eYt!_s%X39N^yA#8OAtoQrZYJKHVBOtFPzs6c}X9;77^J zLwjrou;1$lW>7ExL&sL3hOO;cG2 zCE!2Rpbum5o}1*AU{6(6V-Ke(x2=mxc?G0w zvpGagO)N(+si2<=rv(HUn~;*BrfjV#r=YBast_ej6si2%f62e@+BNlZ*RCT+j!f;^ zwd-Z}dH7&4-#9`~QzD9kx#RpWqBw?Oik%_|@%KMZ*s%?@ZpbtLx-Hv(7&l4H0nKRn zmMMP#2m~Fdb@S(e2e8AR@PbHVehPvOw#M=%g}9?!0uX|Xzy>kkm@hB44YVC5co=9M z7-p~QfFKNEfNf|P0BFq9W5Ga!Exp#zO0U0Q#pnR>hHaefivq%p6K_roY#wMBXlRhs z9MFssg~Sy!Y;I6VxQV4n96IXpgluzqicooRQziKX7$Xk0*kqK%hI78?3zm8y2~37p zU{FbzmC3?~!eK$V86PeSClVPNLi&cRw>MfA5k53axG@xtCY8u&nRa-5FTIPaR8nQK z)ZSEhVn61@8-<33qCiE;12YOG3dxC&wv4t6jkZj(v`j6EPPMeL%*g?H*yQ9C*$gJp z6Ou-&ZEOY)78NDgq?e_q^J3*72Y=ZBK?c#?W)MmkQk`BF1BXgAg3j5qQc27a6BA=x zQk@R&s62DVxZ3zkOo?%IW=v+LF^@7F(H~nvxM5OjoN01~ZZ(cV%|Q=mGPyy8=pr*l zHwJNEHylOkR~oxjh&^I-WA|dELab4nhBQ&fPDF2Dp25Li-WYMzqPqC- zbav|FAS%k6(>H>H^?W*`;7*BpfF_j(!9GMFZ|@)l>;dtKw~x2t}GIRNvtW4?ISEHWGPk9Pv@z`1T#Ai$27JW3K7p!*_Ngew;7*&aR1r zzxp>~zJcyGyNY)(sU)&Pr7m%+A1DJDLZ?I={dINpc)u<;acl0@+)<1+9@QZQ3{Kel z{r!)!5dHP+el7~za&v{6V=XkJ&1*-qnnwvMaz|m0&ALAd^2j>GF{6j!TzN!Dfmrjy zm}0CwG`j520mw#|{e-cH)AmUiP z_#!>xb-cEC(dyOouEmQM5ru$1end3K&Tl&}EI*Fh6%IBxgh}>-Vd0evg-GDQgW;8( zgdkVx3FKjTrTePk`$EI+E1YO3J43B(W>I$ zFR(?xOce|&RWn)vUZq~>S?NK%XqA>$X=wnu+$&q{2;Z zicXW)rFckJ9B9E6XB9?l(8M1zX?AI#5PHouGrjz2ha5(mfwfgEcdU*>dC zZc}-D($)3y8fRx$0U#$E89dl9dy*SdCV8~M4i)EOE9XY`tPqA+O-wXexfeT6ax*eFjJDyi4R!S!@}T9&dzvTQD;R{(w_qH=5#m3{eQ#R*fn}C#gy3m`GB>Dk zRF)<_lGTv8jll!IL5cN>2;#y!HfgFMvWq^&H)$#wp@0*Z+?=Shku}y(m^gNgYE0rVXU!GssrAWnX zkZnj8IQnZF1{~+nrh?M8ZIEwFVbFmVb=bC`wcOU0j>};-d12d%Jg?zEB94OA*46=felbuk<$Dog zuu9anAkg;rr3xJ}eb_c|5(hFkHs9oU^SC!+qrw5_1){6|MFQGX;*eh60J93#DG3mW zCLr016TcGTUck8$f#9#?gk+UBL>>{5Dk?*fs7^uTXhgWSwk$ywNnnv25fL7q961{q ziF_}S$PvN4Z9_8A;D#Hqgm!o|CL8RDO_d>#0}GaLqnue$vl4HV8qTdLNZ|;zv?;Po z4YeF3^cduEhh?Zuw2ftGl8q(O%x#iFt9xxiZPJSn{FQz%9V?XdqJsto=?9C-MAT>z zm?OrZtf)G@vZ%7UtgFjNLH>pb&T9!hYS|F0n`u&`t6LMJPuQWatA8WY@riy7Qoe4~q{XJi z>et@TchpVO*N>tz{Z3?%dUxvU=?Civdv``f`NVd1#y)rw94zH~={?{KjwpCxqx2ST zQF^L(h(36@ft3pTRVq|b-u6LNq;shH-2r#-B1Hs#HK~43t=RZsBMU{FP~g^t#x^8= zRW$j%+gM>YqKe?Jk){#5IXdZ2WWgwsH|$>7^@BN@`V;&8h%z45CDtXrN=&rt7Y%Nr zxUFt$UBBp05lfVnE9*vAvImw|Dp;aC;&}7^M`EMWyfQ0yp*7_ZFISp#*FIaDE6q9R zVE?kU`w1v8;#fAi|1c2-Qoi=@KX7ROA%c%ze);tmOfi1@Z4qGufC8Ygcm;t4C82Ip5K1{^_O3MIlgn}H$$Q2SbpU?8tT!eQh40( z>P~1z={1;Dpw-|G6~Yk1tNf1Zo`r^8s95Rhy58;CZTMwuX~* zCOUWf(56D*@mi+`W)*~*_n||DFhnN|nWNXLysqg~p<<<{$_paV*F9QJVOXJ51!QP> zVU`iV%n?AK0m|^oaLBlvkwKUt5;+u6+*Ybs8ld?5QiYh}SQU7qM{G|(AJA}|Lvide zVOViEW-bjlE}!GX(R1dH&7Wh=fpy9`6u1$Fh*J9JC!HT7R@4>2U#=$!L>h%v#hK35 z6mvyAGl$hl0t;t%SLb5a;>pI7yiu`QWmU`>9L{j4xGz+-;iz~5VdfKJrE+Qgs`V3> zuuSooUSG^w4(LUt0mnUFuH^4oKfV5v{j|_(w4g(!Wf;Wu!H$~YR^AmJFl)&2>wqV;Cu zw{F3n@_F2>4Yjasx9E{)88F3e7~eqWZT0o_ZF%+J39}7toGU6aI3U~LOb(GXN~n^D z8OR1y#hTS`khThHno{kV5`8vvsHGa#5o)3b-IO!7mTx;R4z^AacR)swaL^}!CF!vAVHLWxPrBuse%UQ zl&i^YQu^*X`S<;jf7|tP*ZC<7IL;GtOuc-0NVLQyVDf6~30ta4*kkN*L1?mXg zT+kpi947`gH^2w`0!6U{n+rJGJmCfVVxbrvXkhzELc*Jyz>5ZzxP}I?KZ)ZQQkCS$ zn<@!$kqNU1;J7K|qmi@8l7&{L9j-mA9iAYIfXpLOyLWar;$tM|n#ZF)1z{Z8vX8P} z8IM1-L>Rf65t?GbMDT*3r?JjvY%7J9;L@#L+}Z zH8N}XxhAcaB^@;;X>@_RRMI$xRDW+;jDDtLTFn#P+9;%eK|Y%HL?nLUypxEd^M<~6 zY*eRrnqKgeCqA{3mIHxb!E9Km^}gZVc>@xTPVQ6@H#lbXxIe}Ff$9UqoPY4~^Hy!* z{0&u~CX~~QV)~|v3ZWXE!!|{=Le;MV5u>VTsGmcp%EQEW8=L%^n%-`l8)3&QqRZcj z+%Fhb{Gb+XvIA)JCvL5H^{Rg^k*i_rwkB3^3dh#|xvj76`@Lf0%GQd}1?uRBmSd&V z5}OMj?4#!9`zsMP`e-fj2GYJnF?}w_nXk=le#UifqiYZE9~E6Hhu1zkEY-s9Kb*V& z(JzO7{qwIs|NQgnMLeb8+X?{1MZg0{gTQ0OiWQ45t`-!5%K|7a5{}?Kc=w7Gzf;HJ z71F_Kkjvsl7k~Tt&%Z8TegTmjSB@8Q5E#ovInLbh_=Rs==T><2!tvu`e{#X_3!bkM zdR+Is-u2$_OE*R#o-nHvio`ESH$0cV$3$a$cL<`*$KJo+{zYgxLY8{6wqxni?O&FT zZ4W^L2a(3usRinAQxEA98V*8_Q*KBbMN#Z2b=0W1?dXH9v`-x_l^uQ8glhCP*STE_ z+tGuFQC_0FhEz0+c%#qBt7<0TwNpS(0E%I?ysnAFFBn$5v}Uw0+Hi96A^_=`nF*N5 z;9T=kt&AQHO{hYoCG|ZjNA3Jj`EJD9do_xIKF&k7MA3ZtgD&`xFNH?FvM8lOq z!>Vy|(tWzvN=oHu>l{#oLx?k3H@L9eap6R(V{y z$G%vebzHgxLu|{Q>3f)POswBCaY^h?_SjG3*>?}|Mt)1ny>-+1i1@-lqvf8Y-{?TP z`Mq^1VyzO?V$spj;rl1nas=h0KBa>XZ(DQ_gn&Hup}k(Z!7(c|9E2VzK}Z|Lq$6d? zK5S2R@4GF0v8*ILDpaGlKeYo*T=pFucWDS_~24Y5^ijdK#3k|Uw>Uk0(G#o5pVAX`O zvOH&KSt}`Up*v@atBH?bYoU%1y`-D)32%)CG0BRGTy14*sh$MYrd(Zu@C;KqO?kwG zLedG9#3s2-o9HuMSy{={T3H@du*&Qh*(5sUmDQ955)fP{$jfPJSj%(1#=rNM{M)Xn zsUy44l|ra- z#VQ=Nu|;&MAdLe{6}RBp&;_=rAc>=uohs$6w)3{-t>u_+040W7q36J3 zYis$7`GVF~)@i)pzLa^!j)DT)`2qnTfuN0f+g9vV3gB6h3RO;^FLv|%z=;zLQU(}* zHEb4f=O|S6OFMLY(UQu_eMonRFacbBIDSp5*eW~tCA2A9*!suZ58d_ z2<-&zn>UlSdxwZiJ|;&bYez&5aq?)d_D6_BhvJ8{KgJ88s4P`GI{c#yj1rk@B)yaQ9+3t^rA(utrQ_`G}JPE(8jXJGRYFo z*n{a@q;jx`=pwYJ40#*~_zJCro+ByUASS)CENL*kveKaFOgg(!VvGk7W1gN~X*|es z(Q0(m*JKWsAo%Nyqp@)k^mrpGHtj}jRBSBkG;VO# zS1m-MsB^3BWW+9UWTV~2#Epp$u}^vEhr+fd5jk36S5Z-C zS5a4&_-bzC;fP&-VjU-sR@lu6?5Nv1R|m0ZUE=+$m7}?H{r!X#iCHV}vrh#s*!vvx z^^A~XRLTJ3FP?vhUf8wwbJsTWQso(Np(Lx>rDwu~(*zwpxJw?%>qR)8IdD3}#i z@Hrl_FS)oFQw+Wa6uBto8iWxHAOtD&%Ib>%5q#rfo^bFxuz;a=@c}^Px1WFg^%tRp~+=+P4?9B%AW2|<+k7zP@z``p~F zeJ@r~SJyeEqjOhGIQn$l`gC?&Ly6mKw76Yk#Gse@bevv`4izj`U|Auw@Cwtqrd5R$ z4lO63L{Avv%X^%(dYno#dS;v)ki=2S$)i0;JrCf(uNj_flp=29+_4#+UZ6wvGN*kJ zT|nJ&T5;wW)E!bR*l9&`#dECMaNy7lx*xlY%u(n?@oer2v&uPh=f~!2%wbV+bzZ{( zqbKq7m>!&+*I2m{YpiL68Kn`Akcv(_ySiGrqmP~k9@7FloL5bAl(}=GyZfrf;%Ry; z241Y1TqPB%xKH4Dy4YhCTH03a5t0ola@2#5FzuK?=IHc#`+L6k7%}WU>|5;fr=<=R zk9;By4(MpXd5iF=z#QA*3q1!O2{$^XeSL%01+jhu@{QY!6uWm@+~!dS0?wBlO}R~v zj29L>%76h`4hcx$Gn8K;|f2`E>S^3;eP~6}Md4KVzTr$;G9< z-Hi7px6E##Q{@)taX`NTDaW|k_;{Pycw1ZE)6Z?taOAad^5{5Z8XM5(X7;&Fh)Fll zyYoJ8fKw%}9>Wb$sDcVv_GJ8^v5)N7m(xpdE{`=J$vf>kt4iPIg-7Nmn$4HDuOwV z3}d2!O!Kc(+~78haC85uVR{^fP37wrjy?BBZ3?eXzA|!@Z}Be|`cmYXBlJt;Z(zPL zJYPO9rG6ougEL5n%LzR2ItxjM1BZbS4XpzMV2pVzO9l!c-)O~@17eLpgp7hZ3SQt6 zony)|k7RR5MK{y6h6Z{ZfO_K%vd$%~=o_iQ4W(@=2>-%1g&$AA8;kr6%rX*ghKp3N zh-3l}cC65)@CcP~!Vmfm8PUbf2wAwSH#ss=_EB3lD}y_B2$5gOm~za9BmApZdkFiK zRC?w*m2hn%qmRAZ;0D@=m-R*)4TXp1psC&{KGjIN&BC!V%g7OLnR76Q^EcwLQ^`SP zJyQlMW0T4T*|SoVW0R8;YEx!oK$psj49dzd*WgtOVMnz=OeI|zb?%h43RC`;?L-6uQBmLSRvgSkX!-MoTh{W6qd3W}YcAF{!RK$#gW4ZnapgAm|)% zqI=UEg-sT74R*!qI*QKNnkSC6LM|Fh_ZrMMYHp-;>gsnAex!*Z#|_9lZp3zWf-#vi7fjEZ90N>r!blLrroJAyg$OV0-`S&TLw(8&iBt=!>Q(L3J36{>e0z=(3^gX$fh zh2)J7qQQ+*N9k~c*#l*61RGF_qKY2tm3Mw~8#juw*pZQ@jed4^O|KID9@_aO+9fuL z9ybCGKVpx>#I3{_@A|jG9SiwJ9n_+)5)uA|s8O`Jy}~91{uBa=xknsk4%%o&#Aq{@ zyEPMsaM#M(l`y3cY&>G*XkL3CDWkcf%#AqY5TN6*0F9qlUtA;&EEWq!Si$-WNvW}j z31rb?fg$wPix_OIhI|9xv6>|vymsNm%8EsTE`T5xS1@qkeDz{hdO+5(_#&`|p0Q*3 zwwz$&JrTum0*Rg1ukbbnPFa-IW4>`ktXB#>zlczrFT8CQyIg+Lw)J?yXqmQ#6$y4u&~Ioj)R!vl=_aoK4?f^ zi`a8F2ukm&tM}=k!L1L`=J2WXvH8_Ycw?C|03_GEm^;G4&`w_!;1v*7)#F9r;pK(7 z29rgV6Vks*y)ux)F+-5ilTljAE0xkxtr;v=2un&q9WcY5lM=oZXD+jZ<2kq2Up}XJ z`q(k3H)a%%E#!bH{tw=X1~=jjbBALN%pRu~V!zDIMHT&;bC;ckBgNH~?%dthIC;|9 zmE9{RVN-#E)XLdv!nGK)4&0q5i=CZMTDj9@x+-( zaG$ueYELm?$of4TEeg zmUVkD-pJ4QU62-Uh!bameoPt*~sIW#mAIY&%m3(!pFfRuqodeLTwmcZjms z_I93qj6c2AUhk53i|(Swjj*Gg9`f?cK68&s+tWPaj}4+NR){smMWA^-!bUd;@y3SF zPx(FLbUBYck?Dh5!&-bI%PVNgYbp~9DB;5%8Q`Y)T-KClyD0Lv=<{988Uf-cX5}|+ zB7lG%lRm5!tTnjJRSkX+c?Av0Wr~f4hLWg_7Y(qIc~x1HZ7F!=2VDMv{(e&q{!+uf zMNNs5B`|{cH~x};-zD9k;@Guq*OBuq58d^0n^>kGf@6wA!jEmMg88un+0ivL$|M|SyFq@1@xGKyxE;Z`~b+%gZMkhUuH zIMvGX(LhWq%FD~?t92erlEB~l*yyRe^~Eru#=I^2UTq-|eK7zbM**OufM**8n1K|~ z!N9z295yPpn;YWjwSvt9Cthrh+kBz{qm2`B2`893;@-SL04Y7G#6=*|`~=GB8-#H5 z1rbHWn@ApcgsZ6BB;<&MUj?&{gap*8pt%jk*zct(+R2d-HxsnABW5w;pci{1WRbm` zZ$2c8)b5RslLBqy@>vb&MD%&uR+K*Qd4IK zEm0V2nVQpUnQj@KV_8;YgOZhlMT4M@oa!pbfSyuL1P4rI+hsMl*Bk@W}2MY zn}h*Jj4nnUd(Z4O!PcatwibP{h~{v_7010#2s&!^a%1ch-84cH)V1w(bkx_4(T&wj z)7NGCNK30B;)vx2Sz(TiLYs;{;RM@Ke4fCe;;p9_9P1r?;{oO!QQi;qf;*7}hFZ4| zXs-7rxZtcWAMXmE;HW!yR3CW1r7PYn5cT${5C#>W4qFp6xSr(DT&%Z%qA+1LkA6BpkRMKS-9}pf~l;jxV&L=B?4xolb zyg{LFf}Dea1MsneS0_UELD+Fo=sOlKW&sE$C@aLGg+-_I&AR(B3x#mB5S6j-K^?>rhF1%(c3;Ih#Sn?*U0p;R-S3wg3fHTsOu;~-t9y(v z1h!c&SQ*>Cy=%;KDN#j;+bQ)fqK#7_$QuQCY**j$g^jWyZf>XCx^@s}^zqn(-g%dW z9d7JUae58ehLh9X*E%~;-sa?Xmw2PcO~)$?+ZC_AFl<%O7>o8cC^y1*lj2oXMa1DW z181zbODh(qDu*8Ag3Yj~lpY)cp7&%ZYGo*vDrP8Fm1Zbrlq!}wV7igv0Jv}vuGn)9 zRm2-a7ng;N<+7r=q66a0ArYOKL0_!HCS+!c+n=9h^m%#md-B1WIP zx=L0Rbkv`Ogkus-Dg+(wFv0T8xzTm9(aH)fDib)LaCdJ6S6o`xyL1r09)@d4es#Bp9YE$X6!*-iv5I#eZDVZ zM)$P%P7`&k^FaF7Jzrm6_$=j?vy1d2;o?Ao4_N;Vjs7`(s|I)-A9QxmZWfDy%1_FIeXNFV@1#226ya6G{-)a zx3xnVx@7z;9V~I7!&_%R@m8h1-OS}_eSLj9?@`Rm@^0NSYtO6yBo(J@Fyl}S9%Ix$ zMH1;#I8^8|B1NC(Vef+2^YOMkG^eyd_W|(WaRPoneSAtw+6V=-vTDM9vI)i$1yrl4;Z164n#!!$v?gp&oQlQ7ww)5FjN4{?3 zkWp?@5lwDij~t;#DTjlND*n}mq?)%PU$h+NSOSe!)^L1<6SkEXEw+KsZp_=3zYxqZY+GJ1Trl4nIM6C` zzQ7(01Ge)81r4Gsmhq$j(+*Z}(D4agpbQKUT?`BioH#K6;@DiUnH?(Fr>JZ`@dh&u zj5Q(>8qnAlk&p~kM;uWGrWg@%D#^*=Z&Z?1!ow4iS;K)omGI=qWUvO&M5OktcI2!~ zh7eJti}ntEl*z&)M45^nN)ZO&=^VC@W#fD6dyy&KM6?I>uCUSu8R-jH^qMOe&3qg2U06)uWkb zxTHR_#3YlKD~=|@p|UsAgwr;JK~|Rq9FFva(9ujsUEP?p8eN2o#_n~jg%&LY5Gs}(w;1I&j&jON7Z_JclxkoR9t$XR~r?Dkp`kTyraDJ?jZV0 zPgM^_*eGvRY*X$uaY_9hNIKrKZq$bzDk5$418-L(MF#{NXmV?6@`FpZNvv2#{N~_S z@oO4k4aeLFEGvnkyDgFa%-JQrLJEi7)+W@$*3A)V^oueTJYf0(tIB;QjQe%>SJq`c zl0>BUS2o{g^=NZ0D5E*I|Gp@uZ(fTmj!{k+#WIBxzhG+p^Us(_V8*aQJgkt08w>}F z2|++0t1sf1-^Q{~Nyo8hVd6ocvRJ&jTDnB2*cp};ELymz?Ie;oM1>o~qt1=ae{y#(cIVe{X;owKDwM+(TM5E&CfIPF zkVK^JtM<6FdbIe``c)pq#l;@(mxwr4@lfNE*r%{5mS6+H<{m=8;k$=;qXlb}dwW{! z?S1ptc@UJ?`>xB+Z{e{<%XB^+`=$uYNMRjEONYfeG`DqFwB&aL{V52$751T6SRA!D znqq-+Se|Yyu?X6SX!8z;NFg8vfndcVgW0&&?lK92M+ z7*)n^5o;Xfe%N*($7lFe+HcVXGnZSR+g#e(>+{;AqP9Fv{SwT9bkXrPLW#V3X_T>H ze8YI&hR7~qHszUUhD9dSTswu$mK|jINnp@zJNA&~$bR~^VEVZy!P%_=LP<5)Tp@1qj5P+H* zm%eUNlgz2|YRaaYG^|AzDl{IZ3jfAmk|<+pVZL!Bdlv#kIrHlX7txC}4j@PNk(X21 zUxlgz8jqL$NBkk;I6|1QZQEDT6Z>+@6w*1^oHE5ZqhJs=sBq?2HZe!*@K>XG%mm4t2 zfL*1P=mP2uMiOW|UWooSTjB}Z69q`yDA+vkV)Kg^#2N#{8Jllze!~$QaSaKZ;~*zh z+59FB%#rX0NuwMBMh6KEaS;s($q8@5Rc^lFs(O_OmB^c-OE$bgMHZ*6@{ulZQ!LOT zIb25136TU9@!{bhjox_eUNpsO%Z7$#d*eT9-_$nJ)(%hAp6!)kvhmS~pu4@XjZ}K!!k6q49szGu*@cD z5LNXCHbn*plMIS%h&ReY4QvQD25rhX;GFX}(BD>R5K0hYT%BHqJo6Z+Im&8`jZ4xi zlVYj|S=)gTM^cipBl_FW*Jff|QdS z7?~TL^dKV6Z$LPz=Mxq6B)IbfYdiEF#Oi%`>+M6R@j(^hP(++V%W>ze>e~)%`t2EY-Ge}s_;{-Xc}pn^V|4t&aMJE91k}VR6s47 z=vTo-v2*>2uPT5XoNw;ePtQ$>b#zz<20`HHE1(A3VqewGZAH@P*4(U>uO8h;eEB0- zQdZv2deqE4v5!_hdUStnb8hp>EPCk~?^Uu!bJu2NiR7I?Sbb3_Na<}CSMY-6 zqS&txoJc<#Dv;IU$FT4N!w&wNFzG0~Ld>!A`Zw%TSgk<-a{ajH)#HR0-#iTqzZf3J zE`@-j(6Af4;W@@0lrP&o3&$2pW4k?3rqYGXFV7H9s71%1<~Y^06o7GeEaZ#&7uMr4E;Sj?FMdC}ub)KE5p4W3ggUlFVxwH9m!V5x+%j{BtL6$?mh&P-Yt>}W)W1@^kOgT7%!xk1fyh=RqV)4nbDIu;_p zfFHgV9Vz)l8Tly)0qa0tTgp*_jNQ9CI`-YRK+>p1O3;$q9Tq{nFgZ(@5p?!8n^E@N zUUKyIlAwM2cJrQvb4Hi!K6;A>8F=^Zv-Az)P=1`fg~13k9OLa^j$5DGT|Rw=O9j@F zTTeOdD=%-{%%u(4qPL!oFOfCfs5j))tN|Gqcl@9v)+;~<{%P_WlID{K9e9(e5}HCZ zVOae){gQu$dIQXH9)?&fQxN`zD_E&);~b77zgNa)a~#K!ZT>HZx1A^4;O@3!-5898)~#XhofyKYLbM2{Zh+@FXKp1a1&zV3oq6j^S3;as={V!?qPu z4qPgqA1)t22uJIS@>ZT;NN7<2yKI4&atsJ(Y{3gwi?Sh>4>t#%;K&W4j2FZe#1?S` z7!9z*-fZAfl^2`iRN~@Js1S>s;1+s%Y>2>9TpWYNn+Wu%By*EnM0f)`Q<5WKjiu-C zH_0l!$ugDjS%M5%cyf61&BzF@Pzj%9*=RECvdKK);6X<`rW%n$GVR%DS){hnY&ZrS zGTD$+Q9mogas?{VoKU0Gc%$BEE>-C@$}!4G&9O|f$tkjoX3U6=F2Yk;JhHxQpx(%d zE-JD?97kDbk;oq{3g!NkvcXEIHw?-KLl0IPVBC>ZCOYfOlAz=;t~4$wV<&74x5gS* z8&_8*ouLa!HPCP*WmaRnVO+_9=8h&wC{uxTr3A$)nIamG1!y2Zvoy`itKo5o>Z`mwsPHG(f{V>$n;^FwfOr#{9VPttriOtkZf zo+ze|eV`Zndu42tUT5r+;3#js55cHc`S2hLr7GTcq>B0vKExi}rQ*#)jd#IZs`8H8 zRebmvI#SgU?ov^GsOrZym5pyz(V_D2A&WZvDjp*GYh>fYCQkkGnhPOuO7tVf z@T+@;nFihc-fb1d^yp6cy)w3LYhBhWbgMiXMHT&{Tp&gNeFh6Ue$>xy*{qdW^lBEJ zk2W`Rw&+@3p8U3Wu@HJPX7Go?U0t3_L%Kaf#yrJ(#q&My zQ@UZ7m8KkBTvSioaVo@3{qCvlrwBI?2c|B0Wj%MO-$l+StSWA9T)A@X6l*!KK7o1z z%Mvb5ankAO2}2$j*TW+5i%xpN+~|d{FfV#3EP&n`R^=3i7L`9bLA{~nHB*)0rB&*b z;nd@W+^+!4Iz%zO1G?M3SH_lV5qF#`CD3?|>bA?rP79|BYdTIVJ{E3S2llK$)ltQz zD#Rw{4yPT=FJr&~uZn~DAD$C$xSF3n&52+CV@{NTRSHg=pSz0aFCJ{Hadm#|3eo5q z=gG#C&Q{K@#mh}5 zQ*p;e#hr&56B9hy;2O6*gdpIKON<#46TW!b!x`uH_i!=+^#<|89^p*!_4O6~Df!dB zod1<_ucHMq=PAA&Exr~ObeGI2K^8&665ApM9lI@vJ5qKBS=>H~fR5WKtm=>=H});L z9V9wqmz;Gu3JdJfqg<+j9n0C>XP11U>r2KF!a=XlAto8DV!`!o*IJw{P}%m$LUh8mh~gBfGPW1G1Klcnz;QV9A(ASX zap1PqQJw$68 zSJW>=bYsFnk2qN9?zR_Bjv6*_)a*+cl=6s(HmpsKwMHBEE7NQio!&3s$04&g=<$NR zJq)95shm-PYlDvt`}gRrjq%F6o*vT_C{dONp!?B z_MFs49K0OynJPMynRD=KsOadVvwG)_Y*1*4AKBoY`&99H4dSPVFfKhjGxq%_h>0N}8mOslVtu}&*NnF_v=GOR0R zN=Sqx*;(7!v1fF~uAUW$QAP~h8u@g_BMvmGRPc;rl$+c}GmPqt8cnKEE1PU$QrB3` z}Y5l*OWd zURIPhsxOnFOa))b$@(}X^#R9*teGC04f9BJ(AiQG#D^>|NFQCX=!rEHPL3!;1G*gb zoD{~e!EjUAm)k&W54o2 zH7N80DGijxc98A}ee|e9HS~V)Tdq<$|GxITDjhxpje~RsJfrWa9A?OH@st}@*GlF%abOG4x7t8ZVSEA|^s zz+%*K`%mBe%fI}I!zx@F`z?&5|K~S<{`FfZ4VDBLN{CqNJ#qKPP~o1q@ALMAXA$N? z8k`o{R`Zk69)x2lCr(O=f+1fVW|5*NJnj&$@VnV_dhaAVIV6O~Nns^LhUK&*KltC? z;}67Zn}2*r9(1tG`0l$?5B`lr#skjF{_%T(`r-=NQ{T}ulSCh&D;7Pmx(~jW^0B(n zBcP6A!xA0s>$}X9b6rujvW&vmAzxh*8Y7Q=A0q^-&IwrWXzryC^dIu}9)a3GM1_vj zUweDIzb2)@WwCmCi-Cdk6yWat+MQ*_;-X$GdR1b%BbHYwuQ@0itF1TDfN6$%KzJ;V zI40cP;nxV~e605M!omqs7=_xvafDBZNe8rs;`ND_ZVVJ9q49F}`aH%QikyIDjnO~? z>1rXJIJEr zY6ia@NnM9fh%6+_uUC4Zj^y8aWK$IJcdXlY_i0%2e>Ry6uERzYy+-MOMXW zTdI(C8t8H=bAnqm1TAh317&3nAw1*gVgpClfJ3B1wpDgnWMm|)9NF2D))+uxY<47* zH@332IHi(>xJo37Vj(a($09o=t0)^uw=_Mow#KruNNbS#(ELeC#P>e@aD)RzV37Zxikc)ECm-cDG;Io`nZz9 zT{9G((0v>$4FzQ;b}Gn2H7Jo*(9lq@)nYUT`#|MHQ>(lS-{ckOKLT7iDG)PTxw|M- z|JwA*051(C7ZPC z&L&#iE+1@yH;S5iELDnmo)N}fDqNqkaIkQB6G8*JW1)ESV%X*;(kY}wYM?x7=)Aio z;^Gng9$q|(5Li*+7{w1Pq*$%gAi6>#qsATuvfXXn>>I7w2lRXzhR{QJMay}>A)&vZ zEvDPr=(7F3Ad#~6AQ@rbZ{KF$-+!ThdJQIywzmDY)V4zo!(K>jE8oA+-?m?#3J(X# ziSqRR^!~N|eo#f>-5`z8-!DcQsp%@n!zLso#J@>biBm~XNq>>R&9R__#jj<$#=Gjo zC#<=`w=p-Tv-VMEEfYm5;DPCIUt5|E>f2a#yqHW#(n(5lWqI);mlQ^lD`Z9-OdM%R zNs~#rNiz~@G%0P;*e)rf9?@7k>w4>Y`l-m6tf;rPo&naJG)8_txLA z8e>;&6k`;VQP0*5q*UrM>abC%sLwz~rIDmY#*7hNtIId4sy30(UnDxJN0XD2NoHgu zkB*v*7D+)B8Ri*aa-g)$w5X`5DWj>XsacRltD0oV`liXr@M-8HuacLikI6@YOrPl+ z$@2>IuM>eAawj-fYhTzd8P(km`fxy=R+c8t=))!N$H+n2t1_395-zxfmU zG`{)fFW-{F_!9^m*r<@~_`@Im@`rE#^cS9Ku(=a5K$q#dQbTw73vw&I(SirYZ^W9#e|dSCRLA4xp&|7le_wA7s`#Q&g=NOAMXpa7dOYMk z^jh8B8@rYv(9ahaxkV*bFHkQqFg7rD@v(OR7A)wce;w=Yj&)0{djR4q@NWdfzD7)? zA$;OV16w!n=sw}jzR`e*@E?H`y{>+C7C6TvIco5 zLvh!Qi(-$vVS9Rh_+kDBx0k|8s^~U#a~}Vo=a_Cz%A?0?e%|Y*rRA;%i4eG1KY-I&gwb8)zzFboH*s2%gJ%d@!Q_^ z%W-1*#-xQYMgJkX+dHA zo-=Ueq(JsQJMWP?5ui~KNr7kM+TO}a3&^A3jb3XJL6u&~<{@W>X#&q3IPD=IShi~N z3iKtf#eJkM3i1GRAyuk&+(4qBESQ~2YVu0*8cKA#S5lT!Q;?V1N#p?8;Dv^n_!lnB zyx_8UTi8G$4wQMNf(8`2q9hg|fRQWHAE!T{!9i6mk|s+3oDcaQo5C%6MWkWHq@$I) zR1QR4+Z~}dHe1CudKs;8f6QXJ)? zp+2>r$zSQI{ppzr=_;9Uji$zDN);+9nep*l*p@EAzt(hA;1<=VDeW?eo1`=>5r0_`h4qpyAm|HL3!j?aDNIY<_!NLwNa1G zwwM@F8g}_cjTuH2Fp-)VWdJd1ls}qm1dnKP9XO+P_<>h+)Fe6CWM(vDw93S^D#H}) z4NNvb7cFX*6_~Kl&^MJe>nEF5A@@o@MIsLi*&zf*Q<1Eos35P%u%M}^z)+?y_9%w> zhKVVni5?X5Ji+|R!$DM@VY6XSV$kq&!{9_i0|P^Yr9?J{GT|$T%jy$@&=8w49Aq&3 z6y&ewryrsK{Q968q#C9AjvldCdGsMM7}_JaR#i3h12@)(hT;c`L#nC2&t=_?*ymvg$G;`q=b-W74=ZV3|ILRq|M}O4e;j}KfJDal$iwkde|#YJD5oBr8h^mHQr!{2 zzc>!7`w-)ckr7fF=w|!=m@T@DC*SCy5iY66Su|{-(Z2q^NX3pUtA8O~LE^&~*3e}{ zQcxnR9{4!Wo4hyeV=U~6u%r;IC63%*g2i}p~G#qs|>>rxH)n_HJ2tdF$E%0}PV##rfw<``U~ zVzuJg0)5dUn&?3>zb2Sp8|ZA?*s%Bv14r*CK^%3q*n^kUnL+0ke8$@p`oT=&n#IbV z#pi1nd5HR0G^;pslR4d^f%=wL=D3Hzbt)*yi_C~J-O-iJ?{11@6m}Y)u6OUM|v>pfvQVfyNip2^7DvUM1 zgWyUr(y>US+yQw5&W!`HRe|uh!wVGzWH}OBe0kww@n+b?qoXi}fsraX0)b=U;${sZ zDPaq6X)HioL>zX5w^74|vAkKq+@q%E2>7p~BWzmeT{ZU82*Pp#mLy2c^a4c2H2X(+ zzEN&J-A00BT6j9_!RbJ3c6xdr>y&b!HU!Z8LVuemiRBGT>RSJWH*fk8kyTk!>3=h= zqLSW*>b8D_RLc7WkYi0{Ej2+UA(csAnb@qvqfR9*?u|-Ze8QVI@dEjFMdI4C|5^5*_t1)|0t*Xl#qAuduU@A(@e6myus@SD#;9 zO@gDuZpJRgt~$TYsIhv+sIj7cX2!1GC_mpQrk>7bj2OVd?9oPeJMya=>)_u=HpwT! zF`6u!f!bgOSn_Co9mi6NGT_&cnP!+)vE(o{HECkZxoMNEpvhF0T%g~K9ZCUgqy_Mi z<~5-|7SyjIPO0b@=r3`DTb?XWW@uQDVhH@XOop&Zv!N^{uemuW&oCv?a4E`gIHlRp zz;JkJ*dWRfDU}ow8Bd23IiNyvBZX8($_J$^B5(_p0|nI7g>SD6=>^P^UtY<9zM;_xD53htd~=Io{I6 z(EHCELqp#>hCb_f`}X!#$NTpkYp*(n-hS(N$q|hyFl^l5w%DtoH#nfeHT73tefyR! z(eXEzzWK`^Zu|>m#vg9HBE9h~Jfh$H`YUt0cwlfsL{)h(aqph6O<;v$F02I;|M8EE|N8#a!w3G+u^B7+`|I`e$);K5BTv%(i!gV+7lq0{}_%bM`3tjIHy#wV7Y#s11j3r z!`-wCC)}RUCE?hZm?Hf#fr-Yv;!8!h>nuE^1k02gOOYSk+#h~B!>qtRy@|( zEIR1X?u7-5hX?!{?dXlQX%JL-DF}8t}$_8nT70*w78>mnbRrR0f z0_s!{S?O!p`24wL16}hg8>B`)Tkz(@V#V2cqZgx$-fJ*vtN_8`+~Ry~ujMn^VcD?( zP6z5@uQ7_Wg^n?LP-eI%_oyOUs*D2#1tnz{Ia@6kH4F+gl;xFNT%{xd%0fBZYz=v(oP5l1UzM(e@l%_~=!zp)9^s9g)!8#p|eKstTF9{4YN)VHNx=^tb}E@FnW?F%C}~sKXQd&{ITBRj<6b1d*O94{9-okz9bID5492J8mE(%_998gcS`v0D6?P^0CG{}~#b#8n(tuwSdz6amjB2Qh zI-~l!`uzGjAi(mg>6QEpE|JYJGBHY~2araS@l#!AQZ>UZ^-vq5U~)7z(<`!>MpF~h zM%XwSO{6683|Z4s9@n?!H8pdQY=KOc*HmPv zum8Nju-UMQ4icF*nlk)6ubF2Zc`12`c_~1Er3|CH?YZGnuz|pgJ`XYg1q^o4AkrPd z&6skqo#ScHQ-k40AK>t4=CKBB9Q5!g5dqntpr@fxsvm06(H0c^C^(p*=0U>6fjX7i zP`q?Lv=;0Q)$`9n?}v7DfHnGzl?M9i9nXi-_4ii+;JEL2_1RS?oTf}0ep~gvbX;OeB>OC=9 zfNtP~i6~ez=Ya%#6iADR)W}Kk4jxh5V_k6aII7CN4rYCw81IhF|!9{~>?=<<|$N#{cO5$B~C%jXq!x#{(XA_@8><|Gobg z_Jod*`XFiH@2`uNHvi~l-DO|F-x$Hc7j|}#7$L#&Mg6!-1%e_l@bL&;_#&91-XZ|Y z2i~M3hSXmVEy6GA9vJ9-YjF{JgIyeWUJP8sKqFw{HHT!~-Q5Ek8uVgg7vbT+fx9+n zqp<;t0k1ik(hy$g4(L~*Hfm!7u1{!Bg!AM>y8#DJ*x5mEx~}Mka@cUz6ovG7vuC$K zkuD0RC>uxJZt?*Mj++gNPo^4fTB57o&8_F_44LR^wr;diJgj^&w^3WjmZq=?dv8>k4tm&gL?e><~tP3Ct)I z2eUY=IuVZrmm@3Es?Zw_p)v39~1#X^MrCDPP zH6P1DMQo|&Sl>oxX(#Cl&Aye-eQYo#nXv*J$EPvR&+u?~f;QS`(bD&+?=#OjHd;1% zH(D^XV6R5+p2c2^g}{vNIU~b@DaZ;}#A4e5!YFJTcss%^y`Ryoat$+)UID%!Az*tK z2g;yZXvqN$E@um1h18&idRG`El$32XFgH*`9#xLn;s~>HfK_yn*mCYgL)liIQ5fEa zv<8I60V$4FJhk$UrFHWF0)%A6!R98@MKR~tEZ$@*=}|H1kg!%xrYwXlw1ypRa(PNI zX*anUXMU_~cv_fa?Z)OUAt zYa4Sqx=|z>FC`vr7;dC8ccYEBCTpxOR764+$a9tbxP;U< znLu%+(8f|SEBsA(`jErVTaUddVl#f+Q zb$)Wjs8Rl?QL@QQat62?Rdu8?k{gpvMjM%Oo?!y=mx-xKlSz}zv^g2=^niYuCeuL? zsg0s0sVx?lkWoo~ULD+o;&GV9ud$s1J4VJTFhyoB|YwVM+>? zF@}aqdBcY2r~hC$4AgnEVWL4$ltEr%)bl9dM57EoEG?132ntTYNMjhrjTBJMmk^Vc z=p4bSSgq8mCO+jJw_4Rl$ivo3ZLy3t4^<7Seb*8E?vW~84}ApMXe|j0Aitn5&Uf5r z#^^iz0`v9kE%L9=p4|_97V79&8ye~eD92mJP@vA=I^J-+ef84S8y&B%Vw7?9(%VispOLSSv0!sRL&lE(x2PLD#dSgd)99!=UmRLQx1q%n_XiBwiomM0(wopwE$S9t&F~Ha=?UdSLnKYWEIo7#V3UN}(@9Pr) zbWhDsnxJoZZg8avXH`Ct`uN=PnN){oOG{s03xK12eatuiOp1devb`24bL(B%yN2s4 z7N7U_dPP%;^R<<0du-hJd<~P0mR>>Uz=b`=kfK{vPTAH>+^`koBuuyxSE<5SDc#4l zg2n=jdNL^gnY~4hC+rgd7 zSA=)YX_iKbl6i|*s zWLUcGyUUq3ddNB02z#`tl($V|`~eR~`5W$TYZDmr3%ph-?}zN@7h`(jenz~qw|6^#aev2 zPWqb8T7vM10?P4%jij!wX_H`qy_kgA!&N8F)m3LM3EmFZ7f=-+xhr;(qbZzFi8IbE zf$X3Q){}skCxJJbR#HD%kzrkuL6QStbC5Z#V@j;+t@BBESF8QBjrPQI4VqxOqz}Aj7+MG>hj^^FlkOUVW2sRWAl?of#b*+ zH8qhnCks_!a*Q%BGL<2sLh3_bKZ8LV1yxN2vMSOc$s|1TnnY79d>l<^Qju{?78NQ5 zc}s?9hi#S>v3FxQFDT_X35*n0BE!h4M4_;LDG#hs11^XC5R`(*YcN3O$iW&g(0n)u zeQi&df((M_l4|gWcS2PNq9{E*1#}}y^-+)z8;?|LgMw>=LxUf^LoD|FBa#}SBsPLb zWYmIc4#Mbtu8X}NTI;A9dfxGEXa_x>uMNF={@HsH7|$^0ctz6V)w2%AtF?}=t_s@d zZO2QFZ*SkeKV1UQ{PwrseETnszy9*G^%l|vGzF0z73&7xF;5CmDA>5n_y3fT9vOgDFPkJ zju8B(1YhHS#fSXsZ@>O44>kVV!x7Z0u;@5-N_WKnq3?t5NMJm`oa1{)57HeY%eu>w zM-)leFOV6eHKLd4N&Wi~T?U%_`}(R!j|?FI`xqAy!MLgiLULbrN zx7-8W10TmO>bWllE;8608y39=G^yZ(v;`gmCcysEdmYdafILb-Ky2(Hd>cP%YimD& z^oVUhVf}>U5e5D0Nw^zFWUo)a#_^;<HcJl{9hh^pBcrlzvuoppIR|Nq zv+G{%m}#sJ3XdqD9P5x7J~loct3Z9Nx1SL}b074z&B7|mb@e;k70XbLbM)hPZuXoH zmM61*$6^DU6}UCF{a_!>Sv^N8;~W>#&-&3}8Gxef;PA^?MZJpCc3D@BgAQ`&>8fdg{+Fz7Ha7MG>?qb%<9Y@h}4YS%7#yab19wh zjEb*P&8#tW(SH>ionvF2W2IxIpFBU2@L*#piH)%_P0f`tG^sG~iy!+|EXMjsQuJ+X zcv@JjAfvJe-v;I!d-TFy%L)h`dmDoM)nWm_d5g0JqOg0;uyL%w^}(SOi(Y6C3uoX* zTRt-@3`P!cJ+-i6pa)ngDXMjTzsea^1|h?usg*IXiqt4B{^}huuvQZI_c$wi^#@?p-Y7{M)AS{^acuro8tnNIK*M!q-L(IL4|OSeQH8n=8N=9*SJh1 zR9=4La!H0HOeX2f#ii+RKz1$(ZS<2& z_)1EeGtP~Ro6JozjzMoMARKANak)t^=+%;vv|J1|a;+yztdp!W0N}8*p257sZpKbD zsFc`MRP&T0zb+#q#;(yW2J4l^`s)0dm^vfGUn@w6HFX))sA3Qrj3%+cobawPL#I@)*S3#V2_{6XDVW(lI} zAMG2hJM=}@cR3pBV`zCqH`+f?oddGbBkCg~2&vG+$WSx@ql?i)>bH=Tdy-AXz$L)t*1X#Roxs{zu&Z+ z`oVJ7^6zIQ>IUP?cRjpbz8vIW$}S{_hsS!m*V#c2BvjTtJbZu|t+Z)xA4EWQ)dmv8 z27?fr!Jllr*1dd4cFeA}pYf>lz*ytVtPe?$c7#;up&hjI%Gp&vaL>N$pBtR`k5ee5sQglTlERCA+~Qz{%$5f$~G zpZmr>eS!*Efsvzcqp$DNN( ziYTZ_oGz?9q}T~Y1X@Z8w!#QO?{{GrC_5z3oTequc~@KJWYC|HS5i|G#jYAkjObF( z0D6o6DE~WK7X{vcXkg31{BdP95)E2vzx#*$+W|`2S_SQ^6{*-wELV7`f=d`~w6d4u z;9ygfsuW*l1Q=!ve zhi$^tF)aok0{+Fc(djm^T8ZelXTNAU)+zfii~@8%OG+RCGd7-Efd}JDhcU;fYC9!W#i*i=Hf|tAp4r`8V9=hoQ`W;TzWia z9Pz+?>Ad(j>Bdb}+u~lN2>`@E zJHOqL7E^)!39#ld2&yD;US%ex-fjl%ZS`ny%cy7fMn(y`>5b}R@+%NUnaNJ-Zr_cc`hNV>sfQ28 zADntHuKQ(KSNF>Uf8FQ@{%C_`qXy{aSgQDsh?p#AW*>i9Msb@iLMXl@G<2iYeWM@4 z5~?1!tnM8c?XB+X%_0L$^dvq2H^+SA*5g3b$kO*Mct-i>7J@2^oL6~$D*zM@o_D~m zq1S+y+&L^80OVJ!yY}k|ZSB|L0ov}dg#p)*SZN67=C3B4<|+Rq2_Bt;6!B;>{^V5Jw?1rh2%U z9{p{*Q&Sv_bOHttl8;1vyv0UMHc6a@(&8`<{U+gEfv1vc+G00058;{C%FB_h2 zAfvLr4i^VIHF%F=<8g+&VP`9`JOOkRWwGoRrN`Mb>$7N!^*MKLcALxE=o`}x@iWUD zFsE|}#dhU{a4mffnC9C#=yY=;MY0VaX;(;yQ;46#&HyN*pnZ|@DC5LRb~Zf#1D1_Q ztQGgQSy>Hi4TuBYtypPVA#^gJshOQ+RXU&_Z}U1+}r*s94aC#l}WUi$zPH#pe|Zi_a~feQosh_A<`A zms{#Nn}P`k<2WoV_CBvTb9nX|$5$+zuUWM8?g3S5fwuZyF$SSm8CgL$XEQUpMZ>j8 z|I4qTpmtYFS-}jjEiE}^WzLvtnW>3exDunqg%?6eUP(biNz28B1;TMtyt1;Kng+5U zs9dwPz3akag0%*#1toa}Ff*j3ih|U6qJ@o%t(p>dzDn~5`V0y@p7@`mxlm2LiC0#X@I5ZWK#(DmB6@3cy!4FH(eE6j2osSRv_gw7(ExA8~jn+G4p|r8{B) zd{MDfiI5so=qt!1XGq^4)8e0;OUBIHdR zUe-YwY(i#ydfb}piv&@XBGxLd>Dah@oQwO&q|p~h&+536&WO{AOVV*QPD_e2o}4sJ zBB6mbigBEA+GJ9$>kH$w63(u`Qfi%)k(S0fBPI>1Lr}l!OJeNmV`7q$I0{=Y{pRP} z2;347RU@`dCwDz3}rkNcaz#J82D)*6zMWxC!RTdp$w{rh{Xz2U<&=t=d-@av@`TMt_p`uBJe(&Gzxbe#I z6-36ROSc`R4%fe)yk~xo2McVYxF=PyiW@oKuX)MA4hbF@oEEu{lTwe`3DNp`Y^69R zesaQuX&5XJ%ujsXVH~eDkQ+HI#Dws8m`j-wNiTe@a}x%Sf6j;e`B!dlJLT_>z>5FF z@l#*Ee7Af`SNFRw{{DipL4sr4cf|jD-S0<^cPdz`_`=}f|0Q}zxJSR&{i5rQB&=_= zy8j3kEF*#Hj~}ZK`GW=q!J!_wI1-5ZHg7hTvX5hEF;M6X$PK|bhi?O!SolfZW5tps zKvc)N2P~p2wjnnB_3H^3Ll<8M1c)XTZS6w$@P>dzELFm_kL%pD!%1>HDSUDrB`by6 zici4uQVa)fL$L>pubze;#rb)~DH0lrFL6=v=KM=H#a$>4>{i&v@xwf@UodRIGioWQ zUp-z^mN%!SUcMw@VJX=}=@->0^IqK8c6N7n9RQB?T@N09V39&Xq}}7|Qib(~AdIe8 z{^U`)&ZN=xGdpK&I0;+HymNY}1mt|S5;QQ9BoHC(em>k5J1dN$q%u}>1aA~_LsZ0) z%<$_9$?57^U3KycVbMXq4o;*h%5t{3AU4Eddxv@E+uN))I3i0z1i15oE~H|G-k{%3 z;6!(_vbR`pY|(cl`eGxqtx6GHDeWw^8W_;bE@c}BX^Y5Gm_zMmG&x}Q0eRAz{*C8#plnT;3eJrjFx(2W9hfIWu^Ccsp2en zqZXKD>|OiZi;A{u&d#6d8wnMhT;nmv$~EUbSUXlw-bVk6Z*~_HPi{Kn+qRODh>$8A z$4`|cI;euOf|fjjAPNdfcv2#XprNb`LxsGp0*76>oQ+xHE__#)H$zVv-Q1OJl@$2z zE8o?)%M%8=+hg&ep~kQ;TkeV1V*0qUn$QaLpHU)J@OyvAzqKB;A`Htca|tmD7sr(Y ziDiBV0}LD-V6btAwFdSp!rws`iVs@D!f<&r><$T!i(z3xTP!TV5PJETVNNGu7sU}u z?2%Rp3#-`_GmWE~;%-<+;SoJz^Qe?j;Uc$g`|iye`)>5pcONkl%-+7ct%elDez#zm z*C76igG1lRy7@3})hMBajJOYUG3-&N?a3r=3&e3$b(0`nfO~1gUZa|*5 z^|xJ6>F@6cV1phK-Xx@^#;e4uWWI@yeMv6 z5QjGU5@XKDTGMxoow0C;=2j$?AgxkS5re1tjQW}S5==O(D`xEMX6V_jnng#2U4Dj< z5u-)vx6UZPx^AXkAUWzX(A<`RYexAdF(%a{DNInL(r985Gs=0Dy2dI&0W%pLZOlMk zwyKKM#whzYOgIwT1n20eDf(meN8#eohvWdw9CWY(q*ds3ELHN7^P2NyGK@Y_n)TT* zDsUU9ZX=zMSYVivEJInnY&fMk&oF41;a|h_((@F<#5@D!U5AGm_VodS4bV86gA5WA zgM}Cgs(t$OIr6f>!SA9}pCX!~D)8pDaE^AMEml>^$abg-nmKUhLA4MZ*rc3See^z* zgv5PVN1we@Rpn~>(DR{8;0UdCJdY*Ht58QwI-WUpkh1voH~RYZw_ktx1(S@Q|4LuX zzQRsHB498&OTu5^rf+`wxT00ywj}u0X<^Ddc^nlHhS1Yrn_8v1lM^C>!n+VeOHPVD zSjo_F;<(7|80!Tm=Scw)Wzit*Urzu3@gaZy4LGn<<7iSjH6DHH!FT=`bNKs@fAK%X zaIf)Y|79+wj|St5#~nEMo-rLmoL7mC_GO=jukX;1|Kn(2M|~eJA`}brhR_zbZmE;% zKvV@8Sv_^VTd~|n5BkQUH?}FUv9}Of30w?(9pLUB%X^gA*w+jjWzm8Bik`N+yLRk^ zJH{OD0q#Er0KftGR{%+nC+;wdVzg0M$gnSrJm^?a8yS`B$DQ+qg+23bh`+j#+K{ra z{4@pU=#*f7O(|lmf$0W2MBOZ3dLgaix>=sf!Yvd zZk1=wojErmOc8Vv%Z$k*WeT7I4tU6TaZ@iwOBzTEDrWC*g%)cXJ?Dv zUT0A(O9JB>KplXa?}?0xh4VGK(}P-pI(WWcbH}?Gi3DZb*QH()a6J(trMD_$&>%0T zq^5*M5_v5(xx4ZjS_&Fkwptp>f}24S0&g*sb3t`H{1Wm2Ux@+x7BIfuA7g5sdd3pE!HyDu&j*K}`&b$2h+ zL~M4~EYQypa2!0u0AG~Dv7|WK1m&DWNVy2I?3eFHa5HQ-b}9=z+n`gSFR(p16nt~g zILf;PiUVE_kT}YP;%MVF%7y*4aysC=3Tq9(N8jKiRVB5pTxGvsB|SC0AHI&ZRF$>V z^z``j^fzrFf2r`YB>`KN7bVNEAKF5cBO{)OwD&LlU+Ch%H? zzY^v?#_>ue4f_<=Bq$Nr+_*GjkT}v@S#czxM8(+n#blasT1gttnaUB9VQf4@io-b9 zIyWf|CXSf27?K_(B}w(x8L)}g$IPHFwh;~vI>S~a!!8MS(Q2a#qq=${u8OU$Gs++x zQqc(5D0V6_qD`ev%s7AuF;1Fqg&-8=#mz3Mh`C6qM8(yrc7{kQcS8 z!LW&bKokYyqgH5*r?nuQb7mz}m^eB@JA!J1gM;bp{r%8d)zFT&O#Wh^`T48o-=F{0 zku(JlF6c`#O>(`6sBxDFS;ws-lE?Cdxgb*qGwh+e6-}occdVZ6Fzo94w+K zy8aJzm!rSa^+j*2FLo-(!_sA6o^go7FKp~Um5Oe(?~uQGw7R#if3!Mx)O)ky7=p%7 zSAV?7<_$z=naTn9=xaLC3)FMJ6{z>vTklrDEs`GIq#eNCXowAHcs&8~7Zk@tY(VUy zyWSJMiSSrD4v&qE4bbMQ*nn8=3HL>o9pT{+7!%=iq+Q5s6@eMO{zUN2uPcK6HHD&j zMYo=tipZm)Y3JOCSAXSKngb1yOD0A|$dr%HM} zV^hgxD(!f(K`I3#4w4^C85Oyeoz-yvtwMg} zfW3h+$IdLsUmRC)$Z^;PhGQEb^D=PFyBxa8c(ma#0OD5};w#%$Aq@R;u<8oo83$uV zBXd*`n8Zg_k5; z6f0pydq0aVm0rew2^2@mHJqU?_L@ME3fhGd$XFx>;HXrRQ_|8>;+izv(Uqji_`AG@ zP|{MyU;@qYwsZ@pd%uedPa14BcK*V|m2U%9)f_@!NXm z4hm%1x^cPq0JKqFwZIN~2WHVQk{OpTUWSJQt!!-PC=SEnMd1?VGiVOd8>B#L7NACW z96}-l9rZwsE_5>ljI$~zPeGT8eS|$}6C552p6H>-r<6}4n9eOin-)_^!i8umhJI;^8AZ^}t?sKmFm zrNhnfCRK1ZGSgux%@l-DG`Vq?3gWTpIx0E|AJL+Ma4grgWhRi)m}CD2<3?TSN*snD zu5)bVSj&wAox^xCO~)9f4cCM?SV!qQ&NwD1DW(L0*fi1~F+AglflJi7BsV4|7mX^U zI;>~vNpo0N(0k$Fn5h>_m5P{rJ3zpgFq)B{Z)9gwT~81B$i$+$jU@&WD@Huxpa-K- zBNMJ|t1~jGtFA^*eN`P>IWlC8Oy@vg)}&|@<*=s7;G0(!nT|F!jaDHLTST&BlxG~H z@NDQ;RgE^u(5M0gM{`~iX^?`Zr6SNroAU|`u}~=}f>kudunA~Ul&2ViyrFL>ONrv5 zdaPH148i>}NHItp#)H(=W?&E;gj5QOQ#h|;@N_AdZKVe9=t=b{sSWy#!ls0j$J5%V zr$LxvJR<$k@l=dBFyIKj-_a2Yt~n4KZy$BQ$WdDhmihU2q0i2%qCfVT<8QzH{8wg8 zn2WA5W@CvO6NXjrpx__w&11iVwB-;E2l4LH$G2*8>CXs*rVt0GrTenEgepHF0X{{Y z8d9uqMo3!|!4kd4|3dV~o<1gw{%`#u|N7ghhu@t#^~dkVqfec}1jAQ18pcpv|1W|k z`bCt+M&p%n-4Q9Q;!DC~$lu>rUDr1niInKazW(YcjYUDMI`XeWx73#dG1a)GJ~T9P z3rG%imL1FiyQRL!yOLYCpgHsw5km>Q6+5BlJ+XKT(~s8y-k56W#Wnx{=C0Q;p{E^y z@)WeilAdsng-fIGHS0)^;1E;}I>C%%3NBGgH@B&&o0d1HEP>$I?V0j=Y3cRyMl@H#vA^6XiUb@q^QMZJet`&pZ_gX?%a3#>Wn+B_<4;3I99!YZUXyecc% zs^P&~ly%TXSJB*dhN+`a9;@J+`&6Eroo%1>gIR-t8v_4zZj}cdU9)9qQ~{l1CucTf z)+uClwF^^?oigx6bDToD%A8hFAB*|Mwx1sWU+7XP3*lvosEc*j5v8#q*+|EN{w205 z;F~i5jPoj**#o37GzaLXRFlrKQ4R}3DEFmgcXnovArMQl!!w&_92+b=videwNORB$ znC7J$V=J0tot+za`BUkbXr|wQRkX!(1M`iQmOcwli@t00(A&bxl+Roj3+RT0MK4Ai zEf&4(-)P}^h6PCs^r$%ZvXx^GZ7P_CT;tMN3upS>Yq4;abJ5aLzKg8|I?cG+Oi9kn z)|R2)O3G%o!V&@VCsrC7*kG_aP!OI>!RL~5kyp}^vz1p8kySG}1uft{MJ+t07=V!9 zg+J4l37-n`N(|3n>V^ola-)g7lDw9!8m1rI9E)HpPcMGY5BWFN6|JH)<;nqKDhH4s zJo9LkUIYH?fE}VFKkl^NIVi?fMFe9vFNbk++a_s<%}tgaM~pKkJ#lbE{|Yjr_~3|_ zDaGJ|ZIZ4K=8fauP40?CY1=~g0&_+M+WZi@LpVoyts*F3`+N<>D42POlKO5@rpI$yWlbd>hS3)cI!~o*@%UP!Yp!qN;y^aXHU;`3 zUKF{#V25aej%$Kz!pAus*B9*dfO&%r9mbPMNpq8vxePStOl(?=@r#cyxF9wucM{i< zjN^ax=$ei%bh-rr>Q9>6;4Us6^nQuJ1b;Ai z3S-ApmK_WmjY^DqNB=@K@dK|<5<65seE7gs^c}(PKJYSylm`%_9jd}PDhwRbmIa*A z^R>Yp_kR=gPhn~}ee!rTa8lxcpX9F8dxG#Om9CyVrfNw&CSYXUW2lz-aT$p)RG^MT z;KfDM1UL12_k_C>MI~ZnAp)r4K>Yn<@P)Y7V|#J$r1{DJN+0s)UrB3x&t#712fC+x zANczEE{^~|3M)r6W*!)MjE{>Vw`Hz}9n$sn_Z|5Xjp+xZ2+SMm>=9ia8j4=VYDGQp zG4EOehtwaxejJTS$F0D}kNIu4yuAax7u9d+1?qWoL9CuAZbLLB07nz<4Gns)V{Zwx z`66@&dS%1)AT`3ZW9ch=g06+LmBT&U9a4kzg}W$nd%}{VP&>TPt>MXa?eHgxg^J;d z!n}bNm5CpIxH-?VXI^1qDZ=;9J8=_K$w=7-)b^g+8 zYU=E+m&D?jM@Os&#vGEX15=K(>+N2caCmStEVe16FT8AoU6l1lyNyq!4{#hl>oz>g zVDrW-!$xO)R#$nSvctO;QXe}%?f9WYW!7(Xhjd0eJ2^;lKwn+x<8J4LfeZ$x&X zF$L?DNG_5kp#ca-XJ^*HKo-f4tSzxV$=2N35^VF%tgS6g;pu3w#q-nHhUioIM7m?F&+`))=bn8n7M?Bm+FNO9v0y7luQP8`EU;^VOVk&^;fw_X;-zMEQ-|}Ct5fBaG{j9>nJZddNG~9* z3R~8h7u?si~1B;Iyf{Jj(^-Ts4%?lBR@c2@Dia67tOEqDyq! zS5i=B`-Pc;mX?AtUgVxN3_UcY-g<4qJl)74)i*emE-U&e&-Xzq;A#QO>1o z`w=y55s094cS}gHav?YN>9M>VYn5qH9=m^`t&J{VsUk}1*}jpA$Nn}Hr|i@3g$w=t zYvn2zRMJyb-t70&!5a>ztf{1?t}zM>+JY`JmLoMGAu|=Zl}u5w@+MA4B_8_{6;d1V z3G_{RgXF}TYr-VjQ(WWGt^%*s}%S|Hfk!GxukgF3nX`GYzPT8#ai3h)n{Z_=jUg{#N@M=1EkSv z2#x&u`nu|R5)>77b#!htlW$~40;AE$uHLAwqME+*VInojFp5bws>?Sqs;kO}gQLo% zt{VH5#tb@alqHYCIEu#Dq72jICfGxp81==4^+lvX3b0a1HZ3r%O0GgxTR}lGGAT_m z{pRP#mGpVZ$@((cQd1E}Vhd9Ao7u%7ljY^1%B=|<^+6bG$hb3B224jvN*;-e#2|w_ z1B0cYs3^3j7^VR6)l5nwaaiiD55ii7bjTxBgW6y;wnaq+CqCld6jjxyk3_3VFw(Io zsSgT%ABC}LyXu{nW*EMt$}42tNnOjwwq( z&jB|_D%QI8jbMUcP7KpJJrXy3+0OW!q% z4U!ctBs@rKSU9&pZ8+o7M$3jX<3ug?AS`-&8N`9%2j(1zu3Q6hqt_YXS>fWK3o9$V zq*YLfri2z(H7PG5ub^xucont~BUn0MD4`~Y%2o|I$pRs#Y^J8fLj*I>Jn>JIAt~e; zVUGQUl9Dam@|AeZpoI6xtGOTvizaymMtdpCo0*ZE&~nkBmz3b&r2m483ujQ2Ay4EL zc=TZ=ulDUf#XGkBketQrc=T&OrRNjm;WN86`c!)UR@SC;K`W zVSeF){RLpHnMqJjr+ym)Wn64RjnRAl~Bqwx8e@srg zCh5%4_eXd*I3zono8~&HlcZyun>5Fb^hn3%G6f8+ZNkNomd4waG&nb`a~b$W%3~%s zhNm2p_4(G;`5EAT*|C8%2A&SP`k49|yO{hK%s7my>q%PFHNv%FhwiqD49qnE>!?F3 zY-3$x@~BZ`vPqpub#k&vGJ8m88nI>p^((omu?mJ!(i0>xOi)N)Wg-*p^i?y^9$d6S z&ocd@0{u}CzGOVykTuJic+&A4$Wd7`{2kb<6zDIdkRZ?V2 zhKbM}LBT0YsE$npog*qLYH2tH)osI31}O$lqqt6mzCR2j8VmCIyWqs&cNla8M*(P# zJql@ycic`N`~jHJC{)LCXUd}>kyJ$`(>qaSlSz4vQKjuCYcQwe185vUg^ zd>VT0j{|St{Rsvm4HJdeuTK|*_ul04W!S?p{N-qx-tgPDj*i^RTC66;6XVyI+I@U4X@bU0j<(62? zIQYkhKgsLD2wqihfjaoR5P^W$L$O4_=1 z96c6pJM~>Y3bVoUew~VXG_Zp<{G^%dwVS#D=RD4T6$MFi_$_5dt9f|i)z?j zPN_KWwa_~;=xAB#6;2OK0<<*LL~A=Uzm+x2Bv3e=+%*&JX!87{L4v^yEhapvz#~Zl z7hZ?ROZRx5J)nmTH*``42*1j^@KoWhl7gJVUG8dCQqXdtcW6L5fKe{;CmPa!NR9U_ z^2%CLDusJiexDEdH(s3}{L1zXdi=^X2h@C%WXPQ>ms_uZYkrxrqmq{c#*GC$Nd>al zu!P;YQ_SVCXmdM4HT@<_ip`63vbh8S;1Z z^!*}1C36i6n9Q~Gcpa5Bu*-E4;?pN}5)$HEb>dahG35{&6agO1O+$;DPVQWSl$$mF zXsnZz6bDIx1{FFro=X#gV-hDxlO^ooDB(RyeXex{1{)=IbV358!rIPyGRfN7E}t%8 zzQGkLc6QbI=%=?cs-W+TdZYXp(iuh-8TA!Lb{X)88dX<}CL2{(SHs8=gQ)B%MjGg+ zuQD;I%SVfwNnPFOs1#LcHZd7(YRrdM)TGMPbcUowW3oguZ>ERjs!`B4s`#f!rf-^z z3O7@T41Uyq4!${JvOM1~Gz4uF4$(Z)8u|czESh5(Psvdh5QXx;-Od4c>q-@lqM2t0pf>nb)sH!GDde4&Ly(*o&e=jQB zg8ApseYz4XrD8*aRULV-A+@!i{0Eei@2irj@-)-J`Bus}PU#K!e|dok=iBt@6H;RJ zqzHJNKJnF!Dclr%$CvgS_XH|bI3oBpac38Idh_GZ2~R4-+~U9dhy3}s2dBP&s5`z4 zi^ll!$OBzpKGgL`KfV75v&=_0or25$Qb;yh7ZSoZdT0oP4*LC~u0Avr7>E=2MPEZ- zEb?k)5t3x_vG*;(9@QgVLE6F_gAE>b1bD~7wto>=)zRb z&Ew<>o!;ybf@2;{Zu9eQ?BQ_ZJql?KMNBxnAT@4!3BiH%iU%En#&MjB^@7L{ik9LttIdf*`%$eEt zO16sPVFyW-)pKV!0J}pEtD?e9060i<_-*r{-#I5b2{}iCW0gBoB*Ul^i;yy=a5y+I z=&Q@gkBu8xslY{wX-78t>9f1C>8j8YVuK=uWU~M=r>ATTIy5z{G`FlYvpY+9ff5-h zsG_BuMc2CEPLA>~1$Leq216Vcc)%`s0;%};oD9~}y^xyZ$~cv^rn+5*tm zh6U*hd z%mmi}57bu9OaaRZj)3sJonhb_Fjm;oO`VhhkQE9TM3^zT3sDn!ISpG>p(v=CX^61_ zteSWczzp2|mBoD?69`~5)I^w-Hy$op^qy>k?{P<#}2z}St; zm&H2eA~QKK<)|SYvPo~bSX@IAW0S5}&%CkPagU5MXcM z?!cTwB>_?-6G7Po`hB62o{$N;XeKF%^f}kb^cSu=Xo)?Bam+z;z~%9APG=G^79H25 zBpp{*QWtY^AB~gJJ{rGpbxks!-w2`>lhLlll3trq%!QP(WjC@&tQPn=iB8QRoFG66c%357(Br$YGf3XZ44 z&HBkmsmO{7^no_lPtGf17pZA;fqq_d5hAm)=Xp&9d3j9YXf{MCJs4nlG7=amhB7)L zb@ALV3bT$phz=CdFQK+=I7I|h5QI&H<`^C}2x?9-i24we2s{UAk>GczlWnGd@*!v` zD5zHTQ814;gj1B;=z}|gKd8P3Z8Ve%ROsdR9UYFp{`^-YI8T`06B`Ney`|E1&S3~| z1G^`2&0Jg;|Bo*%6HW`Z=RfYRCy$Mn^mqR!dv6*N=f0->N}S_ZDr$ohXcTdX+vI3z zQ5t7OcN}6_&Wf`}6bDeF3sF&USUBJuH4Z3I(K^s##G#Ce0V75+tX_#B5!T*o*wEda z4=>$xe|S0fb^o7o_c>pCyu=*t{_^WTy!wY%NW;Rr@uSTQ?qY3TfyR+;^JeD9Xd6UU zbd2+KXb!yOTeQv0Q6vT$*r-I4sz_h_WZ)nZ$8zyOm)Z*O_da9H&fHlBkGd>!|f9Nat9p zz)RYxOrGyN<%|lGIB@p@n&UaCkJC`2&Qpvyj5;ye*o4G5HTu9c^hQ4!NyYRcBl{9J zp(CO6+?ml&UN4tIy+S{b^w=B`E>f?v2(OW|B_P1U&L$^ekWpnCd-o$O)A} zdPJhc8-R|%xfHK!<=bs?5A(0 zXY7g_Fue2u*U%G`E@L<~_}_8m_$$({`i$Tbjq~tp`WXN;Ze?X*U=c)DeQDI7Z$R<@ z)N|lOt;~##5j}zD(@akX|1u-}@PFn_{_lV8TM_FNEweYYedtqp#vzmy?F#H0&loZK z3{wsSV?pD<)6!G}wXnC6nH2L4tW;nY)jrXNtRM~x?kKB?nvg4aMm0~6SZTX`S`*Ty zr5g=)EW=MR)VO8FXdB8@G&s6p+vsqLc9_M-!<2^_c7DpCDF4BDV?U7Q!HwgMq)m9L@v$-Zay&S=zc3$o(dCch z`N7M3^w{vbD7vkre?Obza42!?_Z0^I2yw zIGmjoxh=V@F%+aLc*fCF?NUSvqr1AL%FeF3x~EEESDi~YvkG=|AO+ilgbF=pSMAX4 zP>tF)?o)Bt&W3%%fw7`osgl=|Kq4eBEp6IvdV4x85kL-m3^=9}?X&Htv+dKSNl(0c zm-Y^bQBf7!gV6?hW1%{}x5s=VJ1r47j_ic&M6OV|pYZAJ{U4B({T^A_@4x?)@W=b# z|5M^0;o$h~XW?yN{I_6oiXni9q13u6g)*d6hMVCUUyv~a@Ng5!4q?_5nG7B>;4&-r z3c^4sakWHEiJOGH5Fu9PgNt>AAd8FHf(M{nA~+)bLw`eW@|Ry{{s9Fl-=crCvH8cD znb_#qH?cF(Qj-d45#&^2)1g0R(x;~Ac^h+*6X`bQ=HGauk{)dyIW@)JQS-?3H_>mn zLnS_*D^$!Quj8RX%%>J3ffrr8j*Pcm+`>&<9NZkFHGnjS##pq4=s=S!h8viBD3$ag zWKlZV6*{l{Q^u*awX(A_4z={1$#EhE3oi%kqSDXv=bg`!jjS0mY9ztGutr&0V(%zD zm=B$2jR7kMf1SOA2Ju>2f_r0RZUd$bP#$9fFko`7_IjGM9sMsYl90MZ;)^^hF+ z77G>>y2T_lh>^zxv%V&hPLhrT0+NP?N|Q<_N+%e|5fI>=nuK;(Z@L*O^&TuKEgj^L zN-DI5c6z_|e)06|JvgZEt>3BN=^gcys9(?% zjN-sgJ%?1d5ta*N(Vjv_NT<~E>qpOf&pqpT7GYo|sGM{if33%+Ot>yMG@{S+F6=oB z^o%W#H`Sw)`UZLkn(9k|R;G8FaApJ;Q4=!~0~Tg}7RG*DRl;c$G0K3yf(~En3)R4Z z6W1VwW%V(X(1)eOz{0{nA4?HNZm_lU|IC~G=VvP`SM-Cf6e*4J?61QO_($)&q$^k2 zTP+v2AtKwy*e^7y2>NJW%~N)ZKCO9z5*26;kVn}$iukK01n5yTsz80Pg#)43kTzba z0603Vc^Z=PR6>lxzriz&VYo%<@h$zN2-x}X-J*r_Dk+!9!ZxNrRirFm_9HCY*v}E! zMzBSfQyMQDA98s;&peoKj<(noW|=plFcx*O%cxPw??+gLNu&8};phiyqmWlBNW*4A zON^UJ9#PJ~nidvk=I3W+nr5j#W);Fd+Rvd_(@dZ^ta>4{-iaU{5x7#J5;Ow2+5MJXma#->|l>5 z!YL@G@1P?-?kdV%&83&*I<`2sJGU1(x3qRSwimTy$&%}=sOEgEvvYMzw}NLHiWWt# z3p+<=XHlclGTTzcjVdlKEeaRA*$RYW?W##@xG>)w3@{ium~F18s**}$6^e>HyNZgQ zitZlnQ$c4eGP8MTP{~Ft_I;j18nP;+Cmg2VZ}(t>l4hT1pJ$)<-ahf&yM(;=*;uEf z+2=udWdC5F@Gc=O+a8!N`m=vRfl3;QjBFIReTT40B3v7|`Tp&9|M&er-cS7PXL0Ri z!+{RM=^=5jJf!O^T~$uK8CC4RWp901?qd0iDk&I&cIAm48<9I$l ze~K;@SUBi=6TtK2(9IW+9i2w3D_$^g^rsg*(|BR@aucV2A{he6Xy_8cDoh$Bonf>z zvJ^@pV<`guQKVrbmQdivJ#HX-aVTXn!Yd4f(MgOv&bUtnow0(#;VZ%^pnl9K7=VA&-92m{U~`nHumB;=L8nQBtYg{{vo4IHP#Q(1=-o#5)vbo5R1srLh4G4P{9IWo0L@aiC8Hb`A8?$Dlz4Ar%zU4+S(N@ob}X zV&cyzZG(kFVvE+LV#JY(^h#9%>-q0tupj@XCGDg9iSBBTD&( z8%*YqOdPX{S?JZsS^}fb-zC0Fu%{mq8~g>6=yyMGql(D9{(rtmfc)VX!@E!A_oQ|ZLs4%g z(42QD@HNB#f)VFtR&m7Z1ZhSf{0wfQ7tI4}1;aJrgV1d>#4AJvn(m6tYlgojNe%D1 z-=&!Vm#Y0uzR6#HSVc6*+M1&qHe*p4f5cu5I5+a008-d)jf^6(i;Lz?}M}1%t36YZ| z6st4@1adef;3R26WMoUl4a<;#BuI$~78I$ahnRI7mX>fIJ!TxGsg{6?mZp|U6)H>` zO@&Ex@DPp-=5Lf({+BOO86|axgGj`Vk>p_h2J)|@O6tN7!goko>_BvQV#eXw`<2lf zV^aFn^J^~~Md_Dr9;2fk4|uu}HC9%%S6VU6SfLvxj0!V{MCG7ErkwX(t<=yj$qQ{CuTZoIrmP^YvhuVK zp%nC}TwQ5bo<6D3puO$slPhpHYBU&ZXn-=m1>FmC4N;%cCaDiZRN7M7hBY954Y!fN zxDjF_MJlLoYt!6lQ&KcM?HGn#v=L>pZNn@;Zs@*kxPQ1G(9z}P;Y$Q)`x{wQ^k1T{ z?cpWH9!vzI^Ea25{ma;?aC=++C3vHaq)mc@`y0pm^DnVgp;N3pOdAU&Nioj*lKgQ_ z!xk5sW`4Y6IEQd=6lR*L3;WUAwpVPb&iYsk?r3qb+LZK#JhNCvx*}7aRlJb7K+@uK zmb!Q^3(c_`EHoAt&=V_L0EnX;4v(Ayr_UD)IayBf@*LFCJC!fw05hsWg$g~HQ+|Q1 zic^79pHd)FDkM43rQ%aws{+})sl&f%RaC|zp#t2ff2&HhTIA@T)9&Bex?1U2>Vz7~`aPDq_cN7IHg1(`s5bA^H8P#@*s%{)qRoLbA^nf$!Alw_> zb`I0s+u(d{=h5HvWCI75oj1+6QSiWe((>%n^0xD~Nm}%zO=oW-p0b_CLL+S(I~IHR zO4-_xm^N+y>C+D|ktU`k+z0UMhcr?jZ@NhRKYIY!KqtR= z-0c~3#br8|!rGdS#OWplM0qN}JApOH)Zif;6EIZ@qNkzgboby0i0<0WxGY{RxJeZz z9$a@K{dYVOaJ%bSU%|ogiqo;*W?tFMym}K& zS|k12Ozf*SQ)rHjMr_vR&6`+IIo_D3&j{Nn_oy7rOqoYU3$AGN(G2z~Q^=;w#79m= z9x)XxJ~9%b!ru$4l(B&$W&m~$p**-z1%r(kfib5Sz$!Wvm~?V1 z#0K0OfkOcSWkVCk$JnMMH4L3JfIHfNO-gAJD~;o!B=4bAE{aV$93s&H4#$KjR+*Tn zEAbxmo)9f6Fmlv+A0AH7Es2d(j>3|pC`qj=#b9Hw&T?=L-q8biIRvIdz|Ji(`=FC! za|dIV;o)-!;d6&y!X@7*QYyIH`Eu?VzSE0o$7uMuXYVM`=CF<;{z~@;z1Wa=j()AL z?>&dAz+3~_l=FIiB$6wLr;rZ$dd_wZ6wB)I2YwSXQ7{50r*zHMH^6mX-;9KUtFakZ z#Ot#cf)N+EfMfK4zcDu8%!siuJwlLGpZ^7nF)UC+Z{lYl<`gCtW>|BWSm|5vUmKf< zO@|e8%S||&B2p>zujmVIml-0b$;*2PZ)pf=`(-p;vA{C4^ zbg7glPp%}^If$bg#&GmK?c-1^wkEof2DT<7MxKE8rNx$o8RsEf+y-06u;`7&2@R^+ zQb>ukg|rQ)XxSwyEl_S#NK}vx;eIx_I)+mol0s<%O!Q$J6V1`4(gUU(LZv@N zvU2p(F@_ucXpVgt%xVMa*oR`AF&>=XkK(qyVA3eTh53b;b)d5z9FBgH8^J6%_$KQT zMvg*6WB1r4YHAAdsQM$RkK&JzA(=AMVs&w5W~QvTuvnc5+-O!YjG`N)Gd5&hz5%t7Q!Y(4oNDDxqD9!14Pu|6D&Rpr!j zbu36=bW_=}0Fy_De`}LZkx!FPt-rrxS0zb~RY%8;O41>Yh^bVzb~Uy8J65XNg->)A zZS|xtX1PcOeo@Y`+5rk={X>F%TEevb{j|h{#B9tj-rdKsg!Mv3 zHtmnRU-_rEiTA%x{QkFJJj7>lj$DX~u0T|990T@84?`(0DlXaF@Gd$~1^nAk^p=Ra zRFAt3RzqnmAzitJb3q(&iMt?N80w1E-0r3Y{Lgvt>cUNjB)|7J{wDwOn@xIbZ1g`y z+oaFDVzfCc4;yY%NhdXuuFI;}M3cyP6D{&7>2IRVXHcFpMaKYtVbC$98)+c35=nX^ z-h9g(wQY+^o@~V9C&PAeUddC8MQl?RGn8>#bZ(IzW!P@fd687H&WO9VwvJO?XCzy5 zOeId4oSbZ}>{Q0_Ugh}<>twRB#S4*DF{0mOqj_d}B^$k*2hvMmI7C0&2p zC~iYHQQWp^w24G)=o05tcz@!xv?R*gmXJ-Mn~}2zUXx2Gq-V@%gp^bXJA+x2ZhY|_ zCblkN1B^Bo%0uwJP^1DN*Z_|;usPWmkrel^-6*bVLrp!O!tr|4BN#?8&WL&xwOdB2 zV;2_DN9!@`f;~z=?0>A&$ld#$dh?h%rXP32+?D|H3e%p#emW zh6#Z-2k1P>J0R72$Q$w3Bwbd8H!Bm|7Mp5WGLdRI$QX_}?-I!*3fQP5EavDH5}9ML zt^_P|&Z&rAm7PH(Vlm{XgNb8o>2Z*P937^A!!;=of$G)Pe?Q5?fiH?qq_VzyZ z9Np=y=h4Q&D4&&zR7M4B14|bAK9`P0&mW^)mY?FLS$H{iNEpy{pM4Hy=x{eMGZFo( z7!8=YVrBqpmx&;L3JZp-pT4UVdnSS`f`nN@&sg8&wGbM--2ilhzFzBzl&hYfp0TTe zvA&g65QY}$I1vm`J^j}r%*rAG3W0Qkg;`M0YYP<6v;UOU%kTUq|4oRCD;>g|7b{mJ zK1gY3wOXmCUdw(`Uc5YcCIDb?jdB~r(3#r&FEL;j?>Wzhk1$qR|XgQ-e3gjDNxZzYFzbKcX zlb(KZxWuhs0d(`)TBq7_C$4ehF-PsfYHd+YId&{L9dMLl#G$IKEW#oMAr<K#7Wr%WO5fNL#AF8MQ+^790+{s@$Hc+-~TOs@#fgI65Fb z@~U#}^6Z2jad5~(Kbr%o5AZjzPjMhkl9son^ zPg>gcJ1%mQz~^rhv+WZT(-IQw-zI!YOh|Z}egAEuV2FPI_6LMiU>W`6|MlC?_jK+C zR5wGl7SXno$6tzLw6e9U2vt@frp`vedQs14I)RP zW#z~jef)816+<+n;BlX z(9fGUZ(?nxW^AB206CA3Mx_e((nlV}n@6U{o5!aMA1RV6i{?s5k9c!lvBX15L~dOx zx3(ZEBDdnU7L}1GZ(EF1Zt;L4E+c~;D&ywqHhC*KBUu@j0hW2(X>zA>zH>gV(;7su zc{n+MJfDBT0TtAzSa;$8=F$0i3_Ln{u`OT=Igbh(weFNbr3gXpB68>|~Vn1iJw{9ukm$D9<4<*Itzox{I@Ghf3G zzVsgS?(FQ0?RfT%9bmoU3BG9WIoe_$OO#P=Q=y-o^K+>rc9c;Z^}VBFxFG-?Fjbnk ziUL++$qr#Wg0|Q-BfwTxwA`XnBVsDhA=)Rp2}cb##Maax z1k3$xh{$S~F50H$VMmBo>X0Sb5Q10TV9P>JF{n1*-!}YEi>*mY+a>7? z)V19(PqdLMRoWWwD(Z(p6-8nNi1Yp*QK*f=LUxT_n&uaRW=?l>kR{2Cexzpm=?Qfv6VNYA3-_`}P!wj$ zF4SO|BN=-k%Mxi7m`5{Z8%!9@k`*k-)#?SbsK{`lz)9vL&)HZIEwO5zZpcY!6nuu{ zD6i$y})Q#sB}qzm6q;y z@W3RrDDYq1)zv+a9GGvw$I;#00!h)`lIxPIu*FEN za~t{raunu`$f@)r(Gp9Ng7IHdvGLcC9P#3G{M1xD8e_LeQf$TBZbe3BfN35XzeqX* zeDk8}p51dYs(4soQ!~vV?XQxCK^mMpOn%nz>NBa zMKpu`?c_WZ&|QZdwc-0KO5D2WTt(VfTjz>LBw>d8zFcs|bA(SZras2-!B!5_vN#{wLw~aK%o=;`e=%|0?R*xHg5v zMW2YH2!rUAV44dnskByk#*hy9HMF;|j!KJ^750o`!*X?10~7~hDzJUJqLBBaZO$7@@J~{qPmCXFYPDOOMt*R=U;Nz%V zZSq&KWuqO;FUJo5_O423jMmEbN)e3hf~~aGF*kR$sK}Y6hjU9;H+x3ATAW*oirNv1 zEy5C|vc;uEp=jv=arK%r%&1TY3~w#0H-4xPbDUPx}Sjk2|yh0?!RLi*xQ8rZ%J;rxvhy${P%P@ z3~_-J(9Jtjg2;h-2v4P(Kvn2`mu@<1A@H!-4U0pjDwHEI!>tsb(Fqe2=3SX{OSBDDG!_r zCE-EmjZn^Zy7|KB!*e5ch`yx9FJ8i~u_=K#M(`S;Nl;?H`P11*=uf;xdC6TWp==p_ zxw%Q|BLX%KuLoh1Bsw^cayE(O$-slKvk0Kg!`MZ-|A3FNJ0YPFAxVue!Ts7DIN9H4 z*+Dm91HMu06?Ko=-;Z)1XvUa>cPdfzb>bei+bn>i>+b8ibP7w1^^-Ee09!BPE|r+T z!1chGvg6~x-M}Pa6(zmVfLLrm*~w5$5_lU8Cy1jYaWVyQ6&yEkZ)^jd^bR1c(SYH` zkT;?#6QaM(JGEqp>0e099v+r>50(PNkqYwYTuDjlp=Ih|9lJ^AN`d(jD&%ku1+s^A zB|P^y5U|lXpuy(qzI?%*7;B8xg^zKY%E6ZdOg(nO$G*;mAMlK$Hypa-0DBegjU5#W z60uT&Ut{O%`MFdX+dIm2v7DGigGxQ@9FKdsICgZj_v>RlKRpZ7#Rnl%VxYrmnOOyK z8N8oVM03``jP2@POn zp>Jk^`V)*kNNxDh-x{0nutO|gfF4D;o1Umj(YHXY3jIsQuzLQUZ}MON`I)v)^uzW^ zohc9(OyWQc1y5WFgU1!19B5Esnehxna}>qiazAVhGr+FIf`xR)EtV>MPqnu-+)W?C z-q0G2mggI{x}vt48ot*sWt4NWT2dRZkPbhsX?uDTGOPg&Yd4 z8*Xd^_UlH(R&Gc~w2AT*o?yVLu}r7AAC^1m>6sL4R+jOaWmq`+Z|E%hLLZ8uMsVYJ zAy}giIi8Xa6h|;mB08)=)Fx zyfBNj2H2q}YAY_5eU@#gvx+xFaT}%?3o><9rtCBK!)`Ed^g<@jQ9EUU14iNlJCx7m zIZg^QQCaDu zBE_Ks|EnGAl}e;gDna5IXoRb z&MmpskQ*(lsH<1F|AE;=tNb8f{WW+VCbl* zs-oW>%sO%_Dil@7sW?lOW4gh=_ur<$IQs5B;wpdm#luhs zv2yz>TtXNc*z(}Pmsfn8m&(xGJjB)7L)sCz32--maF=!t7#|2b<=Pzy16k2EolC+H zXbSdNdT>t~U$57stGw_w2u~(6J?{n(e>ZRPFTdIRC`#LYgs0;b4?8$0OW(0m(Kg@M z%8?GxsQHn3^h_+t4fDuv7-U47zxkFfb<;=kP-BMs>5n7?n7J|@nb*ig+sH^|Bw*%{ z86a@PD>+Q9u2iV9kDj*6rJ=&r$(VmBSz0hK;#&K+~_>j(KlFSbarw-JqeId=8T47 z#^Hrx6{FBlU^u)sITZUcEFywL#u*-LhE0ZrA^gg&QTA{M=4iyE?*m@|%)=%p!5Z~_ z5D|eHNAo1RM_G65(s%PflnFxFz<`yDqhE&T{aOBnDWcY~iRI z9OQobL-BCVvZMrMZF2{fBrE3Xuul1c@rLDC-Pqh%-P{*?DtvJ4U~CLI6^49`^^(xw zBKk4jpX}84j*T7EdxnFp(L0J6NBsd*$2pHU&gmWuj<4`&aCWwy&Y(yIyit_7jrMw; ze?`m6V^0whH3RoMNZ*gH?H0yhaOqni;USt(49v`o>0%xP7bu7rC_2&OT9hDEsxb#c zf`Mo$t1HgJUSZGyH;0zz8GSl0>D99psZwQy)3Hz3JSx6!nQg9J zxwDO;l%D%kBqN9R$t`J)70pvkO~?~E0*M3gauOIAcc8Jpjj`t}L{RUhg-euUvBSJe zNx4BMTbuT#jXPs8=m3J0w8n6wy+i;HW)_*V^rF#FkO_H=9gjuf4tYH{;R-$sft#+kc=%-t#(!VH?^*k)@Z79tn_bp^mpuPCxy|$4fS2^ zDuEeg3n{iLq&HY=DIJIM_IIY?oU=0H8$>a0sk^{h)b9=No$pkxIFD+)TN zP+*l(fzlMg`%>7s*ma{|rK(y%H=;DQ2R71*+#X=g^WInYKy_5SC#_+Z?T~Gk=aAqq zokxlTdzVjim?kiz5^5B@uO8MKBr`a$f&oYxPP4n?Z9=wvVp`&-clQ(DeRn_Y?ft|w zQQUU_*I%TOKNBWAJnov?MP<7Pj~eQ-pC}un*9ZqDeU#@e_cb>UsrF1bAl)Qj7A~J2 zx(oBYyDPlNd+1`J(#6|N!eb!n;laKJiAJvLjsH7*lRy9VBNxQ}XcPNt#wIpeVvO2| zdirlzwM4&}vavaewlTLcH%|v>)ZBa~nwwJ0qu)dyk>-ed#&VAvof@RyI?^GjgL8){b1OwQ zB^Ag~j>~c|rIa2G3i=mkWe=HVj{evZk{V-k;S$;WOZY+f*uj@CW84{g@RbBe@7R}} zvEJUX@KIE_dGdzkfE()3SYO{OF-ASbaN{eFG0yAJpu#7ZJ^J|Z4%<9NVfpx4|5%th zvEP7Q;;_4T~*L*(RDKzC*BX+5DQer8t9{fo?F*o2{9JMD!<#C z{I_SiK*}?zm!6~p7pKrY92|dTj|vRwlRm6dYB~Nk$Z%Ut8x}1$AtX3L zQtrNnNrh+tHGilX){yvU8|D;B3d|U7B9H>$C?*(f!#A26RMSIz3^(396os+NV&T#+ z`c+ay$BOWZV#4vTKmYQwpM$U;m;0Cb{7tZEbGsyU(J$gDrsLxuO^fsM3t`u|G|l{I z`d_|^i%EQ_Ax#R43)L5Db#WHUianWHR+uF~qghNcm*r%BF8r*PtD!gK@`BGKG_u&v zp~k``N9F|ohI|7K4!M(PQE_5ZP={y~KblM>;&B30&f%Tf6 zws%O&`;^u*ojBb?;v*q3y9Xq5`vm)U{0-YEdSsDOd3QhIep-UyZ6uQV_~BDp;;%m= z8tNu|i)+$2e@)jm!Yw0fVgcdCRPMFE0>A}|(@m@**c0htsKa{l%kN1xNFFE{u3^!@ z)vJaa$-H{g35SlDkBKTo_R%tg)M;twu$oC2Y>w{fe|)e* z_Z^7XA2?}_3Oj-LpgR&8D2#=~SQo`A%>p?J8;7WEbAJ>SL+ay%ts9R{%3vPF0ZEO( zN4t-XnQz{JM}bK(CxI~yF=gO@9R~)S+)+4i8xzt#bzsy;daH%Y}#~xbFjg1{x?i>JTE(K%f(47MM#@G&K9iBU*J7Zt!$C%53 zkw(4ejyOb2788v6-mg0(Bu2k__JY9i)zfoSxHp(Ey7QR(Vnw+u5-fV0Hnot-OsrTc zSeXH|h464`_?elwA~MTzg4;NXS-AdwZ}LA@?)aeswV_2CMKC#} zvscfga0=@X?NyD)%)%@xAfsCJs9Z^Q(LNEC)zn;ZBv$f_KGib)OH=bigYgAhM)4h@ zt!dK;iP6T*Zdy*9@Un#|93(qZLfFG01{uSgO+o%ugZq>eL|^GFoTLvm5Ahh(4bm8w z>=#8hJ@Z5{+qk)5i2>!&$h(&1OI@McLsAoThSMp_`J^?FP?1_yf-f7F3q`R?;U4T8 zd*dWB3K{SP97kq8D~){MzxYfssSqSrvWnp>-OChF*<#ZRQ&X9|P!7(hEK9!kSzUYq zv{z;Uk}4Y)GWh}>ef|u)D0);jP~#@gQD4Zw+t5M25LQ7t1*sJzRGex>NOqyT00~&9 z0-pj@sQ47s7SyU#%mQmdXIm{RjSgT%oBV72QRPvP+ zkHwiHMfI$Z7^FQ&eB@Rss=DFma1r*6svh=mD0t41M_R)!TjAi)omY`fiUTGO1qh?r zyiOs(k?2t20M15_BKtkiUU>?PIkMl|18{EN(*udY3IqF7#AZ zk8Et-phx8!@+&x%4$4O4Oe~W{1>_6U4we`*@##~M=0}41r4*tALk@G281Y-MZ)}ms zm|tXR5s6BAB`b|MrR|~!#VRwD*sLg_G?G!+rnF`s$EkpwCnpQv#%bJ((_~CGIH^K% zBe@ehmQ&bFUsxN3f&kWO)H!m>z|j#Y2;0eWo~0R3&~lMR?SZV(}r)CO+>8TH0& zh&v;luICpdnLnAf zWdUcVzOk{siJ!5RpT1~;Gcoo9TI;ndXGZib^!4>kxWGgjOZdT80g<6+Vr+qm6f8wt zdD3AbfEhTzNWzb;r6`qmHM9D?-sHdjFGg&vTwRfdxVqBz>Bt*UAuGDs2c8qh=vsk2 zqpy#>8Uo0nLyY#_KDot&1BX{vHJZLBH7kH|APtKsEcPe}!LnxqE>gB}a3}q(mXFci z#`SG&0FAZ{x81gBZiho|;OJ;;ySX7%p}k?`D7G!b!w*S^aJZFMCJ!$kUa~W^Z8=5L z#&(WNo^&*daRv^CheeIbGWIK%myLjMV5!0|4u}o<9tV(vJsbHqQco<)j+=hwj?xQ) zP5bj3#XhBQd>jn(VwAb%XXO`u{746TmwSu@%g-z>F6KIwJ((%OD4%7rJ=tC+9TjJ( zGco7L%#>ZoGP8txR4ovra`|VxQU)Ap`QAlA&W3y=OCY`qkV(m*Z_GScddSN^!^2VQ z1OHC(gP0%;N`Gz^yIQV6zy$x+lmU5$5vHWS1G_Sud?fLfTN?Lr-xKU z?)z=9IP#|R^70f8-J~x#C5w10iH`RPc&=hPaT`eUJbTd0p)lTYAjKXD*dF^HI(mo5 z><^!^)3Ot?)83NE$iDv;6)I`;oj{MjOZdYt;#2oMT}SG@H8-8ZkuQPbW4{Nzh)4hY ziJw0BuW>c11hEqAZmFw8e71MHCp{mY0R-$v8&s#6&xd3wT+~b9Ha;T_Lc@8hf=Gn{6 zP@@-1fN{Judij!x=bZw`p{uAj@&fVeB`h4Ln+!7F#Lb77Be0J~EG&e06nK_cW)RP&=p>!?$SiaBwYdSZFRvCGH1d{ai^qXA2nn3$8Y zp_3S7Q;<_RIi`c-<732TlK|dm2podA;E6`S3G~JU5-MD8;JCC-m_!G;kls?{Vo^~a&NdDqI|e1MC72&V=XDRgv>y0+2!dN?#N2RY|S z$Ec@Nm@?{#)yh{)IKb&RKOfy0CB*?42l`e>q3F3Xy3^R$I0%7MGh^4g>Q*Z=KLf!I z7v*PW27Wxf5Q7S?G0_(T1S?}>Jy#Py5-)m;-a_*`K%ssHW>)w&^_j=vXRK#nre^{* zVP-6qtoZ4_=23`V5UCIY#8N=tFf*_~eVcKR#&4T@8SQ-(Dthvl(`Hsu!3Q8cUc zLw(%R+2xew;o;kqIojo7d(qtdOBK^*MlSia zU-C``9?|~BkK+Px&Jd2m#_`MX{9x1ZeA7$rPXT7MpQjvPeqH8&G#$^xvZdHm&HI); z(~p_?g{EqC7DBV8nI8*HGmGUL#l@dx>MXfhUI-^g@gB&c#j=GxxlFFklx?VUfZb4| zzAbA(#-LG|dV%?(IXO8CBsB^O)E5H##e{E4s>(FDT*mh6|>V`vJ zPa5(nioCoYhv_s2g#)%KpYpar7R|HY-cGPjn9j4$eg`5)!aJm4aR~mG{qzrM*sr9$ z`!0>1$$tCplh~=;|HZ?N8%!nSCBweOMbl8!mEH5uQ71&@DV~*91RjRsdb`HG?h??# zaLrAMaB$F6d`Ms8=>d1L3Y0iwCp@IKG&gCd;K7Bh_;1|U+91`w8%l+1f8THNXB*+< zc=aZoj$YZkc@u4eBDd(+Da2Fghy=(Ro@khx#~#f@b9V}znSxW}NQeC*4RS33&Upl~ z7$12Qsf@IZRN5Y8*fPvqsqEwp$|A~RmDje)3|r+q{2Xz%8J)>n8P^$cBsSvWGPb}s z|5Il&tQ<<^pRD7aC&wkDt^Slb9Gx$$=jXvY548?`{y|siW`t<0XoTJD1m=7RVcF-T zIY!QO*e|1>UY<&`4?#SC8S$Uc9-*OLXGZXFEO~`S{KQj^rOBlSn_>S6i)h#fX}mG; zVDrJ*gRp@IXja)5o(@tVzNA6+eZwXh_=QrHeE`2Ad_gz&MUh)GdR3yD-621Ag+tW6 zd3OKGvXdwQIA;jQdRfegRIPIIDDV-c88I>ITu47uHWYY_ zwe)SwX@Eju*f>zjbQV=94 zb_8w%o%9lsWAt%7XH#(ge1}ig*Prv9w2&}ywPFsJi7U9|t_CI|DZ)8YV+(jA446r3 z%=|3mTU{CRDI6QTP%*G@)i*O{yG4+zgbXFsU;*pJYwS#nt*rE|*rmaVSyv1!(8^|p z0Ywmq=zxYlN$I&u|!$HK82LW?TXZ*vT`MSq%}PFKvk@U)2}sGHKZ-Bp28+70xKak8YEP1 zajbdT_LT8oBtULLhOtlKwF(4C2&28)La{Cc^@Ioas>#~^i1(qrOdlKZ_RBft;S9DKJ=d%h6iwi%? zGBN8Yl&SY*g&>T|)En|l!Q99xkki55=LNZpXB}Cbll?3&fOA7Gc;hRWx{hYp!`?oR>)dzFrbrY>wxy8Lql1I*vwvDL8^ zq1d8U5*!_>*(TMhu!yQ0yNVoDUD&a7bvZUMbEB0W18csT>nK`OW{W`JSat5ICf(73 zs@Pd)7&sI?U36FEf)Y2Wq+U^74Fd;89NjI|2(47x+0EMZV9p`puy#En75lyl4Y4Fh zw%?*cp_o%)I$V>cS@0hQtKIu+iV5(?R2iMu*#!`3%e(NAYxb z6iK(~5_2@3l!m$ObtD(F#VeUFNkx zo$pk3{s{qDtW{oYvCaVLE16GNKkr1jO6TS&DG_c_`M_?CQ==CnVrvq*xy1XF(6f;b z*rtSjFw!wcUoMRpdC^NrT6_o{31y%;iH$P^V+C&%ov}E*84OVM%5*51?FSVE<%5f^Q7$!?ddrzyjdkK>jrl zF4Cl-iJ_#S6RxHwu~9k^Fo9+_@6v`;u4(fYO|gR$hj55;PyL~{C59a(bxa=xYlDXx zC5P~997-k)Ua*wZ)rDJfZ7jwbbK#bvMCH(u4(CM0$`@cq!^ghN0ct)s1_%cXqVR}% zj`fatdY+G=DR$?ecjqft(<6v-j)MAgj4ePF-GP|{(~BLhazk^9XZ^V+4?Eb;L63XS z>*43{97Tk~%1j6by&wZKGkpUrCH%~+^w^LAcc+QIv8yrHv4NdsruW+FHDBBHNh2^- zLtoFt%7hC|q{}~VM((yK#t;njhcHnDu?{ida4c8383#=YU zSdHJ~P5uXq2@EnMIl&VR-4Cj@uf>sqR$qlVW%Nk=iq)k)Ph8af)+@Tsk z%td1?dq%ZFbKIn~wLL@(wvDtyiU#WxoaN;SBhDX+07^ffB+YS&=?3z#%fk|m6h~OE z1cUq4Ki=4w|L|k~c<>E$$1)&bg&21%?-gRTauYm$8JusrM}p$xIJ_K%{rQ5)k>5`; zqnQ02rlLL8w0KWwj?YNR!oH!-{7lkACO4JKGBYnoO2{`dIf4RtQ6OKCXMUE;HsrDk zxx7FoyU1eFxg5ArSys6$hxuOe0;e2#K|#6vGf9qB=nge0jvN&LVCn@-I%-#aY8QM8 zd~$Mfd{+IjUg^jw??7DEzg$)6uWBlSdqYPXt!37zqyMU7M=RSmTANlKd8~mpH(s%z zy^X){f?O<8VBIJJbkw?*nyOdPdSY%A=#LI2|NHn2tQvU7D?l!^)mWK$G*?_uD8-bhS* zk9PX(Jnm)#WOUjgjkE<@H{K`U_EVaDS|Xb15np+iXrIQ7ZVB)1rzO12PE7ce_J^PO zYRRAQ1(`|^xYxwTZZ|jfJMzbSQIyK=0#P|~XLU5ZoBd0p0(`E&Gd{TKT0|riIR4yl zO=o}L8WVvIcauB~ZW4EsT@Qx%kbm!g-lX&ugCi=jHtErCNL#RV<0zVMr);L8 zqme`rC2mv9_lk@+pNUM5kBmIpGB>}DpE5UJjEpp&T7-pkachypNBp&U?bhO! z?KSGzY~$u3G3Y=!ugsVS4~&T%acmjgO18!KwRI;tVz;d0GOn>uv1Zo>+#I4U7F(3( z)-T}nIK^DU`jkB#r_YU^hbBvojpw08KZ)YD5jyemWvJ09iILNnKZ)ta(h@ABBb(ri zhMo$y=;nt_))*09BR_?{9EOF! z;IC#3Jffoh%uOo$QFQCRzwW*s73J;=g@F<`;oLw=Y*bVk$&g)l_mi@9_eblGq8_cU zW2Ujb3n&L#RgOt(?5^(yo-m4|Y`vk3XB-Xd4N1E($BgHI%pkE5b1b$f#{okP0Woyf zfI-KEpq%r}!#gQSil~%&H>6Iark;o<6)vYgEJdFiGP2%G7%XJ~*x&)H5lbF&43auI>FdfUs+gyYu?Ll4z#y1!W;pV9cT+~QsLXL^%!`-u^~nu^`f|~o=%M7`VEBu9H*5) zd16Ij!ubx@Adxq5m7Er?ekOVr`W9yTCVqa#epb4I)*v2bNb11?dk_O-2^(tg8o~fe z4Kw6Pcml!m39g65-{>17?rO#FK%dl!;E9?TVp@2UEecqDlp9(IKABEH-LH8g>Q8x9Cz?VUfX$6AjFwY}Wv9 zl$+Z^uB6n;6^0$!kf&F-D5nSf{Pwo(R-tfFZ8)ROzGFAJiG;n zgI+((1u2@#HjFS*+8VjVZMY5Q4Z2Cuuq;UtJ`Mr|1P#m()GP&?^WaVJ`@ws3@F3;J3$Z&GnfO*47 z2oE(@EO3&loEC(M6w{1a5JfLm%Y6!J3kqt>S64e$RX);J4vCKf|Jo+dzkFJY{C%35 zRQ{Ef9gek*{zXOpQcvt^5jyHgaul_)>HvhJz1^{?wF}#mN}xDefZpJ$*vi>l=OSn4 z)h^N--IdOuj5?FzAVtyL#XxiHQ54nC8@ZfXv2*D`Ra-6zj-D1$9}4>E>8bANmP%w{ z3MCn$sItRcqk<#_DG>*H89lVyriVRw-6TSKw)42!ZTo!%xEx@AL2)FKl9+~^AZd{Y z^oHLW#{l!Bn4RMoVpKFqd!Ht_zVJBEy^H|db zF?4ZI$)n(QPl95Jgs7XYf^|*eT)9b64M76GcUOqUS77116hLorwKlwKDiIfU5gie( z%Kv$jx;OcY%`2Pe8KA%DCK^pD-(cr3w=qwjngL(q8+WOsLu7C%>=ewRygk7lC7zw6 zN5Vu(FOQ4_=IiKMnGqiu7q3)aU&miB+DczrJjzf;ikZf|=yFpkk&R8ps^mJO)7n~j zJ(2S-;Bq8CSH3`j3VLH-aIZ?IwXky-StF#fgfz}(*y7p*D-`^^Ks55WEkgMlMz5;gS$VZKoVzEQsWqL1D^tQp;H?kC)( zf}b$nuB@|y9QaY#HcrY;)*l5v;yB7K*SIyra6ARI zDoII0fiVpY^f2J~I3Vx@@sx&>i6jwHnJ8sLM`;qbxTOv?2&g&t(;u>(10fZ3w^<&R zOh9TtWLSEa06fZ#^CUA+;Z}l<6*g^%e99a$u;GV?W0rMubtQ8phlh2CJGvIx9oR-u zAPc!MwiE6-*L&bOHa14mBK&*@J+TL#o?~BkdO5tZb6&s0B4czE29DnPQP?*|J<&xE z^oH=_AZiuFg9)yPy_N#Au^t<>?9*D^G>?c*6XR8ot#)erTVv&=8rGHUwm)>e$<-At_IxFEsFL z@OS>iRoZ|yM@j|J*x}2EJny(oVfqGO9B_(qk;)CDMu#CXhKGljmzQ;14val? z74?mRZ7y}Q1p~stD05D%;7or$361kCmE*bWy4}6zYyn`C;Aa2(tfol ztlu-GBjC=3jl)!K`dL<-MOs7tS)P@tmdi8cGBCh^8qM6uDv)Jn%GCvGiOYc^6*+jL z&?n{PIZohr+`-McNu@yaso+dbc{vJXeQN33zXJi;T2UQ~MN1Q%tW^20`d4x*{c1<6 zf2FF`zstV^d~;Q+qkpTvqp)$XYm|gYM?1<@nz|g(-iBkKM%yb%dbG5(w05;}hZ}t< zbaZp9R$69VW?NdET|_R$rG=YRs`(a#bC=vIg-dmd!Ueny;J$i9SFBjHz*MTJzFV>M zR4aJip>U{h=-zhNMn5dzUw5w2>@yXfUiQ0e`@Fn_chm30*aJ_c zCD>2%gM`GyH2Ze}p)g+0s7y&fS?q6r5ud2!TI_v!GzyGZZEU!@qLF|L0Bq*Eji> z-!S6%kx`@3>9M3bqN8JDVHj-E1~V(3>aJ3F{p#XMg0sX&cvMJkzd zj?7AAJOZ*x!4}1w1F;n4BF?ekxLzdf(HWPa%!teAoVT5iOSX=)PL9ivT%&)oR>q+) z_D|N&lj(eNGOG+OihV(o@gsg9_qX5?gH9_1nxQA;nC(yw!Wn*=vsXLrZC`y)&_9tqSa zIOj1;(;?EWH_S4hRRszc{giGlO#_5E_=@mL&o` zD!O6?gLIC*#}2{|MLH$?@L-HY2mOk|dOE@rY~z65`06=&0HFC-&jWPE_JYFk6>-<| z-qHHe$GyF9YCP5GEm0g@_UlUhjX;=lbS{{x*VSI`-P>~$p|8-3zeEAl8hN5`|3yJz}Vv|0=} zNNzmqLn!6;i7ule#vGcOKF+ORvGVk)W<^`m$L(z)S|nDqkQOx@R^d98kgJfITXfam zYBV9*kPxj_BTSGXpd89!%<;JdO2PEp>iAPTDjY(u5nocM$J zMQ8m1J8#W}VYIc#(YdJ-p_Ek=#WIM4lPb=+T`jGU8w%)+qORH8E`^J;OSQ90?rgP- zb1px?js>$07dw|CyXqc0yIDm~_pD2K2Q}4*m*_Tw!*=_0zmU@gi?BVzk)a{b}9+>Y3~vl!@<7MAKtx7 z%TBzXaQ|(>{j@*)EWlLKXLC`dPFK-2wmu3&gBx!L1Uy1yJ>AwMjyGRpC8m|`A%}~a5N@On*AJ37F& z!0f5DXpe=Qcwv-m)HxE$7!Hwn9U19lp7~27qxq2$gkX(yQ;rdS$)?xRsh8JM=&7J> ze4sm$9xuJTUIJ>4^4PP@FdT^qN7%pvv0Gv9=z|D=N4YV!8RCN!#sh3lBAN$a7@Y*r zJk0mR*Z0pLfBA;F(=C!J?gPzH&62usci(Mp9%yE{K`I1Oj@=mdDD``D@48kaZLqjLWWetMA(ZJm*bR0m(=#wRdA)p)FOg|*8R1i~{ zAYBqrDuCvP-q0PQojw&il~fST8N*mzp|QVu>loT%FX`Mgjv~ z(!r9$!2>7|tW+$=pghLL#@JGNaQLN8=nb}s?vS_`V+RMXQ0fJA!xO2K15d_?A_rT) zb3Q5=H6TFn^O&D~{Fs|ocItaMx5EC7^ZNRoQJk%R?0GKG7if96(ua3~F3~2yg~R2^ zFfUj*{jkU|Hjsu1`g;2M4D<}bRo+Tp|FzhHxYD1}zhQ+KtO3aheJe9eF^u(?I37gL z82I^FnTWlLXqCk-#?^}59#9WfCi*6>t{gAnvo6M{)xbwYcTLYAol5# zr%$w+CtRw6jmb*LE$NIWBrmQ)ZZ$B4YT86l7I78e&xc8l0LgJfr-!v|OyRhJL3EfT z27M2coEX-$i9yFOTpRs(=H^Djo%Aq`rZ8hPW%*Lm*e|uJkfdn5M5PMPGlt<7z3k^_ z`x`}By(o}v3|?*|rE#;2LB}#ljgO=-J_0^kD8*Gw$MI#F*_huCzFno=!eUG{rlz6}ct%NSbchBOd^dHdnpCYFO_lyC$4b=G7d1Jm{8v{+Cp}D~?B#HD z>?+C?Fb?Nd=iK)8_99fOIJdWB!P3&@T$I}m7e{WF3#J^o-7PIHT>zRZoM&BDG3&^! zc6sm8)9vEY<1$-qH|xUM!p^SBg$rVtI{MzOyQ(UeDWmkTqN*y-VVl0Y?eZ$N^STuk zb_m60^Crd4VLPGceVVja`G4Cxvyiy+HT_G<^_1lj#qvU<5MvP6(O}fVGHNQ8q;QK` z?uz?{;sUNiP*HFJ7hIyo8W)sSQE$X9j8z^L12H6uxtLZ-gg)IpePI57=4yuNt9hUI z`zvQ)nBmZK+hwff_ba=9clFf!KF{Y-s**jF9xq9RoMq(gaeo^YD}{M`Kb&Eul9vIa z=zEX7j68TqGyaTsyv+D}j5vP57jTfc{RB&szHbBN&DKeqyYJ~tgOIArd_@>kd_LYq zH(^%big!OtK?v_0Htx0ockpjEKuTE+PL^&6thle30pP5~E6L?&f>80tMgI4T^!@d1 zCfCMtTkJnP|NbA35RJ`zi<5{+Ho@@{ zR*vOtM^v~W8H=z=wl)dc!)*})g^e4#ZUBqA_2MTxJE=Fj7v(B$y)LnEZ7im`^k!e4 z_DXvd7Z^mjOXak;bCI9cx%76%bZ(z+BO2S;>kLFY)y&VNQkb|pT38$OOl>g~sGKocFIpEt0G#=qe* zw>QGqalm7a4{y|u;+e&v7HH8R^}&exKr(WS(BB`RQstpKaRf%u_J;=}ToOCN!Xt46 zGY&?ARVHpeJZX=3s2SOeIEXmeL@H&oJ%X!M#Kc3R(S%1-R_>#xZBwH=X38ijjY<^9 z?vuzE(~-*1jkS#FFz7&mY}hgS=#P)bTG%x@gKY|lj(`Bjj+p?Fik(SMPoFvDSLvfL ziqfIca(GBboA3_9C}tec7ft0PMh-FS7zNv$9Ua)Vn4F}Ehr!8UFgV3E347_N!7$n+ zc{ZA+42C!({g~nmCZoYPWg5hwV+ym4kArc>!LMIMCp}D~FmDV#c`|upocze5qS@a# zIXL-|D^(^ZjZX%j45DLY5-tv-fN<~w+G3p*&dmL`2HnEyJ1c8DYY`BU3D%a(+S)Rp zppSW(%-&vMFPF*f>`+@GvkI1ReTl-yN*-(_vnPci3l8Sb{O^Pt6n(Mwa9{)n%jIm& z;BFPUvmAC0J5--o$>fkDK7flxip;Aui3jGMiz&tb=_3E%KVJy_0J(7?s@aAvggNv= zN~s8q0kwhHEF7d}!RS~szj{V*c=n3)1&pH?U~!m5HU*5)1_3t*(R`<2XKjcSM+(|t z@oxu_*b5RI0>68;gJk9P=5*4}c9Et!P%_(xo`SOK*9q*)+ zik_)odWTO}dcEsbZi(v_JRJIxEi}UBM&*+1$k_r`^b=SdOSxcyL2KxXd6}~1rT22p z*XQR}k>ZHroXV0{ls?MKD}RYIDlm04MgcR5TX9cSNMmDFRZnAaV==g2A^DAg>y3dZ zjqNE7tXU5sebE)zw!RM85mM?YQYu|-7;1ERmaf;-c-Hig0KvqgZ=t5E0EZXoMMs~f z_kw3BSR4gi5FFl~UET|Q9RkY%{8xSdf(lU;AUK%9;r-x2eP13U@@mxAt4M38 zRP`OIT2+0$yZeLwT5v}pL#hkhRRc(18fe(5=Et)Ltktj@+xNoDwQ&?IzySq zXEO5UxXvwultdxPj|{GmeV_6Ej8`aVsZSPI4iXy~bey!t%jAqd{oP;hiB?kKa1=3& zy9P6kYS>7{9!ODh+hbNh-#gC8TX?)gd|+BVWN z3yhQD6gGFoX`;QIKjNiY)juxszhC6f|8)O(CR$>@hwAu#_6SIG3^y`)wvox^jTf`9 zkUmd%i@N$<#{~F8XGMz&*SBHW;)dEbtu~fkXk)dpNeM~I;C?M8xLsnhvCMJd&BZOrB?*18XjE}Iy^KxbaR+y+boO@EA)}Joi;{YhA9bnQFmv?QZKtwN z)V59(v++{p^TbLWCsG)}u`PK;xzdfF;+?{)F@cJ=n3(yAn3#CM{rVg)wkj)+Np7eC z9VLYk&m;~}8p|maaL-9#92}_W<(rX5YIr!*Z`AGk*rmK7*^#JDBn@&96d%++(tZ#m z+@bC5!Y7I;$4EPC4OG(agEgAC2}j3%L_}glL_65#8e!y!Ac+C3(W0ruPi199gljsD$h_IwP83H(`V)2TK7L_I`A!|XLPsJGw<>gIN<%V(- z36MCWfwwAgCMS{`=%>dj#qjmUKh7vhQ#hqEh_S_#|IOE_DgQ|mX$^QdCQXxQgl*EGvwmAt{m~ zLBCjstgUhPikbbP&u~Qar|<)K9A-9>J_A}KCFOP}g?B0`%o@cH`coPj8lu_QVFnUx zr@?Hdd-im6Bfw%70ohvsH^u?m7*FdSXPv=KDrw!MFM0Co0KTmE!3#w=0~~ay9xsO6}n@uxH*My`Bfq(TU?c2T+9oV z#;PT@kQOg3H3sHqH3n8OgQE@Au~|$5D-CQc?&)f44C!iv&fr=VdQmFEu{}MWue0C| zeH|jmUraRb>C1ZEwXp8l=ebbO)q$f0HC;8LKZPF_`bk}II@a5J0E?FO{s$c@0o*9) zc;Jo9O1-MT@3o2@9jXFYNc&X<1-0(|3$^fvA{mQTm3pRu6}Z=`s#S$_23(E;u1cv@ z&YkV?k_8!+{=!0KA(kx40cEl>xv*N9F-L+UV*q1~y$r-tJm_^^9=kVC>E@A<@zNuM z)3F{NKP1zMA2K{%zV!IhFO~*V(Gw=T1{}>0Fii`ILaYhIryv+s>}#}l+VM&TgxezW%^OfKg>}uG!>r(yWBLAn0{OhmZ?%%)v zHuFWMXplvC<^Js3=l8$oq7+BRZ}5zM6Rj$^eV>5#lmy3R?W|U-W$c&sNUOzx-6OhR zPT*w<7t(V;_7Zimu}Miu39)WT*}Ys>&%u?X-Nn>w`bkR4&hCwkb;-_7We-QHOKNPL zOI=cLY+cDHMxvAl03LV&Ip%H$vAHENVXbVW@8m_Ca3=a#_gjF6N^DtveJb#6Ox+mc22mZ$tkJtKa~`%q-Cq6S;~WPj&H$^rXg zU&fG#zYa-2uM4wSrCi+YQ$N+qDMnj2&8g#>7!N z!`WC87o1dK_Xd_HtTD_Dw`g#af-9VfZRkdIECMQkZj77R$-z91>*#JyrbLqh+2K`6 zH?#+^8)-YCJER)MVImbd*Va|iA6TitINF*fR0TJvoTD*zRdmLpF!tKgCmXsd6bT&% zcw;pb(+pAwP~v;og3{S=))ZEanhe#*-fhoaUZsZ*thg~d7PjUwz%N*vw|ljtbo zC5r%ZfCBc3bjLe~cL<-jx^C&q%6O#$PsbKdGOi#s^il6}qOSD%Vvsqia_Qg|@T6XJ z-Yd6QUknEagUpN3OV2O!t3F|s;#Jk?1(ngb6xdVT(->IHJ@t(}J%NqfsZxV z;y|%g< zm@gGKtT2p2Ra;Akl=aGjJrW%%r3%`EfgD(~oGA_HDDkGa$`9gc=O#w062K+;g${NSFg*W%PBo9 zrlJ)#6~2znST>B-xwypec7@K{x^zyYF2!%f!)BAusCYCnn~RlVvLh z#vgdO(mVfQ#g=oiw)3PiVzwVo%ufLQ1+fu7K}SBr$)T2D9Hco`9-)t(qz0BL>TSzD z1#6arNAV*gf;0LkNUc_j+BRm5ayk~GL){*9keGPV-mc~`#t2D`6X*@TZMO6A5jJ(S zw`(3oRBA|F>?4Z8xmea1$i!mGA$saHm2_l3yd@k%jc^tkI?Y&^hE&M$K2j?fd5po! z5vH37n?XJ73?64hiHcaMgaI+COBbpF;S{7}4=XCl0|bBc(CIM1>Cg#K^MHybx{(TD z$3_#zAHa@|j+P(tDy7`O8l(J>&Nb2VDTs}!rY0wPGKGXnoRbp?jVW+AutT}QQf112 z>c-#13zR|3C2r`!IA|2i(aA}u3QRPFp@Si$z>mTv`n4IM73PnA^(QUzm8;$SnOE1RqS|f?d2440H3di?s{P zG%kdF6bhtaN7}BWV55Shs~M9Hct_D5%T!U49TyE|#8q~7cDiq)FK$zAqv`by{dO}D zEc!N@R7G^!j;L+x##Dpl#?B6pJi0N{K=?H+bQnXBaX|wM1#C1l4I39!wRLkxY})zi zb=qoZ+W9pUNZL8iIYftBEB(WJuCLF(3_m5YK~iINy7db@qphve=c~hDnGZvKJU#z1 z{l($DWV+KnbX^7HA?r%#RRfu&i2SxZ@gJy~2+zYY*sNMKeVlfG(DrUJRqlZ9tp zHGL>_d+mwdwo)*_gnZ7KB-m&gk;O$wuj+BZgRx5q|P#Q?h zVx#gvwZ2|DumJXk3ieUe0QM;=)q~Rd{{9EG5BeAS>j&Hi25JY~3;G`{6!e3^Q7fQd z)neAcMvi_`9FQB;dwcGMN>!msd4{wsdfWCOGN3;)=zQ{AwTCjf+M_xTRc(N5=Tw}XSJ7ZM@I>lZoA9ia=2-i;S&YXJW0$tI27AU_lxu@X>pOXM^bE( z+a>7c=xtjB0}T2DsPojMI<8cy>+C#b)Tm2mZ|Zj4DK;$IC|I#g-9|k2Gk3VHblTQo zywQ1jx_!EWB?@~*=TG_RyzTb^3>t;pFd2=8!MLfxskocLIQlWg z`G0I46b25U&Yw)7XujFM8Q@VQU!P1GpL}fQ8kLVNN6-ep-g`L`Vnoh z_Ht)CATgLePWL|Fv15}X9zMZnF^O~zW-CP`=>r9VCeQ>(opJ(&+fJM@#2}dfLx)@; zv$g}n3$a&w$xJGz8!AwOW%Q?!Z0%(Cg-q0^$eGJwEr?-Ok#cLUTLI=AYDHoFyD#$J zha}lBba7#Z(zp;rQ7l&^n7QN%WizR;lU{%TW?qvFqie5*BK zuXkYMxJ`?`28aWL4luwtC(Ec%(jBectD!rgJGThLqC%DvDXU_{A#p`pr9cWCqUWMZ zc2)9lw6+efLU9Op>G0F@VUiicA`W|gJ={87Gz>$>)1s%t(5ihZB0XAEu5zO2J{N)_2VvM8QJ;c4eHn1( zxGfb&z2kSl*%Gc*_Rk@-l7L9F7OX!Lfaad9yRZ&&>`JXV*2+a2)l~IgL zN?_X(qOnW{KoS2v}et>y3dSHC>HuJ#FjE|KiHFQXn^KYQQqjT5t0#m8xRf zpf_H_JnHR<=xf(&#(uFwl->l7DA?xSD(^n;*M0p93$Ino8SQvb@SwEdfvTfl#9>PZ z`c)59{rw<`cDR$yK>pRezTf>p0b;N)kdoM_Jv-}%jbp%FIZ&%qs;X<otvP+?UNO=$Og zB}#=ZpC5NrGZF^Evz*To-$^>!{6~6>-?3%UNv#oj-DPbBuM{%_fzh&30Q}c1zeLp^*Rs zsn*Rc_K4(1_98N}h{+};ArX5C^r(v)D%(&*PdD_bEdB*~6hvh?ky0l{94>rU2hqWC z*u~CN_Ho42^>RlnvM4Kcb)7M`i#+Psw%uN|MR(f@$6-&=RR8(&hsU;$V;+CDC6zH@ z`xx#GkiX(rFy=sW+XvpTd_YY0(Id4w{?VKG43NFmXoOP5rweA!(lCp{Jj?m_{M(?yONOL&XkLx zi~kIutO>sc1PBWeo@l%R0$t2T#99S)Dnrl}BA8-k%BaOSibEtb8hFcsg4mRX6ux_Q zySB!Q6|Qr8mSRDi!zUVT?q-^Kv{dNEMvk-wG{JU5W|+q%tob-wHCT3xw|0~8pwmEe ztTF<9TsSl6a9S(<6$=l{JVMjpBt7Q{OQ_W5hM0=jra*B#T@^Z`=*#NrQ=mrS-MB-X z7nQWaL&}kqA|ORurLb!&UQauGDJm)Z;_$BQok*qRkkrrv(~;|tQ>KS&G&jdV?@*$T z(tpayMN;J|YAYuyhweV*pexo@55q=OF^P;%`MhKi9*(FgRI20_S4Bl_0lrZzbVk)x zRu*G8;N7@GoL6NP7o#Y)iq%J82x75(qsJ;bvR(t3QQ9bB&FQWN#L>2{khYqx*BEKE ziC+4yQus(2$$_;>O-E^)X9o$7u6|GS(3g69mlg;R#{zpeR3tl83-ugYDS$_mbwsCB zKPft~;pZn|r|wz$N;zubchAyAX88cVXF*QjBN88u!c{4KN#~DeKlUYv{Q{S-!*gI` zfnae7n&f8il|H}kFj>-=z*n@W+1UJXk$=-g{`?OpS9$*8`}_Bg-ZFpm{#$OQe~wBO zk{byLnc$9Uh0aJgnq6iL2iLh}aHaK7*xj2OWdysELGwke^5ha05=+M z>8Do<>}WhR$RoAXR3FrS5RZvQ&;iUEi9zbbgG4+8p+zMq5l+&BkwhkO2oFahDpfYy z6Jg;HohqDIIoaFCZ))r1Q@(Nc*7 zY-O0P1v!E(39fFx{s zdHQI8QYGG zwE0Q1RU}ur!mb&~pWZS%y7?&V6r%jh8juYRoj6CbX*1Zz-pbw?0zsyrLy`8FB*2bg zExe-We6{mIY83+xurpxslv#Zz^T7Z@A^(p3p3XjU8OK#3<<|Bx_J)23>=!WRXk<}{ zIIRz>m*C%bk^c&ffwhTw=#`KdLqltpvBerm3gO-uGOvj$x1m>New*1s05glo$}|2C z2yduaa*ML^fYuP0bJVGrQ`S<<$ik+W*WeXJYGvo5VW+{&ngYv|TTa97m>KtVd(psR zq&qsLduIoE*%YpiB{jjcb6%%hkF$vb1hDbY9gI0b&Eu$1+38*#PUA9_>(%klR+1Q` zKw8In%rP7~&Ti4P)>WZ3fH~(7?KRw^!)zuU9)3!X)2nnlClT^ah>fSi!(W~X`UWeH zFXw=NO+#ap3=caD7dhmdv%1I;UQvf!2YtyoSGxgkPEw?D+S=ZtFJT!z|n%zzBbRA1$xrw`MRwO0;7Q49Nt|WeIzrwNNc<%>A~%7 z>u`@2c&k(oyn!$8P<1TS_b&|8FVu6j3do}i1DuBKFL1BzaK~1~y|(`WG)8s*o_oIv z_EEt(cUQXC0`pZ}f2MM;2ZbYVPnDEcYstODnNYwEM4z5;!oKz$({%P@-On98^YA! zYpERh<0Ai-i~RZ5=V(=V!ToJ7?$6%OMEyKQ9p9ilb{#V_9Ub4!!XcV4yF9ym^kP}N z>&W86ae4L<)KRzHUG%oOT^@n+1-X%+UEFosjfH(Qn*>O<@QT8_vAi3b-3vhyyGT+4 zN!VMdd7YA2k2}I8fixc_7N&mNNr%PGNMjY#{=~hv>%ih&kb&9r6*Yt zq()&13>zotY};qg=m^wCBI%FK&4cz$?xEN4GUY^+x9!8r5pi;?3D<0DnA5SjPXZ%c z$IU4koq(Lfy)kx7$^sOQmYEh!*qAPyMT-EA&diVq5sQ{|ouG@(0Q-eW zM^lB+8gPm-h2wD4slsUnE)793FHf&%s;D?@;?OHwI!vZRCqds}+}GjF6lo6wb2{jd zamp#~hAU+mXwI0gDbvkVoQbC!#wpSmV43@W9Q^1%>2GYF`UrX9Z@eM3fw&5qU7@FdJ*a%P}MZuXm^~Av*-5V@+D8 z(BBL(ZiC)+%XE%c=2y)2x>Ft8*zG@ishk3H_tY%8Nz|5p>$y9d_3(Mow3NQ@Kl3- zcUr;ZVB|Ts$BGx{JpLHwU5Z$^q;Y{8+EnN#tq8QQ=~kX;46lB9`fhsqnvtW^@1Cxb z)+lK$DJq&S`7%r*V){!_(Vc+k>>eX z>t3aeUh9k;ZCvu|A+3?sLuVU{(NrJOwq9Hu*o9d~2&oPXH5#+n$T>nLz{FLW=&Vbs*~ z(qsU%(L8=i2l~(QlF!f?Tez23SUB*Ko+{bA!RZs8XRu%6WwOT}ltx|#sSdQQ@QFYD za?j=to5_qS3xx_^1Dkt-yp4JOhQzb7VH!6ZC+|sEP3arG)Q~DToEv@;^oj!(zM{x% zgDsc*U0m`-Sb`m5_rPuk8?k5byLU(Jl>C%`T;$(&k-t2@|MotKj_+Y1%|taDCu1?@ z(7te#A}W}9(2*A;JJ?FPeCfEGkZ{R)mV{l$|c^%d(jw6a^p0W%^ST^p~`2hS@;EM zl2hBxUYAbR7(C|aoVc4}pf<%q`vU6xgY9;V?I~B)$86h9e296xGBFV|A3yOM+eSak z&-0vP;xSamBhniyqOwh(zCN(*cq2ts-W;f596fjwKZ3^C_#pJDjG!wvD3M*GBUrQW z3p#NUq{e)Mb1BdmT;TT50v&}SN#t`ZHTE?TC)^mT*^j6^A^B0sq1coCi0}x_No6G) zI4apQ3g+k-&pcWxE5pLW!cG{%!PKuYU`J7=qB|MW&FFNx6OtQ0A(|3KLPN*tlrct& zBBL?`5Lo#r@L%Ol0bPL!EduDj^*2b0Db8Xl*Z~!7XSbN07*qoM6N<$f+|ASrvLx| literal 0 HcmV?d00001 diff --git a/versioned_docs/version-2.x/about/01-RSM.md b/versioned_docs/version-2.x/about/01-RSM.md new file mode 100644 index 000000000..63820c100 --- /dev/null +++ b/versioned_docs/version-2.x/about/01-RSM.md @@ -0,0 +1,60 @@ +--- +title: Request Scene Model (RSM) +--- + +## What is the request scene model + +The request scenario model is based on the perspective of the client. It describes the abstract model of the client from triggering the request intent to receiving the request result. It consists of four stages: request timing, request behavior, request event, and response management. For example, when making a request, you often need to think about the following questions, + +1. When the request is made; +2. Whether to display the request status; +3. Do you need to retry the request on failure; +4. How to process the response data; +5. Do you need to encrypt the request parameters; +6. Whether to cache the frequently used response data; +7. How to operate data across pages; +8. How to process requests in a weak or disconnected network environment; +9. ... + +`fetch` or `axios` are often more focused on how to interact with the server, but we always need to deal with the above problems by ourselves. These functions that are beneficial to application performance and stability will always allow programmers to write low-maintenance sexual code. The request scene model is to abstract all links from preparing the request to processing the response data, so as to cover the model of the entire CS interaction life cycle from the perspective of the front end. `alova` is a library that requests scene models, it is a supplement to request libraries such as `axios`, not a substitute. + +> CS interaction: generally refers to data interaction between all client types and servers + +## Request scene model + +![RSM](/img/rsm-en.png) + +## request timing + +Describe when a request needs to be made, implemented with `useHook` in `alova`. + +- Initialize display data, such as just entering a certain interface or sub-interface; +- Human-computer interaction triggers CS interaction, and the request needs to be changed again, such as page turning, filtering, sorting, fuzzy search, etc.; +- Send requests in an anti-shake manner, avoid view data flickering, and reduce server pressure +- Preloading data, such as preloading the content of the next page in a page, predicting that the user clicks a button to pre-fetch data; +- To operate server data, it is necessary to issue a request for addition, deletion, modification and query, such as submitting data, deleting data, etc.; +- Synchronize server status, such as polling requests in scenarios where data changes rapidly, and re-pull data after operating a certain data; + +## Request Behavior + +Describes how to process the request, implemented as a Method abstraction in `alova`. + +- Placeholder request, when requesting, display loading, skeleton diagram, or real data used last time; +- Cache high-frequency responses, multiple execution requests will use fresh data; +- Multi-request serial and parallel; +- The retry mechanism of important interfaces reduces the probability of request failure caused by network instability; +- Submit silently. When you only care about submitting data, directly respond to the success event after submitting the request, and the background guarantees that the request is successful; +- Offline submission, the submitted data will be temporarily stored locally when offline, and then submitted after the network connection; + +## request event + +Indicates sending a request with request parameters and getting a response. `alova` can work with any request library or native solution such as `axios`, `fetch`, `XMLHttpRequest`. + +## Response management + +`alova` makes the response data stateful and manages it in a unified manner, refreshes the view data and operates the cache at the request level, avoids operations at the component level, and is more elegant and unified. + +- Remove the cached response data, which will be pulled from the server when the request is made again; +- Update the cached response data, which can update the response data at any location, which is very helpful for updating data across pages; +- Refresh the response data, which can re-refresh the response data at any position, and is also very helpful for updating data across pages; +- Customize the cache setting. When requesting batch data, you can manually set the cache for the batch data one by one, so as to meet the cache hit of subsequent single data; diff --git a/versioned_docs/version-2.x/about/02-comparison.md b/versioned_docs/version-2.x/about/02-comparison.md new file mode 100644 index 000000000..1c4557231 --- /dev/null +++ b/versioned_docs/version-2.x/about/02-comparison.md @@ -0,0 +1,92 @@ +--- +title: Compare with other libraries +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## Compare with axios + +axios provides a very simple and easy-to-use HTTP request function based on promise. It only requires a simple line of code to send and receive requests, and can run in the browser and nodejs environment. It is a very excellent request js library. + +But axios focuses on sending requests and receiving responses, which means that if you need to write more code by yourself to actively optimize the request function, and alova is like a weapon of axios, combining axios with alova can get more powerful request capabilities. The following are the request management capabilities added by alova to axios. + +### alova provides automated request status management for axios + +When you only use axios, you usually need to maintain request-related status by yourself. You can get automated request status management capabilities by using alova's use hook. + + + + +```javascript +// vue3 example +const loading = ref(false); +const data = ref({}); +const error = ref(null); +const request = async () => { + try { + loading.value = true; + data.value = await axios.get('/xxx'); + } catch (e) { + error.value = e; + } + loading.value = false; +}; +mounted(request); +``` + + + + +```javascript +// Use axios as alova's request adapter +const { loading, data, error } = useRequest(alova.Get('/xxx')); +``` + + + + +### alova provides high-performance request strategies out of the box + +alova provides you with [multiple high-performance request strategy modules](/tutorial/strategy). You can use different modules according to different request scenarios, which axios does not have. + +### alova provides response data cache for axios + +alova provides 3 caching modes to meet different caching scenarios, namely memory mode, cache occupying mode, and recovery mode. They are component-independent and can hit the cache as long as the request address and parameters are the same, unless you turn it off. Response data caching can greatly improve request fluency and reduce server pressure. + +### alova provides request sharing function for axios + +Request sharing will reuse the same request when sending multiple identical requests at the same time. It can also improve application fluency and reduce server pressure. + +### alova provides data pre-fetching for axios + +Requesting the data to be used in advance can also greatly improve application fluency. + +### alova can manage request states + +You can use alova across any component hierarchy to access stateful data in other components, which allows you to reduce some of the trouble of cross-component communication. + +## Compared with react-query and swr + +react-query is a powerful asynchronous state management, and swr is a React Hooks library for data requests. Their common feature is the use of use hooks to send and manage requests, and data caching functions. For them, alova has the following differences at. + +### alova has different goals + +In fact, alova's use hook also refers to the design of react-query and swr, but alova chooses the direction of the request strategy library. You can use different request strategy modules in different request scenarios, allowing you to write less code. At the same time, more efficient Client-Server data interaction can also be achieved. + +### Method proxy design + +Both react-query and swr use `axios` or `fetch api` directly in use hook to send requests, while alova uses the `Method` proxy design mode. This design has the following 3 benefits: + +1. Unified usage without different usage depending on the platform or UI framework. +2. Request libraries such as `axios` and `fetch api` are decoupled from each API in the form of request adapters, which allows alova to provide a unified development experience and perfect coding migration. +3. Each `Method` instance represents an API, you can aggregate the request parameters and request behavior parameters of the same API into the same `Method` instance without spreading them to different files, which is more suitable for managing a large number of APIs. +4. alova realizes automatic management of response data cache by serializing request parameters on the `Method` instance. You do not need to specify the cache key, and both react-query and swr need to customize the `queryKey` to manage the cache. + +### High flexibility + +Alova achieves high flexibility through various adapters and middleware. It can not only run in any js environment, but also support users to customize request modules in different scenarios. + +### Lightweight + +alova is very lightweight, and its size is only 30%+ of react-query and axios. Similar in size to swr, but provides richer functionality. diff --git a/versioned_docs/version-2.x/about/03-Q&A.md b/versioned_docs/version-2.x/about/03-Q&A.md new file mode 100644 index 000000000..f36a19d80 --- /dev/null +++ b/versioned_docs/version-2.x/about/03-Q&A.md @@ -0,0 +1,133 @@ +--- +title: Question & Answer +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## Why to create alova? + +Data requests have always been an indispensable and important part of applications. Since the birth of XMLHttpRequest, request schemes have emerged in endlessly. Client-side data interaction exploration has always focused on the simplicity of requests, such as `$.ajax`, `axios`, `fetch api` and Request tools such as `react-query`, the coding form continues to evolve from callback functions, Promise, to usehook. These js libraries have done a good job in making requests simple, but they only provide common functions, which means For different request scenarios such as sharing requests, paging requests, form submissions, uploading and downloading files, etc., developers still need to write complex codes themselves, which reduces development efficiency and performance cannot be guaranteed. As user experience becomes more and more important, In this era, application fluency has become more and more important. + +Additionally, the collaboration between client and server is also separated. Front-end engineers need to consult API documents and manually write API functions, and any changes of APIs need to actively notify front-end engineers, which will make your product more uncontrollable. + +**We think there is a simpler solution is that based on your request scenarios such as pagination, form submission, breakpoint resumption, etc., select the corresponding useHook, which will help you manage data and control when requests should be sent**. This allows developers to achieve more efficient Client-Server data interaction while writing little code. + +Additionally, alova has very flexible expansion capabilities to implement request strategies in different scenarios. You can also customize your own request scenarios. This part is in [Custom Chapter](/category/custom). + +In order to cover more request scenarios, we also abstracted the request scenarios into [Request scene model(RSM)](/tutorial/others/RSM), which explains alova's request strategy scheme well. In the future, alova will continue to carry forward our exploration of request strategies. + +## Alternative to the request libraries? + +alova is a request strategy library, which was originally created to provide specific request strategy solutions for different request scenarios, so as to achieve a smooth request experience more concisely and elegantly, such as `$.ajax`, `axios` and `fetch- api`, etc. provide good support for request sending and response receiving, they are an essential part of the [RSM](/tutorial/others/RSM) process (request events), alova still needs to dependent on them to make requests, so we can Think of alova as an weaponry of the request library, making the request library more powerful. + +## Why binding UI framework? + +Decoupling a js library means using it in more scenarios. For example, axios can be used in nodejs, but it also means that developers need to write more template code, such as using useHooks to encapsulate axios. However, alova abandons more usage scenarios brought about by decoupling, and positions the Scope of usage in conjunction with the UI framework to use alova in the most streamlined way. This is for the benefit of developers and is prevalent in a UI framework. Sometimes, deep binding can provide developers with direct-use functions and improve the developer's experience without requiring too much template code. + +## How to use alova via cdn? + + + + +```html + + + + + + + + + +

+
Loading...
+
{{ error.message }}
+ responseData: {{ data }} +
+ + + +``` + +
+ + +```html + + + + + + + + + + + +
+ + + +``` + +
+ + +:::tip + +svelte depends on the compilation tool and cannot be used directly through CDN. For details, see [svelte.dev](https://svelte.dev/) + +::: + + + + +## What to pay attention to in React-Native? + +When using alova to develop React-Native applications, you can also use `alova/fetch`. + +But there are the following precautions: + +**Metro version** + +In alova's `package.json`, `exports` is used to define multiple export items, so you need to ensure these two points: + +1. Metro version is higher than 0.76.0 + +2. Enable `resolver.unstable_enablePackageExports` in `metro.config.js`. [Click here for details](https://facebook.github.io/metro/docs/configuration/#unstable_enablepackageexports-experimental) diff --git a/docs/tutorial/07-best-practice/_category_.json b/versioned_docs/version-2.x/about/_category_.json similarity index 59% rename from docs/tutorial/07-best-practice/_category_.json rename to versioned_docs/version-2.x/about/_category_.json index fe9037f89..07d61bd10 100644 --- a/docs/tutorial/07-best-practice/_category_.json +++ b/versioned_docs/version-2.x/about/_category_.json @@ -1,5 +1,5 @@ { - "label": "Best practice", + "label": "Others", "link": { "type": "generated-index" } diff --git a/versioned_docs/version-2.x/api/01-alova.md b/versioned_docs/version-2.x/api/01-alova.md index 08201547c..a7c058da6 100644 --- a/versioned_docs/version-2.x/api/01-alova.md +++ b/versioned_docs/version-2.x/api/01-alova.md @@ -1,6 +1,5 @@ --- title: alova instance -sidebar_position: 10 --- ## createAlova() @@ -146,7 +145,11 @@ Create a method instance for the POST request. ```ts interface Alova { - Post(url: string, data?: object | FormData | string | null, config?: AlovaMethodCreateConfig): Method; + Post( + url: string, + data?: object | FormData | string | null, + config?: AlovaMethodCreateConfig + ): Method; } ``` @@ -184,7 +187,11 @@ Create a method instance for the DELETE request. ```ts interface Alova { - Delete(url: string, data?: object | FormData | string | null, config?: AlovaMethodCreateConfig): Method; + Delete( + url: string, + data?: object | FormData | string | null, + config?: AlovaMethodCreateConfig + ): Method; } ``` @@ -220,7 +227,11 @@ Create a method instance for the PUT request. ```ts interface Alova { - Put(url: string, data?: object | FormData | string | null, config?: AlovaMethodCreateConfig): Method; + Put( + url: string, + data?: object | FormData | string | null, + config?: AlovaMethodCreateConfig + ): Method; } ``` @@ -278,7 +289,11 @@ Create a method instance for the PATCH request. ```ts interface Alova { - Patch(url: string, data?: object | FormData | string | null, config?: AlovaMethodCreateConfig): Method; + Patch( + url: string, + data?: object | FormData | string | null, + config?: AlovaMethodCreateConfig + ): Method; } ``` diff --git a/versioned_docs/version-2.x/api/02-method.md b/versioned_docs/version-2.x/api/02-method.md index 4cda9714b..3a97f7859 100644 --- a/versioned_docs/version-2.x/api/02-method.md +++ b/versioned_docs/version-2.x/api/02-method.md @@ -1,6 +1,5 @@ --- title: method instance -sidebar_position: 20 --- A method instance corresponds to a request information description, which has the URL, request headers, request parameters of a request, as well as request behavior parameters such as response data processing and cache data processing. Through method instances, you can feel a unified usage experience in any js environment, and it can run normally with very few changes. In addition, method instances put request parameters and request behavior parameters together, making it easier for APIs management instead of spreading it across multiple code files. @@ -110,7 +109,10 @@ type MethodFilter = filter?: MethodFilterHandler; alova?: Alova; }; -function matchSnapshotMethod(matcher: MethodFilter, matchAll?: boolean): Method[] | Method | undefined; +function matchSnapshotMethod( + matcher: MethodFilter, + matchAll?: boolean +): Method[] | Method | undefined; ``` - **Parameters** @@ -311,7 +313,10 @@ After `[v2.16.0]`, the method instance is a PromiseLike instance. You can direct ```ts interface Method { - then(onFulfilled?: (value: Response) => any, onRejected?: (reason: any) => any): Promise; + then( + onFulfilled?: (value: Response) => any, + onRejected?: (reason: any) => any + ): Promise; } ``` diff --git a/versioned_docs/version-2.x/api/03-core-hooks.md b/versioned_docs/version-2.x/api/03-core-hooks.md index 99327dd7a..12b496e22 100644 --- a/versioned_docs/version-2.x/api/03-core-hooks.md +++ b/versioned_docs/version-2.x/api/03-core-hooks.md @@ -1,268 +1,267 @@ ---- -title: Core useHooks -sidebar_position: 30 ---- - -## useRequest - -Represents the sending of a request. When executing useRequest, a request will be sent by default, and stateful request-related data will be created and maintained, such as `loading/data/error`, etc. It is the most commonly used method when obtaining initial data on the page. It also supports turning off its default request sending, which is very useful in request scenarios triggered by click events such as submitting data. - -> Go to [Send Request](/tutorial/combine-framework/use-request) for details. - -### type - -```ts -function useRequest( - methodHandler: Method | (...args: any[]) => Method, - config?: RequestHookConfig -): UseHookReturnType; -``` - -### Parameters - -1. `methodHandler`: can be passed in two forms: method instance and function. When specified as a function, it can receive parameters passed in by `send` and require a method instance to be returned. -2. `config`: hook configuration parameters. - -| Name | Description | Type | Default | Version | -| ------------- | -------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ------- | ------- | -| immediate | Whether to initiate the request immediately | boolean | true | - | -| initialData | The initial data value. The data value is the initial value before the first response. If it is not set, it is `undefined` | any | - | - | -| force | Whether to force the request, it can be set to the function to dynamically return a boolean value | boolean \| (...args: any[]) => boolean \| false | - | - | -| managedStates | Additional managed states, which can be updated via updateState | Record\ | - | - | -| middleware | Middleware function, [Understanding alova middleware](/tutorial/advanced/middleware) | (context: [AlovaFrontMiddlewareContext](#alovafrontmiddlewarecontext), next: [AlovaGuardNext](#alovaguardnext)) => Promise\ | - | - | - -#### AlovaFrontMiddlewareContext - -| Name | Description | Type | Version | -| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | -| method | The method object of the current request | Method | - | -| cachedResponse | Hit cached data | any | - | -| config | Current use hook configuration | Record\ | - | -| sendArgs | Parameters of the response processing callback, which are passed in by send of use hooks | any[] | - | -| frontStates | use hook front-end state collection, such as data, loading, error, etc. | [FrontRequestState](#frontrequeststate) | - | -| send | Send request function | (...args: any[]) => Promise | - | -| abort | abort function | () => void | - | -| decorateSuccess | Decorate success callback function | (decorator: (
handler: (event: [AlovaSuccessEvent](#alovasuccessevent)) => void,
event: [AlovaSuccessEvent](#alovasuccessevent),
index: number,
length: number
) => void) => void | - | -| decorateError | Decoration failure callback function | (decorator: (
handler: (event: [AlovaErrorEvent](#alovaerrorevent)) => void,
event: [AlovaErrorEvent](#alovaerrorevent),
index: number,
length: number
) => void) => void | - | -| decorateComplete | Decoration completion callback function | (decorator: (
handler: (event: [AlovaCompleteEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovacompleteevent),
index: number,
length: number
) => void) => void | - | -| update | Function to update the current use hook front-end state, more useful in react | (newFrontStates: [FrontRequestState](#frontrequeststate)) => void; | - | -| controlLoading | Customize the loading state of the control. The call will no longer trigger changes in the loading state. When the incoming control is false, the control will be cancelled. | (control?: boolean) => void | - | - -#### AlovaGuardNext - -```typescript -type AlovaGuardNext = (guardNextConfig?: { - force?: boolean | (...args: any[]) => boolean; - method?: Method; -}): Promise; -``` - -#### FrontRequestState - -The following attribute values will automatically infer the responsive data type of the corresponding UI framework based on `statesHook`, which is the `Ref` type in vue3, the normal value in react, and the `Writable` type in svelte - -| Name | Description | Type | Version | -| ----------- | ----------------------------- | ------------------ | ------- | -| loading | request loading status | boolean | - | -| data | response data | any | - | -| error | Request error information | Error \| undefined | - | -| downloading | Download progress information | Object | - | -| uploading | Upload progress information | Object | - | - -#### AlovaSuccessEvent - -| Name | Description | Type | Version | -| --------- | ---------------------------------------------------------------------------------------- | ------- | ------- | -| method | The method object of the current request | Method | - | -| sendArgs | Parameters of the response processing callback, which are passed in by send of use hooks | any[] | - | -| data | response data | any | - | -| fromCache | Whether the response data comes from cache | boolean | - | - -#### AlovaErrorEvent - -| Name | Description | Type | Version | -| -------- | ---------------------------------------------------------------------------------------- | ------ | ------- | -| method | The method object of the current request | Method | - | -| sendArgs | Parameters of the response processing callback, which are passed in by send of use hooks | any[] | - | -| error | Response error instance | Error | - | - -#### AlovaCompleteEvent - -| Name | Description | Type | Version | -| --------- | ---------------------------------------------------------------------------------------- | -------------------- | ------- | -| method | The method object of the current request | Method | - | -| sendArgs | Parameters of the response processing callback, which are passed in by send of use hooks | any[] | - | -| status | Response status, success when successful, error when failure | 'success' \| 'error' | - | -| data | response data, with value when successful | any | - | -| fromCache | Whether the response data comes from the cache, a value if successful | boolean | - | -| error | Response error instance, with value in case of failure | Error | - | - -### return value - -`UseHookReturnType` contains response data and request-related states, operation functions and event binding functions. They will automatically infer the responsive data type of the corresponding UI framework based on `statesHook`, which is the `Ref` type in vue3 and the `Ref` type in react. It is a normal value and is of `Writable` type in svelte. - -#### Responsive data - -| Name | Description | Type | Version | -| ----------- | ----------------------------- | ------------------ | ------- | -| loading | request loading status | boolean | - | -| data | response data | any | - | -| error | Request error information | Error \| undefined | - | -| downloading | Download progress information | Object | - | -| uploading | Upload progress information | Object | - | - -#### Operation function - -| name | description | function parameters | return value | version | -| ------ | ----------------------------------------------------------------------------- | ------------------------------------------------------- | ------------ | ------- | -| send | Send request function | ...args: any[] | - | - | -| abort | abort function | - | Promise | - | -| update | Function to update the current use hook front-end state, more useful in react | newFrontStates: [FrontRequestState](#frontrequeststate) | - | - -#### event - -| name | description | callback parameters | version | -| ---------- | -------------------------------- | ------------------------------------------------ | ------- | -| onSuccess | Request success event binding | event: [AlovaSuccessEvent](#alovasuccessevent) | - | -| onError | Request error event binding | event: [AlovaErrorEvent](#alovaerrorevent) | - | -| onComplete | Request completion event binding | event: [AlovaCompleteEvent](#alovacompleteevent) | - | - -## useWatcher - -Monitor the status and initiate a request after the status changes. In some scenarios that require re-requesting as the data changes, such as paging, data filtering, and fuzzy search. - -> Go to [State Change Request](/tutorial/combine-framework/use-watcher) for details. - -### type - -```typescript -function useWatcher( - handler: Method | (...args: any[]) => Method, - watchingStates: State[], - config?:WatcherHookConfig -): UseHookReturnType; -``` - -### Parameters - -1. `handler`: can be passed in two forms: method instance and function. When specified as a function, it can receive parameters passed in by `send` and require a method instance to be returned. -2. `config`: hook configuration parameters. - -| Name | Description | Type | Default | Version | -| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- | ---------- | ------- | -| immediate | Whether to initiate the request immediately | boolean | true | - | -| initialData | The initial data value. The data value is the initial value before the first response. If it is not set, it is `undefined` | any | - | - | -| force | Whether to force the request, it can be set to the function to dynamically return a boolean value | boolean \| (...args: any[]) => boolean \| false | -| managedStates | Additional managed states, which can be updated via updateState | Record\ | - | -| debounce | Request debounce time (milliseconds), when passing in the array, you can set the debounce time individually in the order of watchingStates | number \| number[] | - | -| middleware | Middleware function, [Understanding alova middleware](/tutorial/advanced/middleware) | (context: [AlovaFrontMiddlewareContext](#alovafrontmiddlewarecontext), next: [AlovaGuardNext](#alovaguardnext)) => Promise\ | - | - | -| sendable | Whether to send a request when the monitored state changes | (methodInstance: AlovaEvent) => boolean | () => true | - | -| abortLast | Whether to abort the last unresponsive request | boolean | true | - | - -### return value - -`UseHookReturnType` contains response data and request-related states, operation functions and event binding functions. They will automatically infer the responsive data type of the corresponding UI framework based on statesHook. It is Ref type in vue3 and ordinary value in react. In svelte it is Writable type. - -#### Responsive data - -| Name | Description | Type | Version | -| ----------- | ----------------------------- | ------------------ | ------- | -| loading | request loading status | boolean | - | -| data | response data | any | - | -| error | Request error information | Error \| undefined | - | -| downloading | Download progress information | Object | - | -| uploading | Upload progress information | Object | - | - -#### Operation function - -| name | description | function parameters | return value | version | -| ------ | ----------------------------------------------------------------------------- | ------------------------------------------------------- | ------------ | ------- | -| send | Send request function | ...args: any[] | Promise | - | -| abort | abort function | - | - | - | -| update | Function to update the current use hook front-end state, more useful in react | newFrontStates: [FrontRequestState](#frontrequeststate) | - | - -#### event - -| name | description | callback parameters | version | -| ---------- | -------------------------------- | ------------------------------------------------ | ------- | -| onSuccess | Request success event binding | event: [AlovaSuccessEvent](#alovasuccessevent) | - | -| onError | Request error event binding | event: [AlovaErrorEvent](#alovaerrorevent) | - | -| onComplete | Request completion event binding | event: [AlovaCompleteEvent](#alovacompleteevent) | - | - -## useFetcher - -It is used to pull data through `useFetcher`, which is useful when preloading data and updating status across modules. - -> Go to [Data Fetching](/tutorial/advanced/use-fetcher) to view details. - -### type - -```typescript -function useFetcher(config?: FetcherHookConfig): UseFetchHookReturnType; -``` - -### Parameters - -1. `config`: hook configuration parameters. - -| Name | Description | Type | Default | Version | -| ---------- | ------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ | ------- | -| force | Whether to force the request, it can be set to the function to dynamically return a boolean value | boolean | (...args: any[]) => boolean \| false | - | -| middleware | Middleware function, [Understand alova middleware](/tutorial/advanced/middleware) | (context: [AlovaFetcherMiddlewareContext](#alovafetchermiddlewarecontext), next: [AlovaGuardNext](#alovaguardnext)) => Promise\ | - | - | - -#### AlovaFetcherMiddlewareContext - -| Name | Description | Type | Version | -| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | -| method | The method object of the current request | Method | - | -| cachedResponse | Hit cached data | any | - | -| config | Current use hook configuration | Record\ | - | -| fetchArgs | Parameters of the response processing callback, which are passed in by fetch of useFetcher | any[] | - | -| fetchStates | use hook preload state collection, such as fetching, error, etc. | [FetchRequestState](#fetchrequeststate) | - | -| fetch | Data preloading function | (method: Method, ...args: any[]) => Promise | - | -| abort | abort function | () => void | - | -| decorateSuccess | Decorate success callback function | (decorator: (
handler: (event: [AlovaSuccessEvent](#alovasuccessevent)) => void,
event: [AlovaSuccessEvent](#alovasuccessevent),
index: number,
length: number
) => void) => void | - | -| decorateError | Decoration failure callback function | (decorator: (
handler: (event: [AlovaErrorEvent](#alovaerrorevent)) => void,
event: [AlovaErrorEvent](#alovaerrorevent),
index: number,
length: number
) => void) => void | - | -| decorateComplete | Decoration completion callback function | (decorator: (
handler: (event: [AlovaCompleteEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovacompleteevent),
index: number,
length: number
) => void) => void | - | -| update | Function to update the current use hook preloading state, more useful in react | (newFrontStates: [FetchRequestState](#fetchrequeststate)) => void; | - | -| controlFetching | After calling, the state of fetching will be customized and the change of fetching state will no longer be triggered internally. When the incoming control is false, the control will be canceled | (control?: boolean) => void | - | - -#### FetchRequestState - -The following attribute values will automatically infer the responsive data type of the corresponding UI framework based on `statesHook`, which is the `Ref` type in vue3, the normal value in react, and the `Writable` type in svelte - -| Name | Description | Type | Version | -| ----------- | ----------------------------- | ------------------ | ------- | -| fetching | preloading request status | boolean | - | -| error | Request error information | Error \| undefined | - | -| downloading | Download progress information | Object | - | -| uploading | Upload progress information | Object | - | - -### return value - -`UseFetchHookReturnType` contains request-related states, operation functions and event binding functions. They will automatically infer the responsive data type of the corresponding UI framework based on statesHook. It is Ref type in vue3, ordinary value in react, and ordinary value in svelte. For Writable type. - -#### Responsive data - -| Name | Description | Type | Version | -| ----------- | ----------------------------- | ------------------ | ------- | -| fetching | preloading request status | boolean | - | -| error | Request error information | Error \| undefined | - | -| downloading | Download progress information | Object | - | -| uploading | Upload progress information | Object | - | - -#### Operation function - -| name | description | function parameters | return value | version | -| ------ | ----------------------------------------------------------------------------- | ---------------------------------------------------------- | ------------ | ------- | -| fetch | Data preloading function | 1. method: preloaded Method instance
2. ...args: any[] | Promise | - | -| abort | abort function | - | - | - | -| update | Function to update the current use hook front-end state, more useful in react | newFrontStates: [FrontRequestState](#frontrequeststate) | - | - -#### event - -| name | description | callback parameters | version | -| ---------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------- | ------- | -| onSuccess | Request success event binding | event: [AlovaSuccessEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovasuccessevent) | - | -| onError | Request error event binding | event: [AlovaErrorEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovaerrorevent) | - | -| onComplete | Request completion event binding | event: [AlovaCompleteEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovacompleteevent) | - | +--- +title: Core useHooks +--- + +## useRequest + +Represents the sending of a request. When executing useRequest, a request will be sent by default, and stateful request-related data will be created and maintained, such as `loading/data/error`, etc. It is the most commonly used method when obtaining initial data on the page. It also supports turning off its default request sending, which is very useful in request scenarios triggered by click events such as submitting data. + +> Go to [Send Request](/tutorial/combine-framework/use-request) for details. + +### type + +```ts +function useRequest( + methodHandler: Method | (...args: any[]) => Method, + config?: RequestHookConfig +): UseHookReturnType; +``` + +### Parameters + +1. `methodHandler`: can be passed in two forms: method instance and function. When specified as a function, it can receive parameters passed in by `send` and require a method instance to be returned. +2. `config`: hook configuration parameters. + +| Name | Description | Type | Default | Version | +| ------------- | -------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ------- | ------- | +| immediate | Whether to initiate the request immediately | boolean | true | - | +| initialData | The initial data value. The data value is the initial value before the first response. If it is not set, it is `undefined` | any | - | - | +| force | Whether to force the request, it can be set to the function to dynamically return a boolean value | boolean \| (...args: any[]) => boolean \| false | - | - | +| managedStates | Additional managed states, which can be updated via updateState | Record\ | - | - | +| middleware | Middleware function, [Understanding alova middleware](/tutorial/advanced/middleware) | (context: [AlovaFrontMiddlewareContext](#alovafrontmiddlewarecontext), next: [AlovaGuardNext](#alovaguardnext)) => Promise\ | - | - | + +#### AlovaFrontMiddlewareContext + +| Name | Description | Type | Version | +| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | +| method | The method object of the current request | Method | - | +| cachedResponse | Hit cached data | any | - | +| config | Current use hook configuration | Record\ | - | +| sendArgs | Parameters of the response processing callback, which are passed in by send of use hooks | any[] | - | +| frontStates | use hook front-end state collection, such as data, loading, error, etc. | [FrontRequestState](#frontrequeststate) | - | +| send | Send request function | (...args: any[]) => Promise | - | +| abort | abort function | () => void | - | +| decorateSuccess | Decorate success callback function | (decorator: (
handler: (event: [AlovaSuccessEvent](#alovasuccessevent)) => void,
event: [AlovaSuccessEvent](#alovasuccessevent),
index: number,
length: number
) => void) => void | - | +| decorateError | Decoration failure callback function | (decorator: (
handler: (event: [AlovaErrorEvent](#alovaerrorevent)) => void,
event: [AlovaErrorEvent](#alovaerrorevent),
index: number,
length: number
) => void) => void | - | +| decorateComplete | Decoration completion callback function | (decorator: (
handler: (event: [AlovaCompleteEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovacompleteevent),
index: number,
length: number
) => void) => void | - | +| update | Function to update the current use hook front-end state, more useful in react | (newFrontStates: [FrontRequestState](#frontrequeststate)) => void; | - | +| controlLoading | Customize the loading state of the control. The call will no longer trigger changes in the loading state. When the incoming control is false, the control will be cancelled. | (control?: boolean) => void | - | + +#### AlovaGuardNext + +```typescript +type AlovaGuardNext = (guardNextConfig?: { + force?: boolean | (...args: any[]) => boolean; + method?: Method; +}): Promise; +``` + +#### FrontRequestState + +The following attribute values will automatically infer the responsive data type of the corresponding UI framework based on `statesHook`, which is the `Ref` type in vue3, the normal value in react, and the `Writable` type in svelte + +| Name | Description | Type | Version | +| ----------- | ----------------------------- | ------------------ | ------- | +| loading | request loading status | boolean | - | +| data | response data | any | - | +| error | Request error information | Error \| undefined | - | +| downloading | Download progress information | Object | - | +| uploading | Upload progress information | Object | - | + +#### AlovaSuccessEvent + +| Name | Description | Type | Version | +| --------- | ---------------------------------------------------------------------------------------- | ------- | ------- | +| method | The method object of the current request | Method | - | +| sendArgs | Parameters of the response processing callback, which are passed in by send of use hooks | any[] | - | +| data | response data | any | - | +| fromCache | Whether the response data comes from cache | boolean | - | + +#### AlovaErrorEvent + +| Name | Description | Type | Version | +| -------- | ---------------------------------------------------------------------------------------- | ------ | ------- | +| method | The method object of the current request | Method | - | +| sendArgs | Parameters of the response processing callback, which are passed in by send of use hooks | any[] | - | +| error | Response error instance | Error | - | + +#### AlovaCompleteEvent + +| Name | Description | Type | Version | +| --------- | ---------------------------------------------------------------------------------------- | -------------------- | ------- | +| method | The method object of the current request | Method | - | +| sendArgs | Parameters of the response processing callback, which are passed in by send of use hooks | any[] | - | +| status | Response status, success when successful, error when failure | 'success' \| 'error' | - | +| data | response data, with value when successful | any | - | +| fromCache | Whether the response data comes from the cache, a value if successful | boolean | - | +| error | Response error instance, with value in case of failure | Error | - | + +### return value + +`UseHookReturnType` contains response data and request-related states, operation functions and event binding functions. They will automatically infer the responsive data type of the corresponding UI framework based on `statesHook`, which is the `Ref` type in vue3 and the `Ref` type in react. It is a normal value and is of `Writable` type in svelte. + +#### Responsive data + +| Name | Description | Type | Version | +| ----------- | ----------------------------- | ------------------ | ------- | +| loading | request loading status | boolean | - | +| data | response data | any | - | +| error | Request error information | Error \| undefined | - | +| downloading | Download progress information | Object | - | +| uploading | Upload progress information | Object | - | + +#### Operation function + +| name | description | function parameters | return value | version | +| ------ | ----------------------------------------------------------------------------- | ------------------------------------------------------- | ------------ | ------- | +| send | Send request function | ...args: any[] | - | - | +| abort | abort function | - | Promise | - | +| update | Function to update the current use hook front-end state, more useful in react | newFrontStates: [FrontRequestState](#frontrequeststate) | - | + +#### event + +| name | description | callback parameters | version | +| ---------- | -------------------------------- | ------------------------------------------------ | ------- | +| onSuccess | Request success event binding | event: [AlovaSuccessEvent](#alovasuccessevent) | - | +| onError | Request error event binding | event: [AlovaErrorEvent](#alovaerrorevent) | - | +| onComplete | Request completion event binding | event: [AlovaCompleteEvent](#alovacompleteevent) | - | + +## useWatcher + +Monitor the status and initiate a request after the status changes. In some scenarios that require re-requesting as the data changes, such as paging, data filtering, and fuzzy search. + +> Go to [State Change Request](/tutorial/combine-framework/use-watcher) for details. + +### type + +```typescript +function useWatcher( + handler: Method | (...args: any[]) => Method, + watchingStates: State[], + config?:WatcherHookConfig +): UseHookReturnType; +``` + +### Parameters + +1. `handler`: can be passed in two forms: method instance and function. When specified as a function, it can receive parameters passed in by `send` and require a method instance to be returned. +2. `config`: hook configuration parameters. + +| Name | Description | Type | Default | Version | +| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- | ---------- | ------- | +| immediate | Whether to initiate the request immediately | boolean | true | - | +| initialData | The initial data value. The data value is the initial value before the first response. If it is not set, it is `undefined` | any | - | - | +| force | Whether to force the request, it can be set to the function to dynamically return a boolean value | boolean \| (...args: any[]) => boolean \| false | +| managedStates | Additional managed states, which can be updated via updateState | Record\ | - | +| debounce | Request debounce time (milliseconds), when passing in the array, you can set the debounce time individually in the order of watchingStates | number \| number[] | - | +| middleware | Middleware function, [Understanding alova middleware](/tutorial/advanced/middleware) | (context: [AlovaFrontMiddlewareContext](#alovafrontmiddlewarecontext), next: [AlovaGuardNext](#alovaguardnext)) => Promise\ | - | - | +| sendable | Whether to send a request when the monitored state changes | (methodInstance: AlovaEvent) => boolean | () => true | - | +| abortLast | Whether to abort the last unresponsive request | boolean | true | - | + +### return value + +`UseHookReturnType` contains response data and request-related states, operation functions and event binding functions. They will automatically infer the responsive data type of the corresponding UI framework based on statesHook. It is Ref type in vue3 and ordinary value in react. In svelte it is Writable type. + +#### Responsive data + +| Name | Description | Type | Version | +| ----------- | ----------------------------- | ------------------ | ------- | +| loading | request loading status | boolean | - | +| data | response data | any | - | +| error | Request error information | Error \| undefined | - | +| downloading | Download progress information | Object | - | +| uploading | Upload progress information | Object | - | + +#### Operation function + +| name | description | function parameters | return value | version | +| ------ | ----------------------------------------------------------------------------- | ------------------------------------------------------- | ------------ | ------- | +| send | Send request function | ...args: any[] | Promise | - | +| abort | abort function | - | - | - | +| update | Function to update the current use hook front-end state, more useful in react | newFrontStates: [FrontRequestState](#frontrequeststate) | - | + +#### event + +| name | description | callback parameters | version | +| ---------- | -------------------------------- | ------------------------------------------------ | ------- | +| onSuccess | Request success event binding | event: [AlovaSuccessEvent](#alovasuccessevent) | - | +| onError | Request error event binding | event: [AlovaErrorEvent](#alovaerrorevent) | - | +| onComplete | Request completion event binding | event: [AlovaCompleteEvent](#alovacompleteevent) | - | + +## useFetcher + +It is used to pull data through `useFetcher`, which is useful when preloading data and updating status across modules. + +> Go to [Data Fetching](/tutorial/advanced/use-fetcher) to view details. + +### type + +```typescript +function useFetcher(config?: FetcherHookConfig): UseFetchHookReturnType; +``` + +### Parameters + +1. `config`: hook configuration parameters. + +| Name | Description | Type | Default | Version | +| ---------- | ------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ | ------- | +| force | Whether to force the request, it can be set to the function to dynamically return a boolean value | boolean | (...args: any[]) => boolean \| false | - | +| middleware | Middleware function, [Understand alova middleware](/tutorial/advanced/middleware) | (context: [AlovaFetcherMiddlewareContext](#alovafetchermiddlewarecontext), next: [AlovaGuardNext](#alovaguardnext)) => Promise\ | - | - | + +#### AlovaFetcherMiddlewareContext + +| Name | Description | Type | Version | +| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | +| method | The method object of the current request | Method | - | +| cachedResponse | Hit cached data | any | - | +| config | Current use hook configuration | Record\ | - | +| fetchArgs | Parameters of the response processing callback, which are passed in by fetch of useFetcher | any[] | - | +| fetchStates | use hook preload state collection, such as fetching, error, etc. | [FetchRequestState](#fetchrequeststate) | - | +| fetch | Data preloading function | (method: Method, ...args: any[]) => Promise | - | +| abort | abort function | () => void | - | +| decorateSuccess | Decorate success callback function | (decorator: (
handler: (event: [AlovaSuccessEvent](#alovasuccessevent)) => void,
event: [AlovaSuccessEvent](#alovasuccessevent),
index: number,
length: number
) => void) => void | - | +| decorateError | Decoration failure callback function | (decorator: (
handler: (event: [AlovaErrorEvent](#alovaerrorevent)) => void,
event: [AlovaErrorEvent](#alovaerrorevent),
index: number,
length: number
) => void) => void | - | +| decorateComplete | Decoration completion callback function | (decorator: (
handler: (event: [AlovaCompleteEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovacompleteevent),
index: number,
length: number
) => void) => void | - | +| update | Function to update the current use hook preloading state, more useful in react | (newFrontStates: [FetchRequestState](#fetchrequeststate)) => void; | - | +| controlFetching | After calling, the state of fetching will be customized and the change of fetching state will no longer be triggered internally. When the incoming control is false, the control will be canceled | (control?: boolean) => void | - | + +#### FetchRequestState + +The following attribute values will automatically infer the responsive data type of the corresponding UI framework based on `statesHook`, which is the `Ref` type in vue3, the normal value in react, and the `Writable` type in svelte + +| Name | Description | Type | Version | +| ----------- | ----------------------------- | ------------------ | ------- | +| fetching | preloading request status | boolean | - | +| error | Request error information | Error \| undefined | - | +| downloading | Download progress information | Object | - | +| uploading | Upload progress information | Object | - | + +### return value + +`UseFetchHookReturnType` contains request-related states, operation functions and event binding functions. They will automatically infer the responsive data type of the corresponding UI framework based on statesHook. It is Ref type in vue3, ordinary value in react, and ordinary value in svelte. For Writable type. + +#### Responsive data + +| Name | Description | Type | Version | +| ----------- | ----------------------------- | ------------------ | ------- | +| fetching | preloading request status | boolean | - | +| error | Request error information | Error \| undefined | - | +| downloading | Download progress information | Object | - | +| uploading | Upload progress information | Object | - | + +#### Operation function + +| name | description | function parameters | return value | version | +| ------ | ----------------------------------------------------------------------------- | ---------------------------------------------------------- | ------------ | ------- | +| fetch | Data preloading function | 1. method: preloaded Method instance
2. ...args: any[] | Promise | - | +| abort | abort function | - | - | - | +| update | Function to update the current use hook front-end state, more useful in react | newFrontStates: [FrontRequestState](#frontrequeststate) | - | + +#### event + +| name | description | callback parameters | version | +| ---------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------- | ------- | +| onSuccess | Request success event binding | event: [AlovaSuccessEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovasuccessevent) | - | +| onError | Request error event binding | event: [AlovaErrorEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovaerrorevent) | - | +| onComplete | Request completion event binding | event: [AlovaCompleteEvent](#alovacompleteevent)) => void,
event: [AlovaCompleteEvent](#alovacompleteevent) | - | diff --git a/versioned_docs/version-2.x/api/04-cache.md b/versioned_docs/version-2.x/api/04-cache.md index 86414f8e0..0a5339a22 100644 --- a/versioned_docs/version-2.x/api/04-cache.md +++ b/versioned_docs/version-2.x/api/04-cache.md @@ -1,6 +1,5 @@ --- title: Cache operation -sidebar_position: 40 --- ## invalidateCache() diff --git a/versioned_docs/version-2.x/api/05-states.md b/versioned_docs/version-2.x/api/05-states.md index a32f22de5..5c161e14b 100644 --- a/versioned_docs/version-2.x/api/05-states.md +++ b/versioned_docs/version-2.x/api/05-states.md @@ -1,6 +1,5 @@ --- title: Response states operation -sidebar_position: 50 --- ## updateState diff --git a/versioned_docs/version-2.x/api/06-global-config.md b/versioned_docs/version-2.x/api/06-global-config.md index d359d10c6..a3feb3094 100644 --- a/versioned_docs/version-2.x/api/06-global-config.md +++ b/versioned_docs/version-2.x/api/06-global-config.md @@ -1,36 +1,35 @@ ---- -title: Global configuration -sidebar_position: 60 ---- - -## globalConfig() - -Global configuration. - -- **type** - -```ts -function globalConfig(config: AlovaGlobalConfig): void; -``` - -- **Parameters** - -1. config: configuration - -| Parameter name | Type | Description | -| -------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------- | -| limitSnapshots | number | method snapshot number limit, set to 0 to disable saving snapshots. After closing, the method snapshot matcher will be unavailable | - -- **return** - -none - -- **Example** - -```ts -import { globalConfig } from 'alova'; - -globalConfig({ - limitSnapshots: 10 -}); -``` +--- +title: Global configuration +--- + +## globalConfig() + +Global configuration. + +- **type** + +```ts +function globalConfig(config: AlovaGlobalConfig): void; +``` + +- **Parameters** + +1. config: configuration + +| Parameter name | Type | Description | +| -------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------- | +| limitSnapshots | number | method snapshot number limit, set to 0 to disable saving snapshots. After closing, the method snapshot matcher will be unavailable | + +- **return** + +none + +- **Example** + +```ts +import { globalConfig } from 'alova'; + +globalConfig({ + limitSnapshots: 10 +}); +``` diff --git a/versioned_docs/version-2.x/contributing/01-overview.md b/versioned_docs/version-2.x/contributing/01-overview.md index 4a74bfb3d..a463091e8 100644 --- a/versioned_docs/version-2.x/contributing/01-overview.md +++ b/versioned_docs/version-2.x/contributing/01-overview.md @@ -1,175 +1,174 @@ ---- -title: Contribution Guidelines -sidebar_position: 10 ---- - -# alova Contribution Guidelines - -Hello, I'm glad to meet you here. This is a detailed alova contribution guidelines, which includes detailed guidance on contributing to all aspects of alova. Please continue reading. - -## Preface - -In the past period of time, we have received active participation information from developers from all over the world in Github issues and Github Discussion, which means that alova is being loved by more and more developers. Even so, alova is still a rookie, and it still has a long way to go. - -**We expect to make alova a common project for everyone who is willing to participate, and we encourage everyone to become a contributor to the alova community with an open and inclusive attitude. Moreover, we believe that contributing to alova is not limited to code contributions, but participating in any activities that are beneficial to the development of alova is considered to contribute to alova.** Participating in contributions now can win you more effective contribution opportunities, and it allows you to contribute to the world Even if you are a junior developer, as long as the idea is in line with [alova's mission and design principles](#alova-mission-and-design-principles), please generously participate! - -> There is a [Code of conduct](./code-of-conduct) here, please refer to it. - -## Contribution directory - -Here are 13 places where you can contribute, but not limited to these, you can choose the part you want to participate in, and link to the corresponding location for detailed reading: - -- [use alova in your project](#use-alova-in-your-project) -- [star alova](#star-alova) -- [report bug](#report-bug) -- [Propose new feature ideas](#propose-new-feature-ideas) -- [Pull Request](#pull-request) -- [Create an adapter or strategy library based on alova](#create-an-adapter-or-strategy-library-based-on-alova) -- [Participate in community/PR review](#participate-in-community-pr-review) -- [Publish and disseminate information about alova](#publish-and-disseminate-information-about-alova) -- [Share experience](#share-experience) -- [Collaboration](#collaboration) -- [Donation](#donation) -- [Correct or add docs](#correct-or-add-docs) -- [Translate docs](#Translate-docs) - -## alova mission and design principles - -### alova Mission - -alova's mission gives it a clear development direction, which clearly defines what alova should do. - -alova is a lightweight request strategy library, **Its mission is to enable developers to achieve more efficient Client-Server data interaction by less codes**. - -For developers, alova provides them with a simple API and out-of-the-box advanced request functions, as well as various simple and high-performance request strategy modules. For application users, they can enjoy the benefits of alova The smooth experience brought by high-performance data interaction, therefore, alova has the following features: - -1. The api design is similar to axios, allowing users to learn at a lower cost; -2. Deep binding of the UI framework, greatly improving the benefits of developers; -3. Out-of-the-box advanced functions to avoid repeated packaging, such as request sharing, request caching, etc., to reduce developers' repeated packaging; -4. The platform-independent coding way, and it can perfectly migrate in different platform; -5. High scalability design, which can encapsulate high-reusability and high-performance business-related request strategies; -6. Highly aggregated and low-coupling method design improves API code maintainability; - -### alova design principles - -The design principles points out how it should be designed, the following is the core design concept of alova. - -1. `Method` proxy design, high aggregation, platform-independent design, throughout the request, you should be able to access it in any request function, from another perspective, the information related to the request should also be placed in the method instance ; -2. Lightweight, try to keep the source code concise in coding, such as avoiding repeated code, merging variable declarations, prototype chain function encapsulation, no similar API, tree shaking, but long variable names are allowed, because it will be used when compiling will be replaced by a single letter; -3. Highly scalable design. First, the design of alova uses a large number of adapter patterns and hook functions. For example, adapters include `requestAdapter`, `storageAdapter`, etc., and hook functions include `beforeRequest`, `responded`, `transformData`, `localCache`, etc., and most of them have default behaviors, which are designed to be easy to use while retaining high scalability; second, global request parameters can be overwritten, such as `timeout`, `shareRequest`, etc., for These parameters can be set individually for special requests. -4. The api design is universal. First, it means that the function of this api has a high level of abstraction, rather than a specific business. Second, the api design is scalable to adapt to the needs of the api iteration - -> The api universal design is only applicable to the alova library. If you are conceiving a request strategy, you can design it according to the specific business. - -## Select the contribution point you are interested in - -### Use alova in your project - -We believe that your use of alova in the project is also a contributor to alova, which is also telling people that alova is a trustworthy open source project, please open [this issue](https://github.com/alovajs/alova/issues/165), which may give you the opportunity to display your project on the alova official website. - -### Star alova - -Although this may be considered insignificant, it represents your recognition of alova, and every star is very important to alova. star alova in the right top of [alova's Github repository](https://github.com/alovajs/alova), which is important to us. - -### Report bug - -Please move to [Github new issue](https://github.com/alovajs/alova/issues/new/choose) and select the corresponding template to submit. Detailed instructions will be displayed in the submitted issue. - -**PLEASE NOTE:** If you want to ask questions about alova, please go to [Github Discussion](https://github.com/alovajs/alova/discussions) to create a question. That would be closed immediately when ask a question in issues. - -### Propose new feature ideas - -In order for alova to achieve its value and goals, before submitting a new feature idea, please carefully read [alova mission and design principles](#alova-mission-and-design-principles), and ensure that your new idea is in line with alova's mission and design philosophy design concept. - -Then, please submit it in [🚀 New Feature Proposal](https://github.com/alovajs/alova/issues/new?assignees=&labels=feature-request&projects=&template=FEATURE_REQUEST_en.yml), the detailed description will be Displayed when submitting an issue. - -### Pull Request - -You can contribute the following 3 aspects of code through pull request. If you are a new partner who is interested in participating, all `good first issue` issues are listed in [Github contribution list](https://github.com/alovajs/alova/contribute), it is used to Tell new partners who are interested in contributing that this is a good start. - -#### Bug fix - -Issues marked as [`bug:confirmed`](https://github.com/alovajs/alova/labels/bug%3Aconfirmed) in Github issues are all confirmed bugs, you can choose freely. - -If you encounter a bug yourself, please also [report a bug](#report-bug) first to ensure that the bug is confirmed to avoid invalid pull requests. - -#### New feature development - -Issues marked as [`feature-request:confirmed`](https://github.com/alovajs/alova/labels/feature-request%3Aconfirmed) in Github issues are new features that have been confirmed, you You can choose freely. - -If you have an idea for adding a new feature, please also [submit an issue of a new feature idea](#propose-new-feature-ideas) to ensure that the idea is confirmed to avoid invalid pull requests. - -#### Project configuration - -If you are very good at project configuration and find deficiencies in the alova project, such as incomplete configuration, too old configuration version, insufficient automation (including project development automation and Github warehouse management automation), you can also press [New Feature development](#new-feature-development) process to contribute. - -:::warning IMPORTANT - -1. Please read the [Developing Guidelines](./developing-guidelines) carefully before developing, it can guide you step by step on how to contribute code. -2. When you identify an issue that needs to be resolved, please make sure that it has not been flagged by someone else's pull request, which means that it has been pre-occupied. - -::: - -### Create an adapter or strategy library based on alova - -alova provides high-extensibility features, and you can write your own js library based on it. - -#### Custom Adapter - -Customize various adapters to meet the operating requirements in different environments. The following directions are available for reference: - -1. Customize statesHook, which can be executed under different UI frameworks, such as `solid/qwik`, currently supports `react/vue/svelte`, please read [Custom statesHook](/tutorial/custom/custom-stateshook); -2. Customize the request adapter, so that alova can cooperate with more request schemes, such as `GraphQL/SSE`, etc.; -3. Customize the storage adapter to meet the storage requirements of different environments, such as `react-native`; -4. Any combination of the above, such as the official [uniapp adapter](https://github.com/alovajs/adapter-uniapp), which includes request adapters and storage adapters. - -#### Custom request strategy - -Request strategies can help developers write high-performance functions more efficiently. Although the official [@alova/scene](/tutorial/strategy) provides some common request strategies, it is not enough It is a good choice to customize your own reusable request strategy based on alova to meet the business scenarios related to various requests of developers, and you can also publish them on npm for everyone to use. - -:::tip Submit your project - -If you have written an alova-based js library, please submit your project in [this issue](https://github.com/alovajs/alova/issues/165), which will allow your project to be displayed on the alova official website Opportunity. - -::: - -### Participate in community/PR review - -If you are interested in technical communication, then it may be more suitable for you to participate in more community communication. You can participate in the discussion of bugs and new features in Github issues, or in [Github Discussion](https://github.com/alovajs/alova/discussions), [Discord](https://discord.gg/S47QGJgkVb) or [QQ channel](https://pd.qq.com/s/1cdjx0nnw) to answer questions for others, which allows you to communicate with people from all over the world, which is a very interesting thing. - -At the same time, you can also participate in PR review in [pull request](https://github.com/alovajs/alova/pulls), which is also a topic of communication. - -### Publish and disseminate information about alova - -You can publish or repost any information that is beneficial to the development of alova on any social platform, short video platform, or technology sharing platform, which will help increase the influence of alova. We will filter out relevant articles or videos and display them on the alova official website. Here are some good articles: - -- [It’s time to replace your axios](https://medium.com/@huzhen555/its-time-to-replace-your-axios-12c014833b04) -- [Alova.js 筆記-試用相較 axios 更輕量、更高集成的請求庫](https://uu9924079.medium.com/alova-js-%E7%AD%86%E8%A8%98-%E8%A9%A6%E7%94%A8%E7%9B%B8%E8%BC%83-axios-%E6%9B%B4%E8%BC%95%E9%87%8F-%E6%9B%B4%E9%AB%98%E9%9B%86%E6%88%90%E7%9A%84%E8%AB%8B%E6%B1%82%E5%BA%AB-546ec5424df9) - -### Share experience - -If you have alova experience worth sharing, or better practice cases, you can share it in [Github Discussion's Practices channel](https://github.com/alovajs/alova/discussions/categories/practices), better The sharing of will also be displayed in the official documentation. - -### Collaboration - -We welcome project collaboration with any organization or individual, which can help us expand alova's influence and accelerate the development of the project. If you have any suggestions or intentions for cooperation, please contact us via email **hujou555@gmail.com**. - -### Donation - -You can donate to the project through the following 3 ways. Please go to the donation page to view the donation privileges. - -1. [Github sponsors](https://github.com/sponsors/alovajs) -2. [OpenCollective](https://opencollective.com/alova) -3. [afdian](https://afdian.net/a/huzhen555) - -### Correct or add docs - -If you need to add new documentation content, or find errors in alova's documentation, such as wrong examples, wrong words, incorrect descriptions, or unmentioned content, you can [create a new document repository issue](https://github.com/alovajs/alovajs.github.io/issues/new), or [new document warehouse pull request](https://github.com/alovajs/alovajs.github.io/fork) directly modify the error, which should be the better option, and we welcome any suggestions or contributions to improve the documentation. - -### Translate Documentation - -If you are proficient in different languages, you are welcome to translate the alova documentation, which will help expand the use and audience of alova. - -## Become a core team member - -Check [HERE](./become-core-member) for details +--- +title: Contribution Guidelines +--- + +# alova Contribution Guidelines + +Hello, I'm glad to meet you here. This is a detailed alova contribution guidelines, which includes detailed guidance on contributing to all aspects of alova. Please continue reading. + +## Preface + +In the past period of time, we have received active participation information from developers from all over the world in Github issues and Github Discussion, which means that alova is being loved by more and more developers. Even so, alova is still a rookie, and it still has a long way to go. + +**We expect to make alova a common project for everyone who is willing to participate, and we encourage everyone to become a contributor to the alova community with an open and inclusive attitude. Moreover, we believe that contributing to alova is not limited to code contributions, but participating in any activities that are beneficial to the development of alova is considered to contribute to alova.** Participating in contributions now can win you more effective contribution opportunities, and it allows you to contribute to the world Even if you are a junior developer, as long as the idea is in line with [alova's mission and design principles](#alova-mission-and-design-principles), please generously participate! + +> There is a [Code of conduct](./code-of-conduct) here, please refer to it. + +## Contribution directory + +Here are 13 places where you can contribute, but not limited to these, you can choose the part you want to participate in, and link to the corresponding location for detailed reading: + +- [use alova in your project](#use-alova-in-your-project) +- [star alova](#star-alova) +- [report bug](#report-bug) +- [Propose new feature ideas](#propose-new-feature-ideas) +- [Pull Request](#pull-request) +- [Create an adapter or strategy library based on alova](#create-an-adapter-or-strategy-library-based-on-alova) +- [Participate in community/PR review](#participate-in-community-pr-review) +- [Publish and disseminate information about alova](#publish-and-disseminate-information-about-alova) +- [Share experience](#share-experience) +- [Collaboration](#collaboration) +- [Donation](#donation) +- [Correct or add docs](#correct-or-add-docs) +- [Translate docs](#Translate-docs) + +## alova mission and design principles + +### alova Mission + +alova's mission gives it a clear development direction, which clearly defines what alova should do. + +alova is a lightweight request strategy library, **Its mission is to enable developers to achieve more efficient Client-Server data interaction by less codes**. + +For developers, alova provides them with a simple API and out-of-the-box advanced request functions, as well as various simple and high-performance request strategy modules. For application users, they can enjoy the benefits of alova The smooth experience brought by high-performance data interaction, therefore, alova has the following features: + +1. The api design is similar to axios, allowing users to learn at a lower cost; +2. Deep binding of the UI framework, greatly improving the benefits of developers; +3. Out-of-the-box advanced functions to avoid repeated packaging, such as request sharing, request caching, etc., to reduce developers' repeated packaging; +4. The platform-independent coding way, and it can perfectly migrate in different platform; +5. High scalability design, which can encapsulate high-reusability and high-performance business-related request strategies; +6. Highly aggregated and low-coupling method design improves API code maintainability; + +### alova design principles + +The design principles points out how it should be designed, the following is the core design concept of alova. + +1. `Method` proxy design, high aggregation, platform-independent design, throughout the request, you should be able to access it in any request function, from another perspective, the information related to the request should also be placed in the method instance ; +2. Lightweight, try to keep the source code concise in coding, such as avoiding repeated code, merging variable declarations, prototype chain function encapsulation, no similar API, tree shaking, but long variable names are allowed, because it will be used when compiling will be replaced by a single letter; +3. Highly scalable design. First, the design of alova uses a large number of adapter patterns and hook functions. For example, adapters include `requestAdapter`, `storageAdapter`, etc., and hook functions include `beforeRequest`, `responded`, `transformData`, `localCache`, etc., and most of them have default behaviors, which are designed to be easy to use while retaining high scalability; second, global request parameters can be overwritten, such as `timeout`, `shareRequest`, etc., for These parameters can be set individually for special requests. +4. The api design is universal. First, it means that the function of this api has a high level of abstraction, rather than a specific business. Second, the api design is scalable to adapt to the needs of the api iteration + +> The api universal design is only applicable to the alova library. If you are conceiving a request strategy, you can design it according to the specific business. + +## Select the contribution point you are interested in + +### Use alova in your project + +We believe that your use of alova in the project is also a contributor to alova, which is also telling people that alova is a trustworthy open source project, please open [this issue](https://github.com/alovajs/alova/issues/165), which may give you the opportunity to display your project on the alova official website. + +### Star alova + +Although this may be considered insignificant, it represents your recognition of alova, and every star is very important to alova. star alova in the right top of [alova's Github repository](https://github.com/alovajs/alova), which is important to us. + +### Report bug + +Please move to [Github new issue](https://github.com/alovajs/alova/issues/new/choose) and select the corresponding template to submit. Detailed instructions will be displayed in the submitted issue. + +**PLEASE NOTE:** If you want to ask questions about alova, please go to [Github Discussion](https://github.com/alovajs/alova/discussions) to create a question. That would be closed immediately when ask a question in issues. + +### Propose new feature ideas + +In order for alova to achieve its value and goals, before submitting a new feature idea, please carefully read [alova mission and design principles](#alova-mission-and-design-principles), and ensure that your new idea is in line with alova's mission and design philosophy design concept. + +Then, please submit it in [🚀 New Feature Proposal](https://github.com/alovajs/alova/issues/new?assignees=&labels=feature-request&projects=&template=FEATURE_REQUEST_en.yml), the detailed description will be Displayed when submitting an issue. + +### Pull Request + +You can contribute the following 3 aspects of code through pull request. If you are a new partner who is interested in participating, all `good first issue` issues are listed in [Github contribution list](https://github.com/alovajs/alova/contribute), it is used to Tell new partners who are interested in contributing that this is a good start. + +#### Bug fix + +Issues marked as [`bug:confirmed`](https://github.com/alovajs/alova/labels/bug%3Aconfirmed) in Github issues are all confirmed bugs, you can choose freely. + +If you encounter a bug yourself, please also [report a bug](#report-bug) first to ensure that the bug is confirmed to avoid invalid pull requests. + +#### New feature development + +Issues marked as [`feature-request:confirmed`](https://github.com/alovajs/alova/labels/feature-request%3Aconfirmed) in Github issues are new features that have been confirmed, you You can choose freely. + +If you have an idea for adding a new feature, please also [submit an issue of a new feature idea](#propose-new-feature-ideas) to ensure that the idea is confirmed to avoid invalid pull requests. + +#### Project configuration + +If you are very good at project configuration and find deficiencies in the alova project, such as incomplete configuration, too old configuration version, insufficient automation (including project development automation and Github warehouse management automation), you can also press [New Feature development](#new-feature-development) process to contribute. + +:::warning IMPORTANT + +1. Please read the [Developing Guidelines](./developing-guidelines) carefully before developing, it can guide you step by step on how to contribute code. +2. When you identify an issue that needs to be resolved, please make sure that it has not been flagged by someone else's pull request, which means that it has been pre-occupied. + +::: + +### Create an adapter or strategy library based on alova + +alova provides high-extensibility features, and you can write your own js library based on it. + +#### Custom Adapter + +Customize various adapters to meet the operating requirements in different environments. The following directions are available for reference: + +1. Customize statesHook, which can be executed under different UI frameworks, such as `solid/qwik`, currently supports `react/vue/svelte`, please read [Custom statesHook](/tutorial/custom/custom-stateshook); +2. Customize the request adapter, so that alova can cooperate with more request schemes, such as `GraphQL/SSE`, etc.; +3. Customize the storage adapter to meet the storage requirements of different environments, such as `react-native`; +4. Any combination of the above, such as the official [uniapp adapter](https://github.com/alovajs/adapter-uniapp), which includes request adapters and storage adapters. + +#### Custom request strategy + +Request strategies can help developers write high-performance functions more efficiently. Although the official [@alova/scene](/tutorial/strategy) provides some common request strategies, it is not enough It is a good choice to customize your own reusable request strategy based on alova to meet the business scenarios related to various requests of developers, and you can also publish them on npm for everyone to use. + +:::tip Submit your project + +If you have written an alova-based js library, please submit your project in [this issue](https://github.com/alovajs/alova/issues/165), which will allow your project to be displayed on the alova official website Opportunity. + +::: + +### Participate in community/PR review + +If you are interested in technical communication, then it may be more suitable for you to participate in more community communication. You can participate in the discussion of bugs and new features in Github issues, or in [Github Discussion](https://github.com/alovajs/alova/discussions), [Discord](https://discord.gg/S47QGJgkVb) or [QQ channel](https://pd.qq.com/s/1cdjx0nnw) to answer questions for others, which allows you to communicate with people from all over the world, which is a very interesting thing. + +At the same time, you can also participate in PR review in [pull request](https://github.com/alovajs/alova/pulls), which is also a topic of communication. + +### Publish and disseminate information about alova + +You can publish or repost any information that is beneficial to the development of alova on any social platform, short video platform, or technology sharing platform, which will help increase the influence of alova. We will filter out relevant articles or videos and display them on the alova official website. Here are some good articles: + +- [It’s time to replace your axios](https://medium.com/@huzhen555/its-time-to-replace-your-axios-12c014833b04) +- [Alova.js 筆記-試用相較 axios 更輕量、更高集成的請求庫](https://uu9924079.medium.com/alova-js-%E7%AD%86%E8%A8%98-%E8%A9%A6%E7%94%A8%E7%9B%B8%E8%BC%83-axios-%E6%9B%B4%E8%BC%95%E9%87%8F-%E6%9B%B4%E9%AB%98%E9%9B%86%E6%88%90%E7%9A%84%E8%AB%8B%E6%B1%82%E5%BA%AB-546ec5424df9) + +### Share experience + +If you have alova experience worth sharing, or better practice cases, you can share it in [Github Discussion's Practices channel](https://github.com/alovajs/alova/discussions/categories/practices), better The sharing of will also be displayed in the official documentation. + +### Collaboration + +We welcome project collaboration with any organization or individual, which can help us expand alova's influence and accelerate the development of the project. If you have any suggestions or intentions for cooperation, please contact us via email **hujou555@gmail.com**. + +### Donation + +You can donate to the project through the following 3 ways. Please go to the donation page to view the donation privileges. + +1. [Github sponsors](https://github.com/sponsors/alovajs) +2. [OpenCollective](https://opencollective.com/alova) +3. [afdian](https://afdian.net/a/huzhen555) + +### Correct or add docs + +If you need to add new documentation content, or find errors in alova's documentation, such as wrong examples, wrong words, incorrect descriptions, or unmentioned content, you can [create a new document repository issue](https://github.com/alovajs/alovajs.github.io/issues/new), or [new document warehouse pull request](https://github.com/alovajs/alovajs.github.io/fork) directly modify the error, which should be the better option, and we welcome any suggestions or contributions to improve the documentation. + +### Translate Documentation + +If you are proficient in different languages, you are welcome to translate the alova documentation, which will help expand the use and audience of alova. + +## Become a core team member + +Check [HERE](./become-core-member) for details diff --git a/versioned_docs/version-2.x/contributing/02-become-core-member.md b/versioned_docs/version-2.x/contributing/02-become-core-member.md index 501677561..0ab350025 100644 --- a/versioned_docs/version-2.x/contributing/02-become-core-member.md +++ b/versioned_docs/version-2.x/contributing/02-become-core-member.md @@ -1,32 +1,31 @@ ---- -title: Become a core team member -sidebar_position: 20 ---- - -🤝🤝🤝 If you also agree with alovajs’ philosophy, then let us create the next generation request library together! - -## Responsibilities - -1. Responsible for the development and maintenance of vscode plug-in projects, request scenario modules and adapter projects, as well as the Hongmeng version of alovajs. -2. Write and optimize relevant development documents. -3. In-depth participation in the entire life cycle of project design and implementation, promotion and maintenance. -4. Participate in project development decision-making and commercialization exploration. - -## Revenue - -1. With the title of core member, you have the opportunity to enhance your reputation in the front-end field, thereby broadening your career development path. -2. More opportunities for public sharing and opportunities to contact more front-end personnel. -3. Broaden project thinking and technical experience through in-depth participation in project design, implementation and operation. -4. Decision-making power on projects. -5. Profit sharing. - -## Require - -1. Agree with alova’s design philosophy and development direction, and be willing to work hard to move forward together. -2. Passion for technology, sense of responsibility, and teamwork. -3. Technical capabilities only need to cover existing needs. -4. Have some spare time. - -## How to join - -If you are interested, please send an email to [hujou555@gmail.com](mailto:hujou555@gmail.com), and be sure to add `alova core member application` at the beginning of email. +--- +title: Become a core team member +--- + +🤝🤝🤝 If you also agree with alovajs’ philosophy, then let us create the next generation request library together! + +## Responsibilities + +1. Responsible for the development and maintenance of vscode plug-in projects, request scenario modules and adapter projects, as well as the Hongmeng version of alovajs. +2. Write and optimize relevant development documents. +3. In-depth participation in the entire life cycle of project design and implementation, promotion and maintenance. +4. Participate in project development decision-making and commercialization exploration. + +## Revenue + +1. With the title of core member, you have the opportunity to enhance your reputation in the front-end field, thereby broadening your career development path. +2. More opportunities for public sharing and opportunities to contact more front-end personnel. +3. Broaden project thinking and technical experience through in-depth participation in project design, implementation and operation. +4. Decision-making power on projects. +5. Profit sharing. + +## Require + +1. Agree with alova’s design philosophy and development direction, and be willing to work hard to move forward together. +2. Passion for technology, sense of responsibility, and teamwork. +3. Technical capabilities only need to cover existing needs. +4. Have some spare time. + +## How to join + +If you are interested, please send an email to [hujou555@gmail.com](mailto:hujou555@gmail.com), and be sure to add `alova core member application` at the beginning of email. diff --git a/versioned_docs/version-2.x/contributing/03-developing-guidelines.md b/versioned_docs/version-2.x/contributing/03-developing-guidelines.md index 357669268..19997d0a8 100644 --- a/versioned_docs/version-2.x/contributing/03-developing-guidelines.md +++ b/versioned_docs/version-2.x/contributing/03-developing-guidelines.md @@ -1,138 +1,137 @@ ---- -title: Developing Guidelines -sidebar_position: 30 ---- - -:::info version required - -Node.js 16+, npm 8+ - -::: - -## 1. Fork repository - -[Open alova fork page](https://github.com/alovajs/alova/fork), click "Create fork" and clone the forked repository to local. - -## 2. Clone to local - -Use the `git clone` command line, or the `Github Desktop` application to clone forked project. - -## 3. New pull request - -You can [create pull request through a forked repo](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork) after writing code. You can also commit code in arbitrary batches, without commiting a complete code. - -## 4. Code something in your computer - -### Install dependencies - -Install dependencies using `npm install`. - -### Install the recommended plugin(vscode) - -If you are using vscode, it is recommended that you install the following plugins: - -- eslint: check code quality. -- prettier: formatting code. -- jest: Automatically execute unit test cases, and execute individual collection or unit test cases. -- EditorConfig: Make sure the file format is consistent. - -### Project Structure - -``` -|-.github -| |-ISSUE_TEMPLATE -> github issues template -| |-workflows -> github action -|-.husky -> husky configuration -|-.vscode -> vscode configuration -|-config -> rollup package files -|-src -> source code -|-test -> unit test suits -| |-browser -> browser environment unit test suits -| |-server -> SSR unit test suits -| |-components -> Unit test components -| |-mockServer.ts -> mock apis (msw) -|-typings -> ts declaration -|- Other configuration files - -``` - -### Coding specifications - -#### Code format - -If you install the `prettier` plugin, it will automatically format codes every time you save files, so you don't have to worry about the format. - -#### Minimize code - -Lightweight is one of the alova's features, so it is necessary to minimize the amount of coding when coding. Here are a few coding specifications that need to be followed: - -1. Avoid the same code block, which can reduce the amount of code in the library, but two lines of code may not be worth encapsulating; -2. Use a variable declarator to aggregate variable declarations, for example: - -```javascript -// ❌ -const a = 1; -const b = 2; - -// ✅ -const a = 1, - b = 2; -``` - -3. Use constants to save js built-in values and prototype methods to reduce the amount of code in the compilation phase of `uglify`. built-in values and prototype methods that often used are defined in `src/utils/variables.ts`. - -```javascript -// ❌ -if (a === false) { - //... -} -arr.forEach(item => { - //... -}); - -// ✅ -import { falseValue, forEach } from '@/utils/variables'; -if (a === falseValue) { - //... -} -forEach(arr, item => { - //... -}); -``` - -## 5. Unit Testing Guidelines - -After finish code, it is necessary to add corresponding unit tests. - -The alova project uses **jest** as the unit test framework, and msw as the mock server. It is recommended to use the TDD mode. After modifying code every time, please pass the corresponding unit test. - -:::warning IMPORTANT - -When you're ready to commit your code, make sure all your unit tests are passed. When you're working on a pull request, you can have multiple small commits, and GitHub can automatically squash them before merging them. - -::: - -1. To add browser-related unit test cases, please add them to the corresponding test collection in `test/browser`, if there is no suitable test suits, you can create one by yourself; -2. Add SSR-related unit test cases, please add them to the corresponding test collection in `test/server`, if there is no suitable test suits, you can create one by yourself; - -### Run and debug a single unit test or suits - -It is recommended to use the **jest** plugin (one of the plugins recommended above) to test a single use case or a suit. You can right-click the specified unit test, select `Run Test` to run it, and select `Debug Test` to debug it with breakpoint. - -![image](https://github.com/alovajs/alova/assets/29848971/a94ba9db-c100-472f-b870-6bcecb031bea) - -### Run all unit tests - -1. Use the **jest** plugin to run: - -![image](https://github.com/alovajs/alova/assets/29848971/5af3ff15-16b7-4b28-9ae6-d0b5a236b181) - -2. Run the browser unit tests with command line `npm run test:browser`, run the SSR unit tests with `npm run test:node`, and run both at the same time with `npm run test`. - -## 6. Commit codes - -alova uses [semantic-release](https://semantic-release.gitbook.io) as an automatic release tool, which can automatically release new version packages after merging code into `main` , and generate `CHANGELOG`, but you need to ensure that the committed message format follows [commit information convention](https://www.conventionalcommits.org/en/v1.0.0/), it is recommended that use `npm run commit` to automatically generate a git message that conforms to the specification. - -## 7. Writing docs - -If you are adding a new feature, you can try to add the relevant documentation of the new feature. For details, please read [Correcting or add docs](/contributing/overview#correct-or-add-docs), otherwise please explain it in the pull request. +--- +title: Developing Guidelines +--- + +:::info version required + +Node.js 16+, npm 8+ + +::: + +## 1. Fork repository + +[Open alova fork page](https://github.com/alovajs/alova/fork), click "Create fork" and clone the forked repository to local. + +## 2. Clone to local + +Use the `git clone` command line, or the `Github Desktop` application to clone forked project. + +## 3. New pull request + +You can [create pull request through a forked repo](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork) after writing code. You can also commit code in arbitrary batches, without commiting a complete code. + +## 4. Code something in your computer + +### Install dependencies + +Install dependencies using `npm install`. + +### Install the recommended plugin(vscode) + +If you are using vscode, it is recommended that you install the following plugins: + +- eslint: check code quality. +- prettier: formatting code. +- jest: Automatically execute unit test cases, and execute individual collection or unit test cases. +- EditorConfig: Make sure the file format is consistent. + +### Project Structure + +``` +|-.github +| |-ISSUE_TEMPLATE -> github issues template +| |-workflows -> github action +|-.husky -> husky configuration +|-.vscode -> vscode configuration +|-config -> rollup package files +|-src -> source code +|-test -> unit test suits +| |-browser -> browser environment unit test suits +| |-server -> SSR unit test suits +| |-components -> Unit test components +| |-mockServer.ts -> mock apis (msw) +|-typings -> ts declaration +|- Other configuration files + +``` + +### Coding specifications + +#### Code format + +If you install the `prettier` plugin, it will automatically format codes every time you save files, so you don't have to worry about the format. + +#### Minimize code + +Lightweight is one of the alova's features, so it is necessary to minimize the amount of coding when coding. Here are a few coding specifications that need to be followed: + +1. Avoid the same code block, which can reduce the amount of code in the library, but two lines of code may not be worth encapsulating; +2. Use a variable declarator to aggregate variable declarations, for example: + +```javascript +// ❌ +const a = 1; +const b = 2; + +// ✅ +const a = 1, + b = 2; +``` + +3. Use constants to save js built-in values and prototype methods to reduce the amount of code in the compilation phase of `uglify`. built-in values and prototype methods that often used are defined in `src/utils/variables.ts`. + +```javascript +// ❌ +if (a === false) { + //... +} +arr.forEach(item => { + //... +}); + +// ✅ +import { falseValue, forEach } from '@/utils/variables'; +if (a === falseValue) { + //... +} +forEach(arr, item => { + //... +}); +``` + +## 5. Unit Testing Guidelines + +After finish code, it is necessary to add corresponding unit tests. + +The alova project uses **jest** as the unit test framework, and msw as the mock server. It is recommended to use the TDD mode. After modifying code every time, please pass the corresponding unit test. + +:::warning IMPORTANT + +When you're ready to commit your code, make sure all your unit tests are passed. When you're working on a pull request, you can have multiple small commits, and GitHub can automatically squash them before merging them. + +::: + +1. To add browser-related unit test cases, please add them to the corresponding test collection in `test/browser`, if there is no suitable test suits, you can create one by yourself; +2. Add SSR-related unit test cases, please add them to the corresponding test collection in `test/server`, if there is no suitable test suits, you can create one by yourself; + +### Run and debug a single unit test or suits + +It is recommended to use the **jest** plugin (one of the plugins recommended above) to test a single use case or a suit. You can right-click the specified unit test, select `Run Test` to run it, and select `Debug Test` to debug it with breakpoint. + +![image](https://github.com/alovajs/alova/assets/29848971/a94ba9db-c100-472f-b870-6bcecb031bea) + +### Run all unit tests + +1. Use the **jest** plugin to run: + +![image](https://github.com/alovajs/alova/assets/29848971/5af3ff15-16b7-4b28-9ae6-d0b5a236b181) + +2. Run the browser unit tests with command line `npm run test:browser`, run the SSR unit tests with `npm run test:node`, and run both at the same time with `npm run test`. + +## 6. Commit codes + +alova uses [semantic-release](https://semantic-release.gitbook.io) as an automatic release tool, which can automatically release new version packages after merging code into `main` , and generate `CHANGELOG`, but you need to ensure that the committed message format follows [commit information convention](https://www.conventionalcommits.org/en/v1.0.0/), it is recommended that use `npm run commit` to automatically generate a git message that conforms to the specification. + +## 7. Writing docs + +If you are adding a new feature, you can try to add the relevant documentation of the new feature. For details, please read [Correcting or add docs](/contributing/overview#correct-or-add-docs), otherwise please explain it in the pull request. diff --git a/versioned_docs/version-2.x/contributing/04-code-of-conduct.md b/versioned_docs/version-2.x/contributing/04-code-of-conduct.md index 580d03abe..833a9ab0c 100644 --- a/versioned_docs/version-2.x/contributing/04-code-of-conduct.md +++ b/versioned_docs/version-2.x/contributing/04-code-of-conduct.md @@ -1,137 +1,136 @@ ---- -title: Code of Conduct -sidebar_position: 40 ---- - -# Contributor Covenant Code of Conduct - -## Our Pledge - -We as members, contributors, and leaders pledge to make participation in our -community a harassment-free experience for everyone, regardless of age, body -size, visible or invisible disability, ethnicity, sex characteristics, gender -identity and expression, level of experience, education, socio-economic status, -nationality, personal appearance, race, caste, color, religion, or sexual -identity and orientation. - -We pledge to act and interact in ways that contribute to an open, welcoming, -diverse, inclusive, and healthy community. - -## Our Standards - -Examples of behavior that contributes to a positive environment for our -community include: - -- Demonstrating empathy and kindness toward other people -- Being respectful of differing opinions, viewpoints, and experiences -- Giving and gracefully accepting constructive feedback -- Accepting responsibility and apologizing to those affected by our mistakes, - and learning from the experience -- Focusing on what is best not just for us as individuals, but for the overall - community - -Examples of unacceptable behavior include: - -- The use of sexualized language or imagery, and sexual attention or advances of - any kind -- Trolling, insulting or derogatory comments, and personal or political attacks -- Public or private harassment -- Publishing others' private information, such as a physical or email address, - without their explicit permission -- Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Enforcement Responsibilities - -Community leaders are responsible for clarifying and enforcing our standards of -acceptable behavior and will take appropriate and fair corrective action in -response to any behavior that they deem inappropriate, threatening, offensive, -or harmful. - -Community leaders have the right and responsibility to remove, edit, or reject -comments, commits, code, wiki edits, issues, and other contributions that are -not aligned to this Code of Conduct, and will communicate reasons for moderation -decisions when appropriate. - -## Scope - -This Code of Conduct applies within all community spaces, and also applies when -an individual is officially representing the community in public spaces. -Examples of representing our community include using an official e-mail address, -posting via an official social media account, or acting as an appointed -representative at an online or offline event. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported to the community leaders responsible for enforcement at -[INSERT CONTACT METHOD]. -All complaints will be reviewed and investigated promptly and fairly. - -All community leaders are obligated to respect the privacy and security of the -reporter of any incident. - -## Enforcement Guidelines - -Community leaders will follow these Community Impact Guidelines in determining -the consequences for any action they deem in violation of this Code of Conduct: - -### 1. Correction - -**Community Impact**: Use of inappropriate language or other behavior deemed -unprofessional or unwelcome in the community. - -**Consequence**: A private, written warning from community leaders, providing -clarity around the nature of the violation and an explanation of why the -behavior was inappropriate. A public apology may be requested. - -### 2. Warning - -**Community Impact**: A violation through a single incident or series of -actions. - -**Consequence**: A warning with consequences for continued behavior. No -interaction with the people involved, including unsolicited interaction with -those enforcing the Code of Conduct, for a specified period of time. This -includes avoiding interactions in community spaces as well as external channels -like social media. Violating these terms may lead to a temporary or permanent -ban. - -### 3. Temporary Ban - -**Community Impact**: A serious violation of community standards, including -sustained inappropriate behavior. - -**Consequence**: A temporary ban from any sort of interaction or public -communication with the community for a specified period of time. No public or -private interaction with the people involved, including unsolicited interaction -with those enforcing the Code of Conduct, is allowed during this period. -Violating these terms may lead to a permanent ban. - -### 4. Permanent Ban - -**Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an -individual, or aggression toward or disparagement of classes of individuals. - -**Consequence**: A permanent ban from any sort of public interaction within the -community. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], -version 2.1, available at -[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. - -Community Impact Guidelines were inspired by -[Mozilla's code of conduct enforcement ladder][mozilla coc]. - -For answers to common questions about this code of conduct, see the FAQ at -[https://www.contributor-covenant.org/faq][faq]. Translations are available at -[https://www.contributor-covenant.org/translations][translations]. - -[homepage]: https://www.contributor-covenant.org -[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html -[mozilla coc]: https://github.com/mozilla/diversity -[faq]: https://www.contributor-covenant.org/faq -[translations]: https://www.contributor-covenant.org/translations +--- +title: Code of Conduct +--- + +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +- Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +- The use of sexualized language or imagery, and sexual attention or advances of + any kind +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email address, + without their explicit permission +- Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +[INSERT CONTACT METHOD]. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][mozilla coc]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][faq]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[mozilla coc]: https://github.com/mozilla/diversity +[faq]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations diff --git a/versioned_docs/version-2.x/tutorial/01-example/01-init-page.md b/versioned_docs/version-2.x/tutorial/01-example/01-init-page.md index d38ee50b1..93bb52c2d 100644 --- a/versioned_docs/version-2.x/tutorial/01-example/01-init-page.md +++ b/versioned_docs/version-2.x/tutorial/01-example/01-init-page.md @@ -1,16 +1,15 @@ ---- -title: (vue)page initialization request -sidebar_position: 10 ---- - -import InitPage from '@site/example-links/InitPage'; - -> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started); - - - -:::info example description - -This example mainly shows the simplicity of the request, it will return stateful request status, data, etc. which can be used directly in the view and managed directly by alova - -::: +--- +title: page initialization request +--- + +import InitPage from '@site/example-links/InitPage'; + +> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started); + + + +:::info example description + +This example mainly shows the simplicity of the request, it will return stateful request status, data, etc. which can be used directly in the view and managed directly by alova + +::: diff --git a/versioned_docs/version-2.x/tutorial/01-example/02-submit-form.md b/versioned_docs/version-2.x/tutorial/01-example/02-submit-form.md index b5f3bcce0..aa907fba6 100644 --- a/versioned_docs/version-2.x/tutorial/01-example/02-submit-form.md +++ b/versioned_docs/version-2.x/tutorial/01-example/02-submit-form.md @@ -1,16 +1,15 @@ ---- -title: (vue)form submission -sidebar_position: 20 ---- - -import SubmitForm from '@site/example-links/SubmitForm'; - -> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started); - - - -:::info example description - -This example mainly shows the basic data submission encoding method - -::: +--- +title: form submission +--- + +import SubmitForm from '@site/example-links/SubmitForm'; + +> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started); + + + +:::info example description + +This example mainly shows the basic data submission encoding method + +::: diff --git a/versioned_docs/version-2.x/tutorial/01-example/03-condition-search.md b/versioned_docs/version-2.x/tutorial/01-example/03-condition-search.md index e7a58e069..d5a3696b5 100644 --- a/versioned_docs/version-2.x/tutorial/01-example/03-condition-search.md +++ b/versioned_docs/version-2.x/tutorial/01-example/03-condition-search.md @@ -1,16 +1,15 @@ ---- -title: (vue)Conditional Search -sidebar_position: 30 ---- - -import ConditionSearch from '@site/example-links/ConditionSearch'; - -> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started); - - - -:::info example description - -This example mainly shows the simplicity of data search. There is no need for the developer to maintain the state of the request, data, etc., and there is no need to manually trigger the request to send. Just bind the state of the search conditions. - -::: +--- +title: Conditional Search +--- + +import ConditionSearch from '@site/example-links/ConditionSearch'; + +> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started); + + + +:::info example description + +This example mainly shows the simplicity of data search. There is no need for the developer to maintain the state of the request, data, etc., and there is no need to manually trigger the request to send. Just bind the state of the search conditions. + +::: diff --git a/versioned_docs/version-2.x/tutorial/01-example/04-memory-cache.md b/versioned_docs/version-2.x/tutorial/01-example/04-memory-cache.md index 6768af625..c26fd628f 100644 --- a/versioned_docs/version-2.x/tutorial/01-example/04-memory-cache.md +++ b/versioned_docs/version-2.x/tutorial/01-example/04-memory-cache.md @@ -1,21 +1,20 @@ ---- -title: (vue)Response Cache - Memory Mode -sidebar_position: 40 ---- - -import MemoryCache from '@site/example-links/MemoryCache'; - -> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started); - - - -:::info example description - -The memory cache mode stores the response data in memory, and the cache is invalid when the page is refreshed. - -_Operation guide:_ - -1. Click on the following student list item, the requesting student details will be sent, and the modal box will display the Loading status; -2. Click the mask to close the pop-up box and reopen it. At this time, the cache will be hit and the student details will be displayed immediately, and the request record will no longer be printed in Request Records; - -::: +--- +title: Response Cache - Memory Mode +--- + +import MemoryCache from '@site/example-links/MemoryCache'; + +> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started); + + + +:::info example description + +The memory cache mode stores the response data in memory, and the cache is invalid when the page is refreshed. + +_Operation guide:_ + +1. Click on the following student list item, the requesting student details will be sent, and the modal box will display the Loading status; +2. Click the mask to close the pop-up box and reopen it. At this time, the cache will be hit and the student details will be displayed immediately, and the request record will no longer be printed in Request Records; + +::: diff --git a/versioned_docs/version-2.x/tutorial/01-example/05-storage-placeholder.md b/versioned_docs/version-2.x/tutorial/01-example/05-storage-placeholder.md index 9fd88b854..6ef9c18c8 100644 --- a/versioned_docs/version-2.x/tutorial/01-example/05-storage-placeholder.md +++ b/versioned_docs/version-2.x/tutorial/01-example/05-storage-placeholder.md @@ -1,21 +1,20 @@ ---- -title: (vue)Response Cache - Cache Placeholder Mode -sidebar_position: 50 ---- - -import StoragePlaceholder from '@site/example-links/StoragePlaceholder'; - -> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started); - - - -:::info example description - -The cache placeholder mode is to persist the response data. It will be updated to the data state as placeholder data immediately after refreshing the page. At the same time, a request is sent. Developers can use placeholder data to replace the Loading state before responding. - -_Operation guide:_ - -1. Click `Reload page` to refresh the page, you no longer see the Loading status, but the old data is rendered and replaced with new data when the request is responded; -2. Click `Invalidate the data of placeholder` to invalidate the cached data, then you will see the Loading status again; - -::: +--- +title: Response Cache - Cache Placeholder Mode +--- + +import StoragePlaceholder from '@site/example-links/StoragePlaceholder'; + +> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started); + + + +:::info example description + +The cache placeholder mode is to persist the response data. It will be updated to the data state as placeholder data immediately after refreshing the page. At the same time, a request is sent. Developers can use placeholder data to replace the Loading state before responding. + +_Operation guide:_ + +1. Click `Reload page` to refresh the page, you no longer see the Loading status, but the old data is rendered and replaced with new data when the request is responded; +2. Click `Invalidate the data of placeholder` to invalidate the cached data, then you will see the Loading status again; + +::: diff --git a/versioned_docs/version-2.x/tutorial/01-example/06-storage-restore.md b/versioned_docs/version-2.x/tutorial/01-example/06-storage-restore.md index bfa105451..c81262f0a 100644 --- a/versioned_docs/version-2.x/tutorial/01-example/06-storage-restore.md +++ b/versioned_docs/version-2.x/tutorial/01-example/06-storage-restore.md @@ -1,21 +1,20 @@ ---- -title: (vue)Response Caching - Restore Mode -sidebar_position: 60 ---- - -import StorageRestore from '@site/example-links/StorageRestore'; - -> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started); - - - -:::info example description - -The cache restore mode is to persist the response data. When the request hits the cache, the persistent cached data will be returned, and no more requests will be issued. It is generally used for some data that needs server management but does not change for a certain period of time. The following is an example of the restore mode of holiday information. - -_Operation guide:_ - -1. Click `Reload page` to refresh the page, you will no longer see the Loading status, but will use the cached data and render it to the page immediately, and no longer send requests; -2. Click `Invalidate the data of placeholder` to invalidate the cached data, then you will see the Loading status again; - -::: +--- +title: Response Caching - Restore Mode +--- + +import StorageRestore from '@site/example-links/StorageRestore'; + +> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started); + + + +:::info example description + +The cache restore mode is to persist the response data. When the request hits the cache, the persistent cached data will be returned, and no more requests will be issued. It is generally used for some data that needs server management but does not change for a certain period of time. The following is an example of the restore mode of holiday information. + +_Operation guide:_ + +1. Click `Reload page` to refresh the page, you will no longer see the Loading status, but will use the cached data and render it to the page immediately, and no longer send requests; +2. Click `Invalidate the data of placeholder` to invalidate the cached data, then you will see the Loading status again; + +::: diff --git a/versioned_docs/version-2.x/tutorial/01-example/07-update-state.md b/versioned_docs/version-2.x/tutorial/01-example/07-update-state.md index 00f6ed794..50addbe55 100644 --- a/versioned_docs/version-2.x/tutorial/01-example/07-update-state.md +++ b/versioned_docs/version-2.x/tutorial/01-example/07-update-state.md @@ -1,20 +1,19 @@ ---- -title: (vue)Update state across components/pages -sidebar_position: 70 ---- - -import UpdateState from '@site/example-links/UpdateState'; - -> The example uses vue3 as an example, but you can also use alova in react and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details; - - - -:::info example description - -When you need to update state across components or pages, you are often limited by the component hierarchy. Here is a way to break this limitation. - -_Operation guidance:_ - -In the edit box or edit page, after adding or editing list data, the list data is updated across component levels. - -::: +--- +title: Update state across components/pages +--- + +import UpdateState from '@site/example-links/UpdateState'; + +> The example uses vue3 as an example, but you can also use alova in react and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details; + + + +:::info example description + +When you need to update state across components or pages, you are often limited by the component hierarchy. Here is a way to break this limitation. + +_Operation guidance:_ + +In the edit box or edit page, after adding or editing list data, the list data is updated across component levels. + +::: diff --git a/versioned_docs/version-2.x/tutorial/01-example/08-prefetch.md b/versioned_docs/version-2.x/tutorial/01-example/08-prefetch.md index ab2690dc1..8428f1ca1 100644 --- a/versioned_docs/version-2.x/tutorial/01-example/08-prefetch.md +++ b/versioned_docs/version-2.x/tutorial/01-example/08-prefetch.md @@ -1,21 +1,20 @@ ---- -title: (vue)Data Prefetch -sidebar_position: 80 ---- - -import Prefetch from '@site/example-links/Prefetch'; - -> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started); - - - -:::info example description - -Data pre-fetching is a way to predict user operation behavior to achieve a strategy of displaying content to users faster. - -_Operation guide:_ - -1. Move the mouse to any list item and stay there for 0.2 seconds, a request for detailed data will be sent on the bottom panel; -2. Click to open this list item, you can see the detailed data immediately; - -::: +--- +title: Data Prefetch +--- + +import Prefetch from '@site/example-links/Prefetch'; + +> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started); + + + +:::info example description + +Data pre-fetching is a way to predict user operation behavior to achieve a strategy of displaying content to users faster. + +_Operation guide:_ + +1. Move the mouse to any list item and stay there for 0.2 seconds, a request for detailed data will be sent on the bottom panel; +2. Click to open this list item, you can see the detailed data immediately; + +::: diff --git a/versioned_docs/version-2.x/tutorial/01-example/09-load-more.md b/versioned_docs/version-2.x/tutorial/01-example/09-load-more.md index ebe27569f..048739832 100644 --- a/versioned_docs/version-2.x/tutorial/01-example/09-load-more.md +++ b/versioned_docs/version-2.x/tutorial/01-example/09-load-more.md @@ -1,23 +1,22 @@ ---- -title: (vue)Load more -sidebar_position: 90 ---- - -import LoadMore from '@site/example-links/LoadMore'; - -> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started); - - - -:::info example description - -Using pagination strategy, achieve a more high-performance and easy-to-use pagination function, automatic management of paging-related status, preloading of previous and previous pages, and automatic maintenance of data addition/editing/replacement/ Removed, and request-level stabilization. - -_Operation guide:_ - -1. After the initialization is completed, the next page of data will be preloaded, and there is no need to wait for scrolling down the page; -2. Adding, deleting, and modifying list items does not need to reset the list, it will be automatically processed into the same effect as the re-request; - -[usePagination documentation](/tutorial/strategy/usePagination) - -::: +--- +title: Load more +--- + +import LoadMore from '@site/example-links/LoadMore'; + +> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started); + + + +:::info example description + +Using pagination strategy, achieve a more high-performance and easy-to-use pagination function, automatic management of paging-related status, preloading of previous and previous pages, and automatic maintenance of data addition/editing/replacement/ Removed, and request-level stabilization. + +_Operation guide:_ + +1. After the initialization is completed, the next page of data will be preloaded, and there is no need to wait for scrolling down the page; +2. Adding, deleting, and modifying list items does not need to reset the list, it will be automatically processed into the same effect as the re-request; + +[usePagination documentation](/tutorial/strategy/usePagination) + +::: diff --git a/versioned_docs/version-2.x/tutorial/01-example/10-paginated-list.md b/versioned_docs/version-2.x/tutorial/01-example/10-paginated-list.md index a1e55d20d..88cfcfd4c 100644 --- a/versioned_docs/version-2.x/tutorial/01-example/10-paginated-list.md +++ b/versioned_docs/version-2.x/tutorial/01-example/10-paginated-list.md @@ -1,23 +1,22 @@ ---- -title: (vue)Paginated list -sidebar_position: 100 ---- - -import Pagination from '@site/example-links/Pagination'; - -> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started); - - - -:::info example description - -Using pagination strategy, achieve a more high-performance and easy-to-use pagination function, automatic management of paging-related status, preloading of previous and previous pages, and automatic maintenance of data addition/editing/replacement/ Removed, and request-level stabilization. - -_Operation guide:_ - -1. After the initialization is completed, the next page of data will be preloaded, and there is no need to wait when turning to the next page; -2. Adding, deleting, and modifying list items does not need to reset the list, it will be automatically processed into the same effect as the re-request; - -[usePagination documentation](/tutorial/strategy/usePagination) - -::: +--- +title: Paginated list +--- + +import Pagination from '@site/example-links/Pagination'; + +> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started); + + + +:::info example description + +Using pagination strategy, achieve a more high-performance and easy-to-use pagination function, automatic management of paging-related status, preloading of previous and previous pages, and automatic maintenance of data addition/editing/replacement/ Removed, and request-level stabilization. + +_Operation guide:_ + +1. After the initialization is completed, the next page of data will be preloaded, and there is no need to wait when turning to the next page; +2. Adding, deleting, and modifying list items does not need to reset the list, it will be automatically processed into the same effect as the re-request; + +[usePagination documentation](/tutorial/strategy/usePagination) + +::: diff --git a/versioned_docs/version-2.x/tutorial/01-example/11-controlled-cache-by-indexeddb.md b/versioned_docs/version-2.x/tutorial/01-example/11-controlled-cache-by-indexeddb.md index b5b5eaf4f..5ebbc47f2 100644 --- a/versioned_docs/version-2.x/tutorial/01-example/11-controlled-cache-by-indexeddb.md +++ b/versioned_docs/version-2.x/tutorial/01-example/11-controlled-cache-by-indexeddb.md @@ -1,23 +1,22 @@ ---- -title: (vue)Manage Cache with IndexedDB -sidebar_position: 110 ---- - -import ControlledCacheByIndexedDB from '@site/example-links/ControlledCacheByIndexedDB'; - -> The example uses vue3 as an example, but you can also use alova in react and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details; - - - -:::info example description - -Using controlled cache allows developers to customize and manage the cache. Under the large file cache, it can cooperate with IndexedDB to manage the local cache. - -_Operation guidance:_ - -1. Select one of the pictures, the picture will request the network to load first, and the picture data will be saved in the local IndexedDB; -2. Refresh the page and select the same picture again, the picture will get data from IndexedDB instead of initiating a network request; - -[Controlled cache document](/tutorial/cache/controlled-cache) - -::: +--- +title: Manage Cache with IndexedDB +--- + +import ControlledCacheByIndexedDB from '@site/example-links/ControlledCacheByIndexedDB'; + +> The example uses vue3 as an example, but you can also use alova in react and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details; + + + +:::info example description + +Using controlled cache allows developers to customize and manage the cache. Under the large file cache, it can cooperate with IndexedDB to manage the local cache. + +_Operation guidance:_ + +1. Select one of the pictures, the picture will request the network to load first, and the picture data will be saved in the local IndexedDB; +2. Refresh the page and select the same picture again, the picture will get data from IndexedDB instead of initiating a network request; + +[Controlled cache document](/tutorial/cache/controlled-cache) + +::: diff --git a/versioned_docs/version-2.x/tutorial/01-example/12-silent-submit-setting.md b/versioned_docs/version-2.x/tutorial/01-example/12-silent-submit-setting.md index aa49e9dc2..6d2f1215e 100644 --- a/versioned_docs/version-2.x/tutorial/01-example/12-silent-submit-setting.md +++ b/versioned_docs/version-2.x/tutorial/01-example/12-silent-submit-setting.md @@ -1,23 +1,22 @@ ---- -title: (svelte)Silent Submit - Settings Page -sidebar_position: 120 ---- - -import SettingSilentSvelte from '@site/example-links/SettingSilentSvelte'; - -> The example uses svelte as an example, but you can also use alova in react and vue, please read the [Getting Started Guide](/tutorial/getting-started) for details; - - - -:::info example description - -Use the silent submission strategy, submit and respond immediately, greatly reducing the impact of network fluctuations, so that your application is still very smooth even when the network is unstable or even disconnected. - -_Operation guidance:_ - -1. Operate the setting item, it will generate feedback immediately without waiting for the server to respond; -2. Switch the request mode and network status to experience the difference between them; - -[Silent submit strategy document](/tutorial/strategy/sensorless-data-interaction) - -::: +--- +title: Silent Submit - Settings Page +--- + +import SettingSilentSvelte from '@site/example-links/SettingSilentSvelte'; + +> The example uses svelte as an example, but you can also use alova in react and vue, please read the [Getting Started Guide](/tutorial/getting-started) for details; + + + +:::info example description + +Use the silent submission strategy, submit and respond immediately, greatly reducing the impact of network fluctuations, so that your application is still very smooth even when the network is unstable or even disconnected. + +_Operation guidance:_ + +1. Operate the setting item, it will generate feedback immediately without waiting for the server to respond; +2. Switch the request mode and network status to experience the difference between them; + +[Silent submit strategy document](/tutorial/strategy/sensorless-data-interaction) + +::: diff --git a/versioned_docs/version-2.x/tutorial/01-example/13-silent-submit-simple-list.md b/versioned_docs/version-2.x/tutorial/01-example/13-silent-submit-simple-list.md index 2b4ba3046..be1df2877 100644 --- a/versioned_docs/version-2.x/tutorial/01-example/13-silent-submit-simple-list.md +++ b/versioned_docs/version-2.x/tutorial/01-example/13-silent-submit-simple-list.md @@ -1,23 +1,22 @@ ---- -title: (vue)Silent submit - Simple List -sidebar_position: 130 ---- - -import SimpleListSilentVue from '@site/example-links/SimpleListSilentVue'; - -> The example uses vue3 as an example, but you can also use alova in react and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details; - - - -:::info example description - -A simple list implemented using the silent submission strategy, which responds immediately after submission, greatly reduces the impact of network fluctuations, allowing your application to remain very smooth even when the network is unstable or even disconnected. - -_Operation guidance:_ - -1. Add, edit, and delete list items, it will generate feedback immediately without waiting for the server to respond; -2. Switch the request mode and network status to experience the difference between them; - -[Silent submit strategy document](/tutorial/strategy/sensorless-data-interaction) - -::: +--- +title: Silent submit - Simple List +--- + +import SimpleListSilentVue from '@site/example-links/SimpleListSilentVue'; + +> The example uses vue3 as an example, but you can also use alova in react and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details; + + + +:::info example description + +A simple list implemented using the silent submission strategy, which responds immediately after submission, greatly reduces the impact of network fluctuations, allowing your application to remain very smooth even when the network is unstable or even disconnected. + +_Operation guidance:_ + +1. Add, edit, and delete list items, it will generate feedback immediately without waiting for the server to respond; +2. Switch the request mode and network status to experience the difference between them; + +[Silent submit strategy document](/tutorial/strategy/sensorless-data-interaction) + +::: diff --git a/versioned_docs/version-2.x/tutorial/01-example/14-silent-submit-notes.md b/versioned_docs/version-2.x/tutorial/01-example/14-silent-submit-notes.md index d4ae8b3b8..acdddbb66 100644 --- a/versioned_docs/version-2.x/tutorial/01-example/14-silent-submit-notes.md +++ b/versioned_docs/version-2.x/tutorial/01-example/14-silent-submit-notes.md @@ -1,23 +1,22 @@ ---- -title: (react)Silent submit - Notebook -sidebar_position: 140 ---- - -import NoteSilentReact from '@site/example-links/NoteSilentReact'; - -> The example uses react as an example, but you can also use alova in vue3 and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details; - - - -:::info example description - -A simple list implemented using the silent submission strategy, which responds immediately after submission, greatly reduces the impact of network fluctuations, allowing your application to remain very smooth even when the network is unstable or even disconnected. - -_Operation guidance:_ - -1. Add, edit, and delete notes, it will generate feedback immediately without waiting for the server to respond; -2. Switch the request mode and network status to experience the difference between them; - -[Silent submit strategy document](/tutorial/strategy/sensorless-data-interaction) - -::: +--- +title: Silent submit - Notebook +--- + +import NoteSilentReact from '@site/example-links/NoteSilentReact'; + +> The example uses react as an example, but you can also use alova in vue3 and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details; + + + +:::info example description + +A simple list implemented using the silent submission strategy, which responds immediately after submission, greatly reduces the impact of network fluctuations, allowing your application to remain very smooth even when the network is unstable or even disconnected. + +_Operation guidance:_ + +1. Add, edit, and delete notes, it will generate feedback immediately without waiting for the server to respond; +2. Switch the request mode and network status to experience the difference between them; + +[Silent submit strategy document](/tutorial/strategy/sensorless-data-interaction) + +::: diff --git a/versioned_docs/version-2.x/tutorial/01-example/15-form-hook.md b/versioned_docs/version-2.x/tutorial/01-example/15-form-hook.md index 8dbccd923..6fc59d261 100644 --- a/versioned_docs/version-2.x/tutorial/01-example/15-form-hook.md +++ b/versioned_docs/version-2.x/tutorial/01-example/15-form-hook.md @@ -1,18 +1,17 @@ ---- -title: (react)Form submit strategy -sidebar_position: 150 ---- - -import FormHook from '@site/example-links/FormHook'; - -> The example uses react as an example, but you can also use alova in vue3 and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details; - - - -:::info example description - -This example provides four parts: form data persistence, form editing, multi-module form, and form filtering data, which can be tried separately. - -[Form submit strategy document](/tutorial/strategy/useForm) - -::: +--- +title: Form submit strategy +--- + +import FormHook from '@site/example-links/FormHook'; + +> The example uses react as an example, but you can also use alova in vue3 and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details; + + + +:::info example description + +This example provides four parts: form data persistence, form editing, multi-module form, and form filtering data, which can be tried separately. + +[Form submit strategy document](/tutorial/strategy/useForm) + +::: diff --git a/versioned_docs/version-2.x/tutorial/01-example/16-captcha-send.md b/versioned_docs/version-2.x/tutorial/01-example/16-captcha-send.md index 1eca34a3d..68ce05a32 100644 --- a/versioned_docs/version-2.x/tutorial/01-example/16-captcha-send.md +++ b/versioned_docs/version-2.x/tutorial/01-example/16-captcha-send.md @@ -1,18 +1,17 @@ ---- -title: (react)Send captcha -sidebar_position: 160 ---- - -import CaptchaSend from '@site/example-links/CaptchaSend'; - -> The example uses react as an example, but you can also use alova in vue3 and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details; - - - -:::info example description - -This example demonstrates the convenient implementation of the captcha sending function. - -[Captcha sending strategy document](/tutorial/strategy/useCaptcha) - -::: +--- +title: Send captcha +--- + +import CaptchaSend from '@site/example-links/CaptchaSend'; + +> The example uses react as an example, but you can also use alova in vue3 and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details; + + + +:::info example description + +This example demonstrates the convenient implementation of the captcha sending function. + +[Captcha sending strategy document](/tutorial/strategy/useCaptcha) + +::: diff --git a/versioned_docs/version-2.x/tutorial/01-example/17-retriable-hook.md b/versioned_docs/version-2.x/tutorial/01-example/17-retriable-hook.md index 8febc2043..eaf6d19e4 100644 --- a/versioned_docs/version-2.x/tutorial/01-example/17-retriable-hook.md +++ b/versioned_docs/version-2.x/tutorial/01-example/17-retriable-hook.md @@ -1,18 +1,17 @@ ---- -title: (react)Request Retry/Poll Request -sidebar_position: 170 ---- - -import RetriableHook from '@site/example-links/RetriableHook'; - -> The example uses react as an example, but you can also use alova in vue3 and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details; - - - -:::info example description - -This example provides four parts: form data persistence, form editing, multi-module form, and form filtering data, which can be tried separately. - -[Request Retry Strategy Documentation](/tutorial/strategy/useRetriableRequest) - -::: +--- +title: Request Retry/Poll Request +--- + +import RetriableHook from '@site/example-links/RetriableHook'; + +> The example uses react as an example, but you can also use alova in vue3 and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details; + + + +:::info example description + +This example provides four parts: form data persistence, form editing, multi-module form, and form filtering data, which can be tried separately. + +[Request Retry Strategy Documentation](/tutorial/strategy/useRetriableRequest) + +::: diff --git a/versioned_docs/version-2.x/tutorial/01-example/18.action-delegation-middleware.md b/versioned_docs/version-2.x/tutorial/01-example/18.action-delegation-middleware.md index 2590d7c38..a6d704994 100644 --- a/versioned_docs/version-2.x/tutorial/01-example/18.action-delegation-middleware.md +++ b/versioned_docs/version-2.x/tutorial/01-example/18.action-delegation-middleware.md @@ -1,18 +1,17 @@ ---- -title: (react)Trigger requests across components -sidebar_position: 180 ---- - -import ActionDelegationMiddleware from '@site/example-links/ActionDelegationMiddleware'; - -> The example uses react as an example, but you can also use alova in vue3 and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details; - - - -:::info example description - -This example mainly uses the `actionDelegationMiddleware` middleware to demonstrate the refresh of server-side data across any level of components without using global state management. - -[Actions delegation middleware document](/tutorial/strategy/actionDelegationMiddleware) - -::: +--- +title: Trigger requests across components +--- + +import ActionDelegationMiddleware from '@site/example-links/ActionDelegationMiddleware'; + +> The example uses react as an example, but you can also use alova in vue3 and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details; + + + +:::info example description + +This example mainly uses the `actionDelegationMiddleware` middleware to demonstrate the refresh of server-side data across any level of components without using global state management. + +[Actions delegation middleware document](/tutorial/strategy/actionDelegationMiddleware) + +::: diff --git a/versioned_docs/version-2.x/tutorial/01-example/19-serial-request.md b/versioned_docs/version-2.x/tutorial/01-example/19-serial-request.md index 34fb885df..0203ca512 100644 --- a/versioned_docs/version-2.x/tutorial/01-example/19-serial-request.md +++ b/versioned_docs/version-2.x/tutorial/01-example/19-serial-request.md @@ -1,19 +1,18 @@ ---- -title: (vue)serial request hook -sidebar_position: 190 ---- - -import SerialRequest from '@site/example-links/SerialRequest'; - -> The example uses vue3 as an example, but you can also use alova in react and svelte, please read [Getting Started Guide](/tutorial/getting-started) for details; - - - -:::info example description - -This example mainly demonstrates a more elegant serial request through `useSerialRequest`. - -[useSerialRequest](/tutorial/strategy/useSerialRequest) -[useSerialWatcher](/tutorial/strategy/useSerialWatcher) - -::: +--- +title: serial request hook +--- + +import SerialRequest from '@site/example-links/SerialRequest'; + +> The example uses vue3 as an example, but you can also use alova in react and svelte, please read [Getting Started Guide](/tutorial/getting-started) for details; + + + +:::info example description + +This example mainly demonstrates a more elegant serial request through `useSerialRequest`. + +[useSerialRequest](/tutorial/strategy/useSerialRequest) +[useSerialWatcher](/tutorial/strategy/useSerialWatcher) + +::: diff --git a/versioned_docs/version-2.x/tutorial/02-getting-started/02-quick-start.md b/versioned_docs/version-2.x/tutorial/02-getting-started/02-quick-start.md index 6cdfadc12..ec9ead664 100644 --- a/versioned_docs/version-2.x/tutorial/02-getting-started/02-quick-start.md +++ b/versioned_docs/version-2.x/tutorial/02-getting-started/02-quick-start.md @@ -1,115 +1,114 @@ ---- -title: Quick Start -sidebar_position: 20 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; -import EmbedSandpack from "@site/src/components/EmbedSandpack"; - -import quickStartGET from '!!raw-loader!@site/codesandbox/01-getting-started/02-first-request/get.js'; -import quickStartPOST from '!!raw-loader!@site/codesandbox/01-getting-started/02-first-request/post.js'; - -:::tip Example tip - -If you haven’t learned about alova yet, it is recommended that you read [alova overview](/tutorial/getting-started) first. - -::: - -## Install - - - - -```bash -npm install alova --save -``` - - - - -```bash -yarn add alova -``` - - - - -```bash -pnpm add alova -``` - - - - -```bash -bun add alova -``` - - - - -> You can also [use alova through CDN](/tutorial/others/use-in-static) - -## Create alova instance - -In alova, a request needs to be made through an alova instance. Let's create one first. When creating an alova instance, you need to specify a request adapter. It is recommended to use the `GlobalFetch` request adapter here, which is a package based on the `fetch API`. - - - - -```javascript -import { createAlova } from 'alova'; -import GlobalFetch from 'alova/GlobalFetch'; - -const alovaInstance = createAlova({ - requestAdapter: GlobalFetch() -}); -``` - - - - -```javascript -const { createAlova } = require('alova'); -const GlobalFetch = require('alova/GlobalFetch'); - -const alova = createAlova({ - requestAdapter: GlobalFetch(); -}); -``` - -> When using GlobalFetch in nodejs, the nodejs version requires `v17.5`, or you can use [axios request adapter](/tutorial/request-adapter/alova-adapter-axios/). - - - - -```javascript -import { createAlova } from 'npm:alova'; -import GlobalFetch from 'npm:alova/GlobalFetch'; - -const alova = createAlova({ - requestAdapter: GlobalFetch(); -}); -``` - - - - -## GET request - -Sending a request via `alovaInstance.Get` will receive a `Response` instance thanks to the `GlobalFetch` request adapter, which is simple. - - - -In an asynchronous function, you can also use `await alovaInstance.Get` to wait for a response. - -## POST request - -Submitting data via `alovaInstance.Post` is also easy. - - - -## What’s next? - -In fact, this is just the simplest request example. We will learn more about the features in the next chapters, so let's start learning. +--- +title: Quick Start +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import EmbedSandpack from "@site/src/components/EmbedSandpack"; + +import quickStartGET from '!!raw-loader!@site/codesandbox/01-getting-started/02-first-request/get.js'; +import quickStartPOST from '!!raw-loader!@site/codesandbox/01-getting-started/02-first-request/post.js'; + +:::tip Example tip + +If you haven’t learned about alova yet, it is recommended that you read [alova overview](/tutorial/getting-started) first. + +::: + +## Install + + + + +```bash +npm install alova --save +``` + + + + +```bash +yarn add alova +``` + + + + +```bash +pnpm add alova +``` + + + + +```bash +bun add alova +``` + + + + +> You can also [use alova through CDN](/tutorial/others/use-in-static) + +## Create alova instance + +In alova, a request needs to be made through an alova instance. Let's create one first. When creating an alova instance, you need to specify a request adapter. It is recommended to use the `GlobalFetch` request adapter here, which is a package based on the `fetch API`. + + + + +```javascript +import { createAlova } from 'alova'; +import GlobalFetch from 'alova/GlobalFetch'; + +const alovaInstance = createAlova({ + requestAdapter: GlobalFetch() +}); +``` + + + + +```javascript +const { createAlova } = require('alova'); +const GlobalFetch = require('alova/GlobalFetch'); + +const alova = createAlova({ + requestAdapter: GlobalFetch(); +}); +``` + +> When using GlobalFetch in nodejs, the nodejs version requires `v17.5`, or you can use [axios request adapter](/tutorial/request-adapter/alova-adapter-axios/). + + + + +```javascript +import { createAlova } from 'npm:alova'; +import GlobalFetch from 'npm:alova/GlobalFetch'; + +const alova = createAlova({ + requestAdapter: GlobalFetch(); +}); +``` + + + + +## GET request + +Sending a request via `alovaInstance.Get` will receive a `Response` instance thanks to the `GlobalFetch` request adapter, which is simple. + + + +In an asynchronous function, you can also use `await alovaInstance.Get` to wait for a response. + +## POST request + +Submitting data via `alovaInstance.Post` is also easy. + + + +## What’s next? + +In fact, this is just the simplest request example. We will learn more about the features in the next chapters, so let's start learning. diff --git a/versioned_docs/version-2.x/tutorial/02-getting-started/03-method.md b/versioned_docs/version-2.x/tutorial/02-getting-started/03-method.md index a188cbd1d..de747da4f 100644 --- a/versioned_docs/version-2.x/tutorial/02-getting-started/03-method.md +++ b/versioned_docs/version-2.x/tutorial/02-getting-started/03-method.md @@ -1,306 +1,305 @@ ---- -title: Method Instance -sidebar_position: 30 ---- - -In the previous chapter we tried to send a request and obtain the response data. In fact, `alovaInstance.Get(...)` is not a function that initiates a request, but creates a method instance, which is a PromiseLike instance. You can use `then, catch, finally` methods or `await` Send the request just like a Promise object. - -```javascript -const userMethodInstance = alovaInstance.Get('/api/user'); - -userMethodInstance.then(response => { - // ... -}); - -userMethodInstance.catch(error => { - // ... -}); - -userMethodInstance.finally(() => { - // ... -}); - -try { - await userMethodInstance; -} catch (error) { - // ... -} finally { - // ... -} -``` - -Simple way to write: - -```javascript -const response = await alovaInstance.Get('/api/user'); -``` - -Each method instance describes the type of each request, request url, request headers, request parameters, etc. In addition, you can define request behavior on the method instance to control how the method handles the request. - -## Request type - -alova provides a total of 7 request types: GET, POST, PUT, DELETE, HEAD, OPTIONS, and PATCH. - -| Instance creation function | Parameters | -| -------------------------- | --------------------------------------------- | -| GET | `alovaInstance.Get(url[, config])` | -| POST | `alovaInstance.Post(url[, data[, config]])` | -| PUT | `alovaInstance.Put(url[, data[, config]])` | -| DELETE | `alovaInstance.Delete(url[, data[, config]])` | -| HEAD | `alovaInstance.Head(url[, config])` | -| OPTIONS | `alovaInstance.Options(url[, config])` | -| PATCH | `alovaInstance.Patch(url[, data[, config]])` | - -Parameter Description: - -- `url` is the request path; -- `data` is the request body data; -- `config` is the request configuration object, which includes configurations such as request headers, params parameters, request behavior parameters, etc.; - -you can also create a method instance customly. This is useful when you need to dynamically specify the request type. - -```javascript -import { Method } from 'alova'; - -const method = new Method('GET', alovaInstance, '/api/users', { - params: { - ID: 1 - } -}); -``` - -Next, let’s take a look at how to define request parameters, which should seem familiar to you. - -## Request parameters - -### URL parameters - -Pass in the URL parameters through params, and the params parameters will be spliced in the form of ? after the url. - -```javascript -alovaInstance.Get('/todo/list', { - params: { - userId: 1 - } -}); -``` - -Of course, you can also directly splice it behind the url, and the effect will be the same. - -```javascript -alovaInstance.Get('/todo/list?userId=1'); -``` - -### Request body - -When sending **POST, PUT, DELETE, PATCH request**, you can send data through the request body. At this time, the second parameter is passed in the request body. It is worth noting that the POST request can also pass in the params parameter. - -```javascript -alovaInstance.Post( - '/todo', - //The second parameter is the request body - { - title: 'test todo', - time: '12:00' - }, - // The third parameter is configuration - { - params: { - userId: 1 - } - } -); -``` - -### Request header - -Specify request headers via headers. - -```javascript -alovaInstance.Get('/user', { - headers: { - 'Content-Type': 'application/json;charset=UTF-8' - } -}); -``` - -### Other parameters supported by the request adapter - -In addition to request headers, params parameters, etc., it also supports configuring parameters supported by the corresponding request adapter. When using `GlobalFetch` as the request adapter of alova, you can configure any `fetch API` supported parameters on the `method` instance. These Parameters will be passed to the `fetch` function during request. - -```javascript -alovaInstance.Get('/todo/list', { - // ... - // highlight-start - credentials: 'same-origin', - referrerPolicy: 'no-referrer', - mode: 'cors' - // highlight-end -}); -``` - -When the above `method` instance sends a request through `fetch`, it will be requested with the following parameters. - -```javascript -fetch('/todo/list', { - // ... - // highlight-start - credentials: 'same-origin', - referrerPolicy: 'no-referrer', - mode: 'cors' - // highlight-end -}); -``` - -> In addition to passing Object, the request body can also pass request body parameters supported by the request adapter. For example, GlobalFetch supports passing `string | FormData | Blob | ArrayBuffer | URLSearchParams | ReadableStream` parameters. - -If you use other request adapters, you can also pass the parameters they support. - -## Request behavior - -In [RSM](/tutorial/others/RSM), request behavior is used to describe how the request will be handled. - -### overtime time - -Set request timeout. - -```javascript -// Request timeout at request level -alovaInstance.Get('/todo/list', { - // ... - // highlight-start - timeout: 10000 - // highlight-end -}); -``` - -### Request sharing - -We will always encounter this situation. When a request is issued but has not yet been responded to, the same request is issued again, resulting in a waste of requests or repeated submission of problems, such as the following three scenarios: - -1. A component will obtain initialization data when it is created. When a page renders multiple components at the same time, multiple identical requests will be issued at the same time; -2. The submit button is not disabled and the user clicks the submit button multiple times; -3. When the preloading page is entered before preloading is completed, multiple identical requests will be initiated; -4. Prevent repeated requests from being sent under react’s StrictMode; - -Shared requests are used to solve these problems. It can not only improve application fluency, but also reduce server pressure. - -```mermaid -flowchart LR - classDef response fill:#a8bcff - R1[Request 1] --> S1[Send request] --> W1[Waiting for response]:::response --> RE1[Receive data 1] - R2[Same request as request 1] --> W1[Waiting for response]:::response --> RE2[Receive data 1] -``` - -Request sharing is enabled by default. If you wish to turn off request sharing on a specific request, you can do this: - -```javascript -alovaInst.Get('/todo', { - // ... - // highlight-start - shareRequest: false - // highlight-end -}); -``` - -:::warning How to identify identical requests - -The request method, request URL, request header, URL parameters, and request body of the method instance are used as a unique identifier. If the identifier is the same, it means the same request, instead of comparing the reference address of the method instance. - -::: - -### Convert response data - -Sometimes we need to uniformly transform the response data. We can set the `transformData` function for the method instance to convert the response data into the required structure. - -```javascript -alovaInstance.Get('/todo/list', { - // The function accepts response data and response header data, and requires the converted data to be returned. - transformData(rawData, headers) { - return rawData.list.map(item => { - return { - ...item, - statusText: item.done ? 'Completed' : 'In progress' - }; - }); - } -}); -``` - -### Response cache - -Response caching allows you to better utilize server-side data multiple times without having to send a request to obtain the data every time it is requested. GET requests will set a memory cache time of 5 minutes by default, which we will explain in detail in the [Response Caching](/tutorial/cache/mode) section later. - -## Abort request - -`[2.6.2+]` calls the `abort` of the method instance to abort the request. - -```javascript -const userMethod = alovaInstance.Get('/api/user'); -userMethod.then(res => { - // ... -}); - -const handleCancel = () => { - // highlight-start - userMethod.abort(); - // highlight-end -}; -``` - -## Monitor upload and download progress - -**[v2.17.0+]** Bind the upload progress event through `onUpload` of the method instance, and bind the download progress event through `onDownload`, which will return the unbinding function. - -```javascript -const uploadMethod = alovaInstance.Post('/todo/uploadfile', formData); -const offUploadEvent = uploadMethod.onUpload(event => { - console.log('File size:', event.total); - console.log('Uploaded:', event.loaded); -}); - -uploadMethod.then(res => { - // ... -}); - -// Unbind upload callback -const handleOffEvent = () => { - offUploadEvent(); -}; -``` - -```javascript -const downloadMethod = alovaInstance.Get('/todo/downloadfile'); -const offDownloadEvent = downloadMethod.onDownload(event => { - console.log('File size:', event.total); - console.log('Downloaded:', event.loaded); -}); - -downloadMethod.then(res => { - // ... -}); - -// Unbind download callback -const handleOffEvent = () => { - offDownloadEvent(); -}; -``` - -:::warning Things to note when using the `GlobalFetch` adapter - -Due to fetch api limitations, the **GlobalFetch** adapter provided by alova does not support upload progress. If you need to upload progress, please use [XMLHttpRequest Adapter](/tutorial/request-adapter/alova-adapter-xhr) or [axios Adapter](/tutorial/request-adapter/alova-adapter-axios). - -You can also write your own request adapter, see [Writing Request Adapter](/tutorial/custom/custom-http-adapter) for details. - -::: - -**Upload/Download Status Type** - -```typescript -type Progress = { - /** Total amount of data uploaded or downloaded */ - total: number; - /** Completed data */ - loaded: number; -}; -``` +--- +title: Method Instance +--- + +In the previous chapter we tried to send a request and obtain the response data. In fact, `alovaInstance.Get(...)` is not a function that initiates a request, but creates a method instance, which is a PromiseLike instance. You can use `then, catch, finally` methods or `await` Send the request just like a Promise object. + +```javascript +const userMethodInstance = alovaInstance.Get('/api/user'); + +userMethodInstance.then(response => { + // ... +}); + +userMethodInstance.catch(error => { + // ... +}); + +userMethodInstance.finally(() => { + // ... +}); + +try { + await userMethodInstance; +} catch (error) { + // ... +} finally { + // ... +} +``` + +Simple way to write: + +```javascript +const response = await alovaInstance.Get('/api/user'); +``` + +Each method instance describes the type of each request, request url, request headers, request parameters, etc. In addition, you can define request behavior on the method instance to control how the method handles the request. + +## Request type + +alova provides a total of 7 request types: GET, POST, PUT, DELETE, HEAD, OPTIONS, and PATCH. + +| Instance creation function | Parameters | +| -------------------------- | --------------------------------------------- | +| GET | `alovaInstance.Get(url[, config])` | +| POST | `alovaInstance.Post(url[, data[, config]])` | +| PUT | `alovaInstance.Put(url[, data[, config]])` | +| DELETE | `alovaInstance.Delete(url[, data[, config]])` | +| HEAD | `alovaInstance.Head(url[, config])` | +| OPTIONS | `alovaInstance.Options(url[, config])` | +| PATCH | `alovaInstance.Patch(url[, data[, config]])` | + +Parameter Description: + +- `url` is the request path; +- `data` is the request body data; +- `config` is the request configuration object, which includes configurations such as request headers, params parameters, request behavior parameters, etc.; + +you can also create a method instance customly. This is useful when you need to dynamically specify the request type. + +```javascript +import { Method } from 'alova'; + +const method = new Method('GET', alovaInstance, '/api/users', { + params: { + ID: 1 + } +}); +``` + +Next, let’s take a look at how to define request parameters, which should seem familiar to you. + +## Request parameters + +### URL parameters + +Pass in the URL parameters through params, and the params parameters will be spliced in the form of ? after the url. + +```javascript +alovaInstance.Get('/todo/list', { + params: { + userId: 1 + } +}); +``` + +Of course, you can also directly splice it behind the url, and the effect will be the same. + +```javascript +alovaInstance.Get('/todo/list?userId=1'); +``` + +### Request body + +When sending **POST, PUT, DELETE, PATCH request**, you can send data through the request body. At this time, the second parameter is passed in the request body. It is worth noting that the POST request can also pass in the params parameter. + +```javascript +alovaInstance.Post( + '/todo', + //The second parameter is the request body + { + title: 'test todo', + time: '12:00' + }, + // The third parameter is configuration + { + params: { + userId: 1 + } + } +); +``` + +### Request header + +Specify request headers via headers. + +```javascript +alovaInstance.Get('/user', { + headers: { + 'Content-Type': 'application/json;charset=UTF-8' + } +}); +``` + +### Other parameters supported by the request adapter + +In addition to request headers, params parameters, etc., it also supports configuring parameters supported by the corresponding request adapter. When using `GlobalFetch` as the request adapter of alova, you can configure any `fetch API` supported parameters on the `method` instance. These Parameters will be passed to the `fetch` function during request. + +```javascript +alovaInstance.Get('/todo/list', { + // ... + // highlight-start + credentials: 'same-origin', + referrerPolicy: 'no-referrer', + mode: 'cors' + // highlight-end +}); +``` + +When the above `method` instance sends a request through `fetch`, it will be requested with the following parameters. + +```javascript +fetch('/todo/list', { + // ... + // highlight-start + credentials: 'same-origin', + referrerPolicy: 'no-referrer', + mode: 'cors' + // highlight-end +}); +``` + +> In addition to passing Object, the request body can also pass request body parameters supported by the request adapter. For example, GlobalFetch supports passing `string | FormData | Blob | ArrayBuffer | URLSearchParams | ReadableStream` parameters. + +If you use other request adapters, you can also pass the parameters they support. + +## Request behavior + +In [RSM](/tutorial/others/RSM), request behavior is used to describe how the request will be handled. + +### overtime time + +Set request timeout. + +```javascript +// Request timeout at request level +alovaInstance.Get('/todo/list', { + // ... + // highlight-start + timeout: 10000 + // highlight-end +}); +``` + +### Request sharing + +We will always encounter this situation. When a request is issued but has not yet been responded to, the same request is issued again, resulting in a waste of requests or repeated submission of problems, such as the following three scenarios: + +1. A component will obtain initialization data when it is created. When a page renders multiple components at the same time, multiple identical requests will be issued at the same time; +2. The submit button is not disabled and the user clicks the submit button multiple times; +3. When the preloading page is entered before preloading is completed, multiple identical requests will be initiated; +4. Prevent repeated requests from being sent under react’s StrictMode; + +Shared requests are used to solve these problems. It can not only improve application fluency, but also reduce server pressure. + +```mermaid +flowchart LR + classDef response fill:#a8bcff + R1[Request 1] --> S1[Send request] --> W1[Waiting for response]:::response --> RE1[Receive data 1] + R2[Same request as request 1] --> W1[Waiting for response]:::response --> RE2[Receive data 1] +``` + +Request sharing is enabled by default. If you wish to turn off request sharing on a specific request, you can do this: + +```javascript +alovaInst.Get('/todo', { + // ... + // highlight-start + shareRequest: false + // highlight-end +}); +``` + +:::warning How to identify identical requests + +The request method, request URL, request header, URL parameters, and request body of the method instance are used as a unique identifier. If the identifier is the same, it means the same request, instead of comparing the reference address of the method instance. + +::: + +### Convert response data + +Sometimes we need to uniformly transform the response data. We can set the `transformData` function for the method instance to convert the response data into the required structure. + +```javascript +alovaInstance.Get('/todo/list', { + // The function accepts response data and response header data, and requires the converted data to be returned. + transformData(rawData, headers) { + return rawData.list.map(item => { + return { + ...item, + statusText: item.done ? 'Completed' : 'In progress' + }; + }); + } +}); +``` + +### Response cache + +Response caching allows you to better utilize server-side data multiple times without having to send a request to obtain the data every time it is requested. GET requests will set a memory cache time of 5 minutes by default, which we will explain in detail in the [Response Caching](/tutorial/cache/mode) section later. + +## Abort request + +`[2.6.2+]` calls the `abort` of the method instance to abort the request. + +```javascript +const userMethod = alovaInstance.Get('/api/user'); +userMethod.then(res => { + // ... +}); + +const handleCancel = () => { + // highlight-start + userMethod.abort(); + // highlight-end +}; +``` + +## Monitor upload and download progress + +**[v2.17.0+]** Bind the upload progress event through `onUpload` of the method instance, and bind the download progress event through `onDownload`, which will return the unbinding function. + +```javascript +const uploadMethod = alovaInstance.Post('/todo/uploadfile', formData); +const offUploadEvent = uploadMethod.onUpload(event => { + console.log('File size:', event.total); + console.log('Uploaded:', event.loaded); +}); + +uploadMethod.then(res => { + // ... +}); + +// Unbind upload callback +const handleOffEvent = () => { + offUploadEvent(); +}; +``` + +```javascript +const downloadMethod = alovaInstance.Get('/todo/downloadfile'); +const offDownloadEvent = downloadMethod.onDownload(event => { + console.log('File size:', event.total); + console.log('Downloaded:', event.loaded); +}); + +downloadMethod.then(res => { + // ... +}); + +// Unbind download callback +const handleOffEvent = () => { + offDownloadEvent(); +}; +``` + +:::warning Things to note when using the `GlobalFetch` adapter + +Due to fetch api limitations, the **GlobalFetch** adapter provided by alova does not support upload progress. If you need to upload progress, please use [XMLHttpRequest Adapter](/tutorial/request-adapter/alova-adapter-xhr) or [axios Adapter](/tutorial/request-adapter/alova-adapter-axios). + +You can also write your own request adapter, see [Writing Request Adapter](/tutorial/custom/custom-http-adapter) for details. + +::: + +**Upload/Download Status Type** + +```typescript +type Progress = { + /** Total amount of data uploaded or downloaded */ + total: number; + /** Completed data */ + loaded: number; +}; +``` diff --git a/versioned_docs/version-2.x/tutorial/02-getting-started/04-alova.md b/versioned_docs/version-2.x/tutorial/02-getting-started/04-alova.md index 8a6456f6d..36a515094 100644 --- a/versioned_docs/version-2.x/tutorial/02-getting-started/04-alova.md +++ b/versioned_docs/version-2.x/tutorial/02-getting-started/04-alova.md @@ -1,67 +1,66 @@ ---- -title: Alova Instance -sidebar_position: 40 ---- - -Alova instances can not only create method instances of different types, but also set global parameters. The created method instances will inherit the parameters of this alova instance. When the parameters of the alova instance are set to the same parameters as the method instance, such as `timeout`, `shareRequest`, etc., the parameters of the method instance will be used first. - -Next we look at the global parameters of alova. - -## baseURL - -After setting the baseURL, you no longer need to add the same url prefix for every request. - -```javascript -const alovaInstance = createAlova({ - baseURL: 'https://api.alovajs.dev' - // ... -}); -``` - -At this point, you only need to specify pathname when creating a method instance. - -```javascript -alovaInstance.Get('/todo/list'); -``` - -## Global timeout - -The following is to set the global request timeout. - -```javascript -// Set request timeout globally -const alovaInstance = createAlova({ - // ... - // highlight-start - // Request timeout, unit is milliseconds, default is 0, which means it will never timeout - timeout: 50000 - // highlight-end -}); -``` - -## Global sharing request - -Set sharing requests globally. - -```javascript -const alovaInstance = createAlova({ - // ... - // highlight-start - shareRequest: false - // highlight-end -}); -``` - -## Request adapter - -In the previous chapter we have configured the `GlobalFetch` request adapter, which will be used to send requests initiated by this alova instance. In fact, we also provide various request adapters for different JS environments. - -- [Mock request adapter](/tutorial/request-adapter/alova-mock) -- [XMLHttpRequest Adapter](/tutorial/request-adapter/alova-adapter-xhr) -- [axios adapter](/tutorial/request-adapter/alova-adapter-axios) -- [uniapp adapter](/tutorial/request-adapter/alova-adapter-uniapp) -- [taro adapter](/tutorial/request-adapter/alova-adapter-taro) - -## Global response cache - -You can also set the response cache globally, which we will explain in detail in the [Response Cache](/tutorial/cache/mode) chapter later. +--- +title: Alova Instance +--- + +Alova instances can not only create method instances of different types, but also set global parameters. The created method instances will inherit the parameters of this alova instance. When the parameters of the alova instance are set to the same parameters as the method instance, such as `timeout`, `shareRequest`, etc., the parameters of the method instance will be used first. + +Next we look at the global parameters of alova. + +## baseURL + +After setting the baseURL, you no longer need to add the same url prefix for every request. + +```javascript +const alovaInstance = createAlova({ + baseURL: 'https://api.alovajs.dev' + // ... +}); +``` + +At this point, you only need to specify pathname when creating a method instance. + +```javascript +alovaInstance.Get('/todo/list'); +``` + +## Global timeout + +The following is to set the global request timeout. + +```javascript +// Set request timeout globally +const alovaInstance = createAlova({ + // ... + // highlight-start + // Request timeout, unit is milliseconds, default is 0, which means it will never timeout + timeout: 50000 + // highlight-end +}); +``` + +## Global sharing request + +Set sharing requests globally. + +```javascript +const alovaInstance = createAlova({ + // ... + // highlight-start + shareRequest: false + // highlight-end +}); +``` + +## Request adapter + +In the previous chapter we have configured the `GlobalFetch` request adapter, which will be used to send requests initiated by this alova instance. In fact, we also provide various request adapters for different JS environments. + +- [Mock request adapter](/tutorial/request-adapter/alova-mock) +- [XMLHttpRequest Adapter](/tutorial/request-adapter/alova-adapter-xhr) +- [axios adapter](/tutorial/request-adapter/alova-adapter-axios) +- [uniapp adapter](/tutorial/request-adapter/alova-adapter-uniapp) +- [taro adapter](/tutorial/request-adapter/alova-adapter-taro) + +## Global response cache + +You can also set the response cache globally, which we will explain in detail in the [Response Cache](/tutorial/cache/mode) chapter later. diff --git a/versioned_docs/version-2.x/tutorial/02-getting-started/05-global-interceptor.md b/versioned_docs/version-2.x/tutorial/02-getting-started/05-global-interceptor.md index fbb8d9e6b..b67bbceeb 100644 --- a/versioned_docs/version-2.x/tutorial/02-getting-started/05-global-interceptor.md +++ b/versioned_docs/version-2.x/tutorial/02-getting-started/05-global-interceptor.md @@ -1,137 +1,136 @@ ---- -title: Global Interceptor -sidebar_position: 50 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -## Global request interceptor - -Usually, we need to use the same configuration for all requests, such as adding token and timestamp to the request header. At this time, we can set a global request interceptor, which will be triggered before all requests. We can set this interceptor Set request parameters uniformly. - -```mermaid -flowchart LR - R1[Request 1] --> beforeRequest - R2[Request 2] --> beforeRequest - R3[Request 3] --> beforeRequest - RN[Request N] --> beforeRequest - beforeRequest --> S1[Send request] -``` - -```javascript -const alovaInstance = createAlova({ - // ... - //The function parameter is a method instance, including request data such as url, params, data, headers, etc. - // You can modify these data freely - // highlight-start - beforeRequest(method) { - // Suppose we need to add token to the request header - method.config.headers.token = 'token'; - } - // highlight-end -}); -``` - -You can also set beforeRequest as an asynchronous function. - -```javascript -const alovaInstance = createAlova({ - // ... - // highlight-start - async beforeRequest(method) { - //Perform some asynchronous tasks - // ... - } - // highlight-end -}); -``` - -## Global response interceptor - -When we want to uniformly parse response data, uniformly handle errors, and uniformly handle request completion, we can specify a global response interceptor when creating an alova instance. The response interceptor includes an interceptor for successful requests and an interceptor for failed requests. and request completion interceptors. - -```mermaid -flowchart LR - classDef error fill:#f96,stroke:#f00,stroke-width:2px; - - R1[Request 1 successful] --> responded.onSuccess - R2[Request 2 successful] --> responded.onSuccess - RN[Request N successful] --> responded.onSuccess - R4[Request 4 failed]:::error --> responded.onError:::error - R5[Request M failed]:::error --> responded.onError:::error - responded.onSuccess --> responded.onComplete - responded.onError --> responded.onComplete -``` - -```javascript -const alovaInstance = createAlova({ - // ... - //Use two items of the array to specify the interceptor for successful request and the interceptor for failed request respectively. - responded: { - // highlight-start - // Interceptor for successful request - // When using the GlobalFetch request adapter, the first parameter receives the Response object - // The second parameter is the method instance of the current request. You can use it to synchronize the configuration information before and after the request. - onSuccess: async (response, method) => { - if (response.status >= 400) { - throw new Error(response.statusText); - } - const json = await response.json(); - if (json.code !== 200) { - // This request will throw an error when an error is thrown or a Promise instance with reject status is returned. - throw new Error(json.message); - } - - //The parsed response data will be passed to the transformData hook function of the method instance. These functions will be explained later. - return json.data; - }, - // highlight-end - - // highlight-start - // Interceptor for request failure - // This interceptor will be entered when a request error occurs. - // The second parameter is the method instance of the current request. You can use it to synchronize the configuration information before and after the request. - onError: (err, method) => { - alert(error.message); - }, - // highlight-end - - // highlight-start - //Interceptor for request completion - // When you need to execute logic whether the request succeeds, fails, or hits the cache, you can specify a global `onComplete` interceptor when creating an alova instance, such as turning off the request loading state. - // Receive the method instance of the current request - onComplete: async method => { - // Process request completion logic - } - // highlight-end - } -}); -``` - -If you do not need to set an interceptor for request failure or completion, you can directly pass in the interceptor function for successful request, instead of setting a callback through an object. - -```javascript -const alovaInstance = createAlova({ - // ... - // highlight-start - async responded(response, method) { - // Interceptor for successful request - } - // highlight-end -}); -``` - -:::info Interceptor triggering instructions - -When you use `GlobalFetch` to request the adapter, due to the characteristics of `window.fetch`, the `onError` interceptor will only be triggered when the connection times out or the connection is aborted. In other cases, the `onSuccess` interceptor will be triggered. [For details, please Check here](https://developer.mozilla.org/docs/Web/API/fetch) - -::: - -:::warning Special attention - -1. `onSuccess`, `onError` and `onComplete` can be set as synchronous functions and asynchronous functions. -2. The `onError` callback is a capture function for request errors. An error thrown in `onSuccess` will not trigger `onError`. When an error is caught but no error is thrown or a Promise instance that returns reject status is used, the request will be considered successful and no response data will be obtained. -3. In 2.0.x and previous versions, `responded` was incorrectly spelled as `responsed`. In 2.1.0, the two have been made compatible. It is recommended to use `responded` instead of `responsed` in subsequent versions. . - -::: +--- +title: Global Interceptor +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## Global request interceptor + +Usually, we need to use the same configuration for all requests, such as adding token and timestamp to the request header. At this time, we can set a global request interceptor, which will be triggered before all requests. We can set this interceptor Set request parameters uniformly. + +```mermaid +flowchart LR + R1[Request 1] --> beforeRequest + R2[Request 2] --> beforeRequest + R3[Request 3] --> beforeRequest + RN[Request N] --> beforeRequest + beforeRequest --> S1[Send request] +``` + +```javascript +const alovaInstance = createAlova({ + // ... + //The function parameter is a method instance, including request data such as url, params, data, headers, etc. + // You can modify these data freely + // highlight-start + beforeRequest(method) { + // Suppose we need to add token to the request header + method.config.headers.token = 'token'; + } + // highlight-end +}); +``` + +You can also set beforeRequest as an asynchronous function. + +```javascript +const alovaInstance = createAlova({ + // ... + // highlight-start + async beforeRequest(method) { + //Perform some asynchronous tasks + // ... + } + // highlight-end +}); +``` + +## Global response interceptor + +When we want to uniformly parse response data, uniformly handle errors, and uniformly handle request completion, we can specify a global response interceptor when creating an alova instance. The response interceptor includes an interceptor for successful requests and an interceptor for failed requests. and request completion interceptors. + +```mermaid +flowchart LR + classDef error fill:#f96,stroke:#f00,stroke-width:2px; + + R1[Request 1 successful] --> responded.onSuccess + R2[Request 2 successful] --> responded.onSuccess + RN[Request N successful] --> responded.onSuccess + R4[Request 4 failed]:::error --> responded.onError:::error + R5[Request M failed]:::error --> responded.onError:::error + responded.onSuccess --> responded.onComplete + responded.onError --> responded.onComplete +``` + +```javascript +const alovaInstance = createAlova({ + // ... + //Use two items of the array to specify the interceptor for successful request and the interceptor for failed request respectively. + responded: { + // highlight-start + // Interceptor for successful request + // When using the GlobalFetch request adapter, the first parameter receives the Response object + // The second parameter is the method instance of the current request. You can use it to synchronize the configuration information before and after the request. + onSuccess: async (response, method) => { + if (response.status >= 400) { + throw new Error(response.statusText); + } + const json = await response.json(); + if (json.code !== 200) { + // This request will throw an error when an error is thrown or a Promise instance with reject status is returned. + throw new Error(json.message); + } + + //The parsed response data will be passed to the transformData hook function of the method instance. These functions will be explained later. + return json.data; + }, + // highlight-end + + // highlight-start + // Interceptor for request failure + // This interceptor will be entered when a request error occurs. + // The second parameter is the method instance of the current request. You can use it to synchronize the configuration information before and after the request. + onError: (err, method) => { + alert(error.message); + }, + // highlight-end + + // highlight-start + //Interceptor for request completion + // When you need to execute logic whether the request succeeds, fails, or hits the cache, you can specify a global `onComplete` interceptor when creating an alova instance, such as turning off the request loading state. + // Receive the method instance of the current request + onComplete: async method => { + // Process request completion logic + } + // highlight-end + } +}); +``` + +If you do not need to set an interceptor for request failure or completion, you can directly pass in the interceptor function for successful request, instead of setting a callback through an object. + +```javascript +const alovaInstance = createAlova({ + // ... + // highlight-start + async responded(response, method) { + // Interceptor for successful request + } + // highlight-end +}); +``` + +:::info Interceptor triggering instructions + +When you use `GlobalFetch` to request the adapter, due to the characteristics of `window.fetch`, the `onError` interceptor will only be triggered when the connection times out or the connection is aborted. In other cases, the `onSuccess` interceptor will be triggered. [For details, please Check here](https://developer.mozilla.org/docs/Web/API/fetch) + +::: + +:::warning Special attention + +1. `onSuccess`, `onError` and `onComplete` can be set as synchronous functions and asynchronous functions. +2. The `onError` callback is a capture function for request errors. An error thrown in `onSuccess` will not trigger `onError`. When an error is caught but no error is thrown or a Promise instance that returns reject status is used, the request will be considered successful and no response data will be obtained. +3. In 2.0.x and previous versions, `responded` was incorrectly spelled as `responsed`. In 2.1.0, the two have been made compatible. It is recommended to use `responded` instead of `responsed` in subsequent versions. . + +::: diff --git a/versioned_docs/version-2.x/tutorial/02-getting-started/06-method-metadata.md b/versioned_docs/version-2.x/tutorial/02-getting-started/06-method-metadata.md index 995782ccb..a89f55a09 100644 --- a/versioned_docs/version-2.x/tutorial/02-getting-started/06-method-metadata.md +++ b/versioned_docs/version-2.x/tutorial/02-getting-started/06-method-metadata.md @@ -1,157 +1,156 @@ ---- -title: Method Metadata -sidebar_position: 60 ---- - -:::info version requirements - -v2.7.0+ - -::: - -Method instances run through the entire request life cycle of alova, and there will be a large number of different method instances in the project. Sometimes we need to add additional information to specific method instances to facilitate their identification or additional information transfer. Wait, at this point, we need to use method metadata. - -## Use metadata to identify identities - -### Use identity before request - -For example, most of the interfaces in your project need to be accompanied by `token` for each request, but there are still some interfaces that do not require verification. You may handle them uniformly in the global `beforeRequest` function. - -```javascript -const nonvalidateRequiredApi = [ - '/api/url1', - '/api/url2', - '/api/url3' - // ... -]; - -createAlova({ - beforeRequest(method) { - if (!nonvalidateRequiredApi.includes(method.url)) { - method.config.headers.token = '...'; - } - } -}); -``` - -This will cause the following two problems: - -1. Information is not aggregated with method instances, making maintainability even worse; -2. Coding is more troublesome; - -To solve these two problems, we will use metadata to identify a specific method instance when it is created. - -**Step 1: Define metadata when creating method instance** - -```javascript -const loginAPI = (username, password) => { - const methodInstance = alovaInst.Post('/login', { - username, - password - }); - methodInstance.meta = { - ignoreToken: true - }; - return methodInstance; -}; -``` - -**[2.18.0+]** And you can also directly define metadata in config - -```javascript -const loginAPI = (username, password) => { - return alovaInst.Post( - '/login', - { - username, - password - }, - { - meta: { - ignoreToken: true - } - } - ); -}; -``` - -**Step 2: Use metadata as the basis for judgment in `beforeRequest`** - -```javascript -createAlova({ - // ... - beforeRequest(method) { - if (!method.meta?.ignoreToken) { - method.config.headers.token = '...'; - } - } -}); -``` - -### Use the identity after the response - -This method can also be used in global `responded`. For example, in most cases, the request api will return json data, but there may be a file download interface, which will return a binary data stream. In this case Below, you can use different metadata in `responded` to handle different responses separately. - -**Step one: When creating a method instance, you also need to assign a metadata** - -```javascript -const downloadAPI = filePath => { - const methodInstance = alovaInst.Post('/download_file', { - filePath - }); - methodInstance.meta = { - isDownload: true - }; - return methodInstance; -}; -``` - -**Step 2: Use metadata in `responded` as a basis for judgment** - -```javascript -createAlova({ - // ... - responded: - onSuccess: (response, method) => method.meta?.isDownload ? response.blob() : response.json() - onError: (error, method) => { - //Metadata of method instances can also be accessed when responding to errors - } - } -}); -``` - -## Use metadata to pass information - -In some cases, if you want to add additional information to different method instances for use elsewhere, you can also use metadata to save it. Take uniformly generating different method instance IDs as an example. - -```javascript -createAlova({ - beforeRequest(method) { - if (!method.meta.generateId) { - method.meta.uid = generateUUID(); - } - }, - - responded: { - onSuccess(response, method) { - // Access the meta data generated by the current method when the request is successful. - const currentMethodUID = method.meta.uid; - }, - onError(error, method) { - //Access the meta data generated by the current method when the request fails. - const currentMethodUID = method.meta.uid; - } - } -}); -``` - -## Tips for non-typescript projects - -In a non-typescript environment, you can use any attribute as an information carrier, not limited to the `meta` attribute. - -```javascript -methodInstance.showResponseMsg = true; -methodInstance.others = 'abc'; -``` - -Only in the typescript environment, any attribute name will report that the attribute "$0" does not exist. ts(2339)`, so in the type we specify the `meta` attribute as the information carrier. +--- +title: Method Metadata +--- + +:::info version requirements + +v2.7.0+ + +::: + +Method instances run through the entire request life cycle of alova, and there will be a large number of different method instances in the project. Sometimes we need to add additional information to specific method instances to facilitate their identification or additional information transfer. Wait, at this point, we need to use method metadata. + +## Use metadata to identify identities + +### Use identity before request + +For example, most of the interfaces in your project need to be accompanied by `token` for each request, but there are still some interfaces that do not require verification. You may handle them uniformly in the global `beforeRequest` function. + +```javascript +const nonvalidateRequiredApi = [ + '/api/url1', + '/api/url2', + '/api/url3' + // ... +]; + +createAlova({ + beforeRequest(method) { + if (!nonvalidateRequiredApi.includes(method.url)) { + method.config.headers.token = '...'; + } + } +}); +``` + +This will cause the following two problems: + +1. Information is not aggregated with method instances, making maintainability even worse; +2. Coding is more troublesome; + +To solve these two problems, we will use metadata to identify a specific method instance when it is created. + +**Step 1: Define metadata when creating method instance** + +```javascript +const loginAPI = (username, password) => { + const methodInstance = alovaInst.Post('/login', { + username, + password + }); + methodInstance.meta = { + ignoreToken: true + }; + return methodInstance; +}; +``` + +**[2.18.0+]** And you can also directly define metadata in config + +```javascript +const loginAPI = (username, password) => { + return alovaInst.Post( + '/login', + { + username, + password + }, + { + meta: { + ignoreToken: true + } + } + ); +}; +``` + +**Step 2: Use metadata as the basis for judgment in `beforeRequest`** + +```javascript +createAlova({ + // ... + beforeRequest(method) { + if (!method.meta?.ignoreToken) { + method.config.headers.token = '...'; + } + } +}); +``` + +### Use the identity after the response + +This method can also be used in global `responded`. For example, in most cases, the request api will return json data, but there may be a file download interface, which will return a binary data stream. In this case Below, you can use different metadata in `responded` to handle different responses separately. + +**Step one: When creating a method instance, you also need to assign a metadata** + +```javascript +const downloadAPI = filePath => { + const methodInstance = alovaInst.Post('/download_file', { + filePath + }); + methodInstance.meta = { + isDownload: true + }; + return methodInstance; +}; +``` + +**Step 2: Use metadata in `responded` as a basis for judgment** + +```javascript +createAlova({ + // ... + responded: + onSuccess: (response, method) => method.meta?.isDownload ? response.blob() : response.json() + onError: (error, method) => { + //Metadata of method instances can also be accessed when responding to errors + } + } +}); +``` + +## Use metadata to pass information + +In some cases, if you want to add additional information to different method instances for use elsewhere, you can also use metadata to save it. Take uniformly generating different method instance IDs as an example. + +```javascript +createAlova({ + beforeRequest(method) { + if (!method.meta.generateId) { + method.meta.uid = generateUUID(); + } + }, + + responded: { + onSuccess(response, method) { + // Access the meta data generated by the current method when the request is successful. + const currentMethodUID = method.meta.uid; + }, + onError(error, method) { + //Access the meta data generated by the current method when the request fails. + const currentMethodUID = method.meta.uid; + } + } +}); +``` + +## Tips for non-typescript projects + +In a non-typescript environment, you can use any attribute as an information carrier, not limited to the `meta` attribute. + +```javascript +methodInstance.showResponseMsg = true; +methodInstance.others = 'abc'; +``` + +Only in the typescript environment, any attribute name will report that the attribute "$0" does not exist. ts(2339)`, so in the type we specify the `meta` attribute as the information carrier. diff --git a/versioned_docs/version-2.x/tutorial/03-combine-framework/02-use-request.md b/versioned_docs/version-2.x/tutorial/03-combine-framework/02-use-request.md index 0cba915dc..af02e9b55 100644 --- a/versioned_docs/version-2.x/tutorial/03-combine-framework/02-use-request.md +++ b/versioned_docs/version-2.x/tutorial/03-combine-framework/02-use-request.md @@ -1,6 +1,5 @@ --- title: Auto Manage States -sidebar_position: 20 --- import Tabs from '@theme/Tabs'; diff --git a/versioned_docs/version-2.x/tutorial/03-combine-framework/03-use-watcher.md b/versioned_docs/version-2.x/tutorial/03-combine-framework/03-use-watcher.md index ce8fca0f7..5d8843d41 100644 --- a/versioned_docs/version-2.x/tutorial/03-combine-framework/03-use-watcher.md +++ b/versioned_docs/version-2.x/tutorial/03-combine-framework/03-use-watcher.md @@ -1,315 +1,322 @@ ---- -title: Watching Request -sidebar_position: 30 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; -import EmbedSandpack from "@site/src/components/EmbedSandpack"; -import CodeBlock from '@theme/CodeBlock'; -import useWatcherSearchVue from '!!raw-loader!@site/codesandbox/03-learning/04-use-watcher/vueComposition-search.en.vue'; -import useWatcherSearchReact from '!!raw-loader!@site/codesandbox/03-learning/04-use-watcher/react-search.en.jsx'; -import useWatcherSearchSvelte from '!!raw-loader!@site/codesandbox/03-learning/04-use-watcher/svelte-search.en.svelte'; -import useWatcherSearchVueOptions from '!!raw-loader!@site/codesandbox/03-learning/04-use-watcher/vueOptions-search.en.vue'; - -In some scenarios that require re-requesting as data changes, such as paging, data filtering, and fuzzy search, you can use `useWatcher` to monitor the specified states change and send a request immediately. - -## Keyword search - -Next we take searching for todo items as an example. Try to change options in the search box and see how the todo list changes. - - - - - - - - - - - - - - -{useWatcherSearchSvelte} - - - - - - - - - -## Pagination - -Taking the todo list pagination request as an example, you can do this. - - - - -```html - - - -``` - - - - -```jsx -import { useState } from 'react'; - -//method instance creation function -const getTodoList = currentPage => { - return alovaInstance.Get('/todo/list', { - params: { - currentPage, - pageSize: 10 - } - }); -}; - -const App = () => { - const [currentPage, setCurrentPage] = useState(1); - const { - loading, - data, - error - - // The first parameter is the function that returns the method instance, not the method instance itself - } = useWatcher( - () => getTodoList(currentPage), - // The watched states array, these states changes will trigger a request - [currentPage], - { - // ⚠️Calling useWatcher is not triggered by default. Please note the difference from useRequest. - // Manually set immediate to true to initially obtain the data on page 1 - immediate: true - } - ); - - return { - /* ... */ - }; -}; -``` - - - - -```html - - - -``` - - - - -```html - - - -``` - - - - -## Send request immediately - -`useWatcher` can also send the request immediately, but it should be noted that the `immediate` attribute of `useWatcher` defaults to `false`. - -```javascript -const { send } = useWatcher(() => getTodoList(currentPage), [currentPage], { - // highlight-start - immediate: true - // highlight-end -}); -send(); -``` - -## Request debounce - -Usually we write debounce code at the level of frequently triggered events. This time we implemented the debounce function at the request level, which means you no longer have to implement debounce yourself in the fuzzy search function, and the usage is also very simple. - -:::info Tips: What is debounce? - -Debounce means that after an event is triggered, the function can only be executed once within n seconds. If an event is triggered again within n seconds after the event is triggered, the function delay execution time will be recalculated (here to distinguish between throttling, throttling means that the event cannot be triggered again within a period of time after the event is triggered) - -::: - -### Set debounce time of all watching states - -```javascript -const { loading, data, error } = useWatcher(() => filterTodoList(keyword, date), [keyword, date], { - // highlight-start - // When debounce is set to a number, it represents the anti-bounce time of all watching states, in milliseconds. - // This means that when one or more of the states keyword and date change, the request will be sent after 500ms.beg - debounce: 500 - // highlight-end -}); -``` - -### Set debounce time for a single watching state - -In many scenarios, we only need to debounce certain frequently changing watching states, such as state changes triggered by `onInput` of a text box. You can do this: - -```javascript -const { loading, data, error } = useWatcher(() => filterTodoList(keyword, date), [keyword, date], { - // highlight-start - // Set debounce time in the order of the array of watching states. 0 or not passed means no debounce. - // The order of the watching states here is [keyword, date], and the debounce array setting is [500, 0], which means that only the debounce is set separately for the keyword. - Debounce: [500, 0] - // You can also set it as follows: - // debounce: [500], - // highlight-end -}); -``` - -## Block request when the states changes - -Sometimes you want not to send a request when the watched state changes. You can use the sendable attribute in the Hook configuration to control whether to send a request when the watched state changes. The sendable attribute is a function whose parameter is the `AlovaEvent` event object. Contains the array `sendArgs` composed of the parameters passed in by the `send` function, and the `method` instance of the current request, and the function returns a `truthy/falsy` value to determine whether the request needs to be triggered when the states changes (default is `true`), **throwing an error also means not triggering the request**. - -```javascript -useWatcher( - () => getTodoList($currentPage), - // The watched states array, these states changes will trigger a request - [state], - { - // highlight-start - sendable: ({ sendArgs, method }) => { - // do something - // Only send request when state is 1 - return state === 1; - } - // highlight-end - } -); -``` - -## Request timing - -Sometimes when the states watched by `useWatcher` changes continuously resulting in the initiation of consecutive requests, the latter request gets a response before the previous request, but when the previous request gets a response, it will overwrite the response of the latter request. Resulting in getting a response that does not match the state; for example, if the state `state` changes, a request `1` is issued, and then when the request `1` has not responded, the value of `state` is changed and a request` is issued. 2`, if request `1` is returned after request `2`, the final response data will remain at request `1`. -So we designed the `abortLast` parameter, which is used to mark whether to abort the last unresponsive request when the next request is issued. The default is `true`, so that the request issued by `useWatcher` is only valid for the last time. - -```mermaid -sequenceDiagram - participant U as User - participant S as Server - U ->> U: watch state - U ->> S: state change to trigger request 1 - U ->> S: state change to trigger request 2 - S ->> U: Request 2 responded first - S ->> U: Then request 1 responded - U ->> U: Response of request 2 is overwritten -``` - -```javascript -useWatcher( - () => getTodoList($currentPage), - // The watched states array, these states changes will trigger a request - [state], - { - // highlight-start - abortLast: true // Whether to abort the last unresponsive request, the default is true - // highlight-end - } -); -``` - -:::warning Notes - -`abortLast` defaults to `true`. If it is changed to `false`, it may cause the problem of states and response mismatch. - -::: +--- +title: Watching Request +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import EmbedSandpack from "@site/src/components/EmbedSandpack"; +import CodeBlock from '@theme/CodeBlock'; +import useWatcherSearchVue from '!!raw-loader!@site/codesandbox/03-learning/04-use-watcher/vueComposition-search.en.vue'; +import useWatcherSearchReact from '!!raw-loader!@site/codesandbox/03-learning/04-use-watcher/react-search.en.jsx'; +import useWatcherSearchSvelte from '!!raw-loader!@site/codesandbox/03-learning/04-use-watcher/svelte-search.en.svelte'; +import useWatcherSearchVueOptions from '!!raw-loader!@site/codesandbox/03-learning/04-use-watcher/vueOptions-search.en.vue'; + +In some scenarios that require re-requesting as data changes, such as paging, data filtering, and fuzzy search, you can use `useWatcher` to monitor the specified states change and send a request immediately. + +## Keyword search + +Next we take searching for todo items as an example. Try to change options in the search box and see how the todo list changes. + + + + + + + + + + + + + + +{useWatcherSearchSvelte} + + + + + + + + + +## Pagination + +Taking the todo list pagination request as an example, you can do this. + + + + +```html + + + +``` + + + + +```jsx +import { useState } from 'react'; + +//method instance creation function +const getTodoList = currentPage => { + return alovaInstance.Get('/todo/list', { + params: { + currentPage, + pageSize: 10 + } + }); +}; + +const App = () => { + const [currentPage, setCurrentPage] = useState(1); + const { + loading, + data, + error + + // The first parameter is the function that returns the method instance, not the method instance itself + } = useWatcher( + () => getTodoList(currentPage), + // The watched states array, these states changes will trigger a request + [currentPage], + { + // ⚠️Calling useWatcher is not triggered by default. Please note the difference from useRequest. + // Manually set immediate to true to initially obtain the data on page 1 + immediate: true + } + ); + + return { + /* ... */ + }; +}; +``` + + + + +```html + + + +``` + + + + +```html + + + +``` + + + + +## Send request immediately + +`useWatcher` can also send the request immediately, but it should be noted that the `immediate` attribute of `useWatcher` defaults to `false`. + +```javascript +const { send } = useWatcher(() => getTodoList(currentPage), [currentPage], { + // highlight-start + immediate: true + // highlight-end +}); +send(); +``` + +## Request debounce + +Usually we write debounce code at the level of frequently triggered events. This time we implemented the debounce function at the request level, which means you no longer have to implement debounce yourself in the fuzzy search function, and the usage is also very simple. + +:::info Tips: What is debounce? + +Debounce means that after an event is triggered, the function can only be executed once within n seconds. If an event is triggered again within n seconds after the event is triggered, the function delay execution time will be recalculated (here to distinguish between throttling, throttling means that the event cannot be triggered again within a period of time after the event is triggered) + +::: + +### Set debounce time of all watching states + +```javascript +const { loading, data, error } = useWatcher( + () => filterTodoList(keyword, date), + [keyword, date], + { + // highlight-start + // When debounce is set to a number, it represents the anti-bounce time of all watching states, in milliseconds. + // This means that when one or more of the states keyword and date change, the request will be sent after 500ms.beg + debounce: 500 + // highlight-end + } +); +``` + +### Set debounce time for a single watching state + +In many scenarios, we only need to debounce certain frequently changing watching states, such as state changes triggered by `onInput` of a text box. You can do this: + +```javascript +const { loading, data, error } = useWatcher( + () => filterTodoList(keyword, date), + [keyword, date], + { + // highlight-start + // Set debounce time in the order of the array of watching states. 0 or not passed means no debounce. + // The order of the watching states here is [keyword, date], and the debounce array setting is [500, 0], which means that only the debounce is set separately for the keyword. + Debounce: [500, 0] + // You can also set it as follows: + // debounce: [500], + // highlight-end + } +); +``` + +## Block request when the states changes + +Sometimes you want not to send a request when the watched state changes. You can use the sendable attribute in the Hook configuration to control whether to send a request when the watched state changes. The sendable attribute is a function whose parameter is the `AlovaEvent` event object. Contains the array `sendArgs` composed of the parameters passed in by the `send` function, and the `method` instance of the current request, and the function returns a `truthy/falsy` value to determine whether the request needs to be triggered when the states changes (default is `true`), **throwing an error also means not triggering the request**. + +```javascript +useWatcher( + () => getTodoList($currentPage), + // The watched states array, these states changes will trigger a request + [state], + { + // highlight-start + sendable: ({ sendArgs, method }) => { + // do something + // Only send request when state is 1 + return state === 1; + } + // highlight-end + } +); +``` + +## Request timing + +Sometimes when the states watched by `useWatcher` changes continuously resulting in the initiation of consecutive requests, the latter request gets a response before the previous request, but when the previous request gets a response, it will overwrite the response of the latter request. Resulting in getting a response that does not match the state; for example, if the state `state` changes, a request `1` is issued, and then when the request `1` has not responded, the value of `state` is changed and a request` is issued. 2`, if request `1` is returned after request `2`, the final response data will remain at request `1`. +So we designed the `abortLast` parameter, which is used to mark whether to abort the last unresponsive request when the next request is issued. The default is `true`, so that the request issued by `useWatcher` is only valid for the last time. + +```mermaid +sequenceDiagram + participant U as User + participant S as Server + U ->> U: watch state + U ->> S: state change to trigger request 1 + U ->> S: state change to trigger request 2 + S ->> U: Request 2 responded first + S ->> U: Then request 1 responded + U ->> U: Response of request 2 is overwritten +``` + +```javascript +useWatcher( + () => getTodoList($currentPage), + // The watched states array, these states changes will trigger a request + [state], + { + // highlight-start + abortLast: true // Whether to abort the last unresponsive request, the default is true + // highlight-end + } +); +``` + +:::warning Notes + +`abortLast` defaults to `true`. If it is changed to `false`, it may cause the problem of states and response mismatch. + +::: diff --git a/versioned_docs/version-2.x/tutorial/03-combine-framework/04-initial-data.md b/versioned_docs/version-2.x/tutorial/03-combine-framework/04-initial-data.md index 6a37d4136..2a7c5546e 100644 --- a/versioned_docs/version-2.x/tutorial/03-combine-framework/04-initial-data.md +++ b/versioned_docs/version-2.x/tutorial/03-combine-framework/04-initial-data.md @@ -1,6 +1,5 @@ --- title: Initial Data -sidebar_position: 40 --- When using `useRequest`, its `data` value defaults to undefined before the request is successful, but sometimes we need `data` to have an initial value before the request is successful. For example, when requesting a list, we usually need to initialize it to `[]`, otherwise it will throw error when rendering the list. diff --git a/versioned_docs/version-2.x/tutorial/03-combine-framework/05-response.md b/versioned_docs/version-2.x/tutorial/03-combine-framework/05-response.md index 231b07f59..b88cccf9a 100644 --- a/versioned_docs/version-2.x/tutorial/03-combine-framework/05-response.md +++ b/versioned_docs/version-2.x/tutorial/03-combine-framework/05-response.md @@ -1,6 +1,5 @@ --- title: Process Response -sidebar_position: 50 --- import Tabs from '@theme/Tabs'; diff --git a/versioned_docs/version-2.x/tutorial/03-combine-framework/06-abort-request.md b/versioned_docs/version-2.x/tutorial/03-combine-framework/06-abort-request.md index 404e478af..d51fbc734 100644 --- a/versioned_docs/version-2.x/tutorial/03-combine-framework/06-abort-request.md +++ b/versioned_docs/version-2.x/tutorial/03-combine-framework/06-abort-request.md @@ -1,6 +1,5 @@ --- title: Abort Request -sidebar_position: 60 --- Receive `abort` for manual abort request via useHook. diff --git a/versioned_docs/version-2.x/tutorial/03-combine-framework/07-download-upload-progress.md b/versioned_docs/version-2.x/tutorial/03-combine-framework/07-download-upload-progress.md index 5ffba3d35..a91782a79 100644 --- a/versioned_docs/version-2.x/tutorial/03-combine-framework/07-download-upload-progress.md +++ b/versioned_docs/version-2.x/tutorial/03-combine-framework/07-download-upload-progress.md @@ -1,207 +1,206 @@ ---- -title: Download & Upload Progress -sidebar_position: 70 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -useHook provides `downloading` and `uploading` for displaying progress information directly in the view. - -## Download progress - -For performance reasons, there is no progress information in `downloading` by default. You need to set `enableDownload` of the method instance to `true`, which will continuously update the `downloading` status during the download process. - - - - -```html - - - -``` - - - - -```jsx -const downloadGetter = alovaInstance.Get('/todo/downloadfile', { - // highlight-start - // Start download progress - enableDownload: true - // highlight-end -}); - -const App = () => { - const { downloading } = useRequest(downloadGetter); - return ( - <> -
File size: {downloading.total}B
-
Downloaded: {downloading.loaded}B
-
Progress: {(downloading.loaded / downloading.total) * 100}%
- - ); -}; -``` - -
- - -```html - - -
File size: {$downloading.total}B
-
Downloaded: {$downloading.loaded}B
-
Progress: {$downloading.loaded / $downloading.total * 100}%
-``` - -
- - -```html - - - -``` - - -
- -## Upload progress - -Using the upload progress status is the same as using the download progress. First enable the upload progress information through `enableUpload`, and then receive it by receiving the `uploading` responsive state. - - - - -```html - - - -``` - - - - -```jsx -const uploadPoster = alovaInstance.Post('/todo/uploadfile', formData, { - // highlight-start - // Start upload progress - enableUpload: true - // highlight-end -}); - -const App = () => { - const { uploading } = useRequest(uploadPoster); - return ( - <> -
File size: {uploading.total}B
-
Uploaded: {uploading.loaded}B
-
Progress: {(uploading.loaded / uploading.total) * 100}%
- - ); -}; -``` - -
- - -```html - - -
File size: {$uploading.total}B
-
Uploaded: {$uploading.loaded}B
-
Progress: {$uploading.loaded / $uploading.total * 100}%
-``` - -
- - -```html - - - -``` - - -
+--- +title: Download & Upload Progress +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +useHook provides `downloading` and `uploading` for displaying progress information directly in the view. + +## Download progress + +For performance reasons, there is no progress information in `downloading` by default. You need to set `enableDownload` of the method instance to `true`, which will continuously update the `downloading` status during the download process. + + + + +```html + + + +``` + + + + +```jsx +const downloadGetter = alovaInstance.Get('/todo/downloadfile', { + // highlight-start + // Start download progress + enableDownload: true + // highlight-end +}); + +const App = () => { + const { downloading } = useRequest(downloadGetter); + return ( + <> +
File size: {downloading.total}B
+
Downloaded: {downloading.loaded}B
+
Progress: {(downloading.loaded / downloading.total) * 100}%
+ + ); +}; +``` + +
+ + +```html + + +
File size: {$downloading.total}B
+
Downloaded: {$downloading.loaded}B
+
Progress: {$downloading.loaded / $downloading.total * 100}%
+``` + +
+ + +```html + + + +``` + + +
+ +## Upload progress + +Using the upload progress status is the same as using the download progress. First enable the upload progress information through `enableUpload`, and then receive it by receiving the `uploading` responsive state. + + + + +```html + + + +``` + + + + +```jsx +const uploadPoster = alovaInstance.Post('/todo/uploadfile', formData, { + // highlight-start + // Start upload progress + enableUpload: true + // highlight-end +}); + +const App = () => { + const { uploading } = useRequest(uploadPoster); + return ( + <> +
File size: {uploading.total}B
+
Uploaded: {uploading.loaded}B
+
Progress: {(uploading.loaded / uploading.total) * 100}%
+ + ); +}; +``` + +
+ + +```html + + +
File size: {$uploading.total}B
+
Uploaded: {$uploading.loaded}B
+
Progress: {$uploading.loaded / $uploading.total * 100}%
+``` + +
+ + +```html + + + +``` + + +
diff --git a/versioned_docs/version-2.x/tutorial/03-combine-framework/08-receive-params.md b/versioned_docs/version-2.x/tutorial/03-combine-framework/08-receive-params.md index fa4686bdb..a5f856185 100644 --- a/versioned_docs/version-2.x/tutorial/03-combine-framework/08-receive-params.md +++ b/versioned_docs/version-2.x/tutorial/03-combine-framework/08-receive-params.md @@ -1,6 +1,5 @@ --- title: Receive Params -sidebar_position: 80 --- In both `useRequest` and `useWatcher` we can call the `send` function to manually trigger the request. When the send function triggers the request, any number of parameters can be passed in. These parameters can actually be received by the following three locations. @@ -32,7 +31,9 @@ send(2); Received through `event.sendArgs` in the event callback function, which is an array containing all parameters of the send function. ```javascript -const { send, onSuccess, onError, onComplete } = useRequest(newTodo => alovaInstance.Post('/todo', newTodo)); +const { send, onSuccess, onError, onComplete } = useRequest(newTodo => + alovaInstance.Post('/todo', newTodo) +); onSuccess(event => { //The value of sendArgs is [1] console.log(event.sendArgs); diff --git a/versioned_docs/version-2.x/tutorial/03-combine-framework/10-typescript.md b/versioned_docs/version-2.x/tutorial/03-combine-framework/10-typescript.md index de279e29c..91e48217e 100644 --- a/versioned_docs/version-2.x/tutorial/03-combine-framework/10-typescript.md +++ b/versioned_docs/version-2.x/tutorial/03-combine-framework/10-typescript.md @@ -1,159 +1,158 @@ ---- -title: Typescript -sidebar_position: 100 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -In terms of Typescript, we have indeed spent a lot of effort on optimization in order to provide a better user experience. We try our best to use automatic type inference to reduce the trouble of defining types for you. - -## Automatically infer alova useHooks states type - -When createAlova creates an alova instance, the state types created by `useRequest`, `useWatcher`, and `useFetcher` will be automatically inferred based on the passed `statesHook`. Currently only Vue, React, and Svelte are supported. - -> `useFetcher` is a useHook used for data fetching. For details, please read [Advanced-Data fetching chapter](/tutorial/advanced/use-fetcher). - -The following are the status types returned by useHooks by default. - - - - -```typescript -const vueAlova = createAlova({ - statesHook: VueHook - // ... -}); -const { - loading, // Ref - data, // Ref<{ data: any }> - error // Ref -} = useRequest(vueAlova.Get<{ data: any }>('/todo/list')); -``` - - - - -```typescript -const reactAlova = createAlova({ - statesHook: ReactHook - // ... -}); -const { - loading, // boolean - data, // { data: any } - error // Error -} = useRequest(reactAlova.Get<{ data: any }>('/todo/list')); -``` - - - - -```typescript -const svelteAlova = createAlova({ - statesHook: SvelteHook - // ... -}); -const { - loading, // Writable - data, // Writable<{ data: any }> - error // Writable -} = useRequest(svelteAlova.Get<{ data: any }>('/todo/list')); -``` - - - - -The type of data will be different depending on the response data type specified in different Method instances, let's continue to look below. - -## Type of response data - -When you specify a type for a data interface, you need to divide it into two situations. - -### Case 1 - -When the response data does not need to be converted by calling `transformData`, the type can be specified directly through generics. - -```typescript -interface Todo { - title: string; - time: string; - done: boolean; -} -const Get = alovaInstance.Get('/todo/list'); -const { data } = useRequest(Get); -// vue: The type of data is Ref -// react: The type of data is Todo[] -// svelte: The type of data is Writable -``` - -### Case 2 - -When the response data needs to be converted by calling `transformData`, the type needs to be specified in the conversion function parameter, and then its return value type will be used as the response data type. - -```typescript -interface Todo { - title: string; - time: string; - done: boolean; -} -const Get = alovaInstance.Get('/todo/list', { - //Write the type into the data parameter, and the headers will be automatically inferred, so you don’t need to specify the type. - transformData(data: Todo[], headers) { - return data.map(item => ({ - ...item, - status: item.done ? 'Completed' : 'Not completed' - })); - } -}); - -const { data } = useRequest(Get); -// vue: The type of data is Ref<(Todo & { status: string })[]> -// react: The type of data is (Todo & { status: string })[] -// svelte: The type of data is Writable<(Todo & { status: string })[]> -``` - -:::warning note - -The response data is converted by the global response interceptor, so when setting the type, it should also be set to the converted type. - -::: - -## Type inferred from request adapter - -Because alova supports custom request adapters, and the request configuration objects, response objects, and response headers of different adapters may be different, so the global `beforeRequest`, `responded` interceptors, and the configuration object when the `Method` instance is created The types will be automatically inferred based on the types provided by the request adapter. Let's look at these types first. - -If you are using [**GlobalFetch**](https://github.com/alovajs/alova/blob/main/src/predefine/GlobalFetch.ts), alova will automatically infer the type using `fetch api`, The types of fetch api are as follows. - -```typescript -declare function fetch(input: RequestInfo | URL, init?: RequestInit): Promise; -``` - -### Method configuration type of instance - -The method configuration type will be automatically inferred as: - -```typescript -// AlovaMethodCommonConfig is a unified request parameter and behavior parameter -// highlight-start -const methodConfig: AlovaMethodCommonConfig & RequestInit = { - // highlight-end - // ... -}; -alovaInstance.Get('/api/user', methodConfig); -``` - -### Global response interceptor parameter type - -The type of responded interceptor will be automatically inferred as: - -```typescript -createAlova({ - // ... - // highlight-start - responded: (response: Response, method: Method) => { - // highlight-end - // ... - } -}); -``` +--- +title: Typescript +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +In terms of Typescript, we have indeed spent a lot of effort on optimization in order to provide a better user experience. We try our best to use automatic type inference to reduce the trouble of defining types for you. + +## Automatically infer alova useHooks states type + +When createAlova creates an alova instance, the state types created by `useRequest`, `useWatcher`, and `useFetcher` will be automatically inferred based on the passed `statesHook`. Currently only Vue, React, and Svelte are supported. + +> `useFetcher` is a useHook used for data fetching. For details, please read [Advanced-Data fetching chapter](/tutorial/advanced/use-fetcher). + +The following are the status types returned by useHooks by default. + + + + +```typescript +const vueAlova = createAlova({ + statesHook: VueHook + // ... +}); +const { + loading, // Ref + data, // Ref<{ data: any }> + error // Ref +} = useRequest(vueAlova.Get<{ data: any }>('/todo/list')); +``` + + + + +```typescript +const reactAlova = createAlova({ + statesHook: ReactHook + // ... +}); +const { + loading, // boolean + data, // { data: any } + error // Error +} = useRequest(reactAlova.Get<{ data: any }>('/todo/list')); +``` + + + + +```typescript +const svelteAlova = createAlova({ + statesHook: SvelteHook + // ... +}); +const { + loading, // Writable + data, // Writable<{ data: any }> + error // Writable +} = useRequest(svelteAlova.Get<{ data: any }>('/todo/list')); +``` + + + + +The type of data will be different depending on the response data type specified in different Method instances, let's continue to look below. + +## Type of response data + +When you specify a type for a data interface, you need to divide it into two situations. + +### Case 1 + +When the response data does not need to be converted by calling `transformData`, the type can be specified directly through generics. + +```typescript +interface Todo { + title: string; + time: string; + done: boolean; +} +const Get = alovaInstance.Get('/todo/list'); +const { data } = useRequest(Get); +// vue: The type of data is Ref +// react: The type of data is Todo[] +// svelte: The type of data is Writable +``` + +### Case 2 + +When the response data needs to be converted by calling `transformData`, the type needs to be specified in the conversion function parameter, and then its return value type will be used as the response data type. + +```typescript +interface Todo { + title: string; + time: string; + done: boolean; +} +const Get = alovaInstance.Get('/todo/list', { + //Write the type into the data parameter, and the headers will be automatically inferred, so you don’t need to specify the type. + transformData(data: Todo[], headers) { + return data.map(item => ({ + ...item, + status: item.done ? 'Completed' : 'Not completed' + })); + } +}); + +const { data } = useRequest(Get); +// vue: The type of data is Ref<(Todo & { status: string })[]> +// react: The type of data is (Todo & { status: string })[] +// svelte: The type of data is Writable<(Todo & { status: string })[]> +``` + +:::warning note + +The response data is converted by the global response interceptor, so when setting the type, it should also be set to the converted type. + +::: + +## Type inferred from request adapter + +Because alova supports custom request adapters, and the request configuration objects, response objects, and response headers of different adapters may be different, so the global `beforeRequest`, `responded` interceptors, and the configuration object when the `Method` instance is created The types will be automatically inferred based on the types provided by the request adapter. Let's look at these types first. + +If you are using [**GlobalFetch**](https://github.com/alovajs/alova/blob/main/src/predefine/GlobalFetch.ts), alova will automatically infer the type using `fetch api`, The types of fetch api are as follows. + +```typescript +declare function fetch(input: RequestInfo | URL, init?: RequestInit): Promise; +``` + +### Method configuration type of instance + +The method configuration type will be automatically inferred as: + +```typescript +// AlovaMethodCommonConfig is a unified request parameter and behavior parameter +// highlight-start +const methodConfig: AlovaMethodCommonConfig & RequestInit = { + // highlight-end + // ... +}; +alovaInstance.Get('/api/user', methodConfig); +``` + +### Global response interceptor parameter type + +The type of responded interceptor will be automatically inferred as: + +```typescript +createAlova({ + // ... + // highlight-start + responded: (response: Response, method: Method) => { + // highlight-end + // ... + } +}); +``` diff --git a/versioned_docs/version-2.x/tutorial/04-cache/01-mode.md b/versioned_docs/version-2.x/tutorial/04-cache/01-mode.md index 175ffc3fe..7fe8e640a 100644 --- a/versioned_docs/version-2.x/tutorial/04-cache/01-mode.md +++ b/versioned_docs/version-2.x/tutorial/04-cache/01-mode.md @@ -1,215 +1,214 @@ ---- -title: Cache Mode -sidebar_position: 10 ---- - -import MemoryCache from '@site/example-links/MemoryCache'; -import StoragePlaceholder from '@site/example-links/StoragePlaceholder'; -import StorageRestore from '@site/example-links/StorageRestore'; - -The cache mode can be set at different granularities such as global or request level. When set globally, all Method instances created from the same alova instance will inherit the setting. - -:::info note - -Whether to use the cache mode and which cache mode to use depends on the scenario. The usage scenarios of different cache modes will be mentioned below when introducing different cache modes separately. - -::: - -## Memory mode (default) - -The memory mode puts the cache in the memory, which means that the page cache is invalidated when it is refreshed, and is the most commonly used cache mode. - -Memory mode is generally used to solve the performance consumption caused by frequent requests for the same data in a short period of time (minutes or seconds). For example, when you are writing a todo details page, you may think that users will frequently click on the todo list Check the details, if the user does not repeatedly request the interface when repeatedly viewing a certain detail, and can return the data immediately, the colleague who improves the response speed also reduces the pressure on the server. At this point we can set the response data cache for a todo detail `Method` instance. - -```javascript -alovaInstance.GET('/todo/list', { - //... - // highlight-start - localCache: { - // Set cache mode to memory mode - mode: 'memory', - - // unit is milliseconds - // When set to `Infinity`, it means that the data will never expire, and when it is set to 0 or a negative number, it means no caching - expire: 60 * 10 * 1000 - } - // highlight-end -}); -``` - -Memory mode is the default mode, you can abbreviate like this - -```javascript -alovaInstance.GET('/todo/list', { - //... - // highlight-start - localCache: 60 * 10 * 1000 - // highlight-end -}); -``` - -> GET requests will set the memory cache time of 300000ms (5 minutes) by default, and developers can also customize the settings. - -> If you need to set the caching mode globally, see [Global setting cache mode] at the bottom of this section (#Global setting cache mode) - -### Memory mode example - - - -## Cache placeholder mode - -This cache mode is used when you don't want to display the Loading icon every time the application is entered, but you want to use the old data instead, you can use the cache occupancy mode, which has a better experience than Loading. - -In the cache occupancy mode, `data` will be immediately assigned the old data of the last cache. You can judge that if there is old data, use it to replace the Loading display. At the same time, it will send a request to obtain the latest data and update the cache, so as to achieve In order to quickly display the actual data, and obtain the latest data. - -Set on `Method` instances: - -```javascript -const todoListGetter = alovaInstance.Get('/todo/list', { - //... - // highlight-start - localCache: { - // Set the cache mode to persistent placeholder mode - mode: 'placeholder', - // cache time - expire: 60 * 10 * 1000 - } - // highlight-end -}); -``` - -> If you need to set the caching mode globally, see [Global setting cache mode] at the bottom of this section (#Global setting cache mode) - -### Cache placeholder mode example - - - -## Restore mode - -In this mode, the server-side cached data will be persistent. If the expiration time is not reached, even if the page cache is refreshed, it will not be invalidated. It is generally used for some data that requires server-side management but is basically unchanged, such as the specific dates of annual holidays. It is different, but it will not change again. In this scenario, we only need to set the cache expiration time to the last moment of this year. - -Set on `Method` instances: - -```javascript -const todoListGetter = alovaInstance.Get('/todo/list', { - //... - // highlight-start - localCache: { - // Set the cache mode to persistent mode - mode: 'restore', - // cache time - expire: 60 * 10 * 1000 - } - // highlight-end -}); -``` - -:::warning Caution - -When request body is special data such as **FormData**, **Blob**, **ArrayBuffer**, **URLSearchParams**, **ReadableStream**, it will be considered that you intend to communicate with server. In this case would not cache data. - -::: - -> If you need to set the caching mode globally, see [Global setting cache mode] at the bottom of this section (#Global setting cache mode) - -### Restore mode example - - - -### What should I do if the data changes in restore mode? - -When the `Method` instance in restore mode is set, it may be due to the change of the interface data or the logic change of the front-end processing response data. At this time, it is necessary to let the user re-cache the changed data after publishing the application. At this time, you can use `tag` The attribute sets the cache tag. Each piece of persistent data contains a `tag` identifier. When the `tag` changes, the original persistent data will become invalid, and new data will be obtained again, and the new `tag` will be used for identification . - -```javascript -const todoListGetter = alovaInstance.Get('/todo/list', { - //... - localCache: { - mode: 'restore', - expire: 60 * 10 * 1000, - - // highlight-start - // Add or modify the tag parameter, the cached data will be invalid - // It is recommended to use version number management - tag: 'v1' - // highlight-end - } -}); -``` - -## Global setting cache mode - -:::info version required -v1.3.0+ -::: -The above settings are all set separately on `Method`. If you need to set the cache mode globally, you can do it as follows: - -```javascript -const alovaInstance = createAlova({ - //... - // highlight-start - localCache: { - // Uniformly set the cache mode of POST - POST: { - mode: 'placeholder', - expire: 60 * 10 * 1000 - }, - // Uniformly set the cache mode of the HEAD request - HEAD: 60 * 10 * 1000 - } - // highlight-end -}); -``` - -Henceforth, the `Method` instance created by `alovaInstance` instance will use this cache setting by default, and it can also be overridden in the `Method` instance. - -> Note: When the cache mode is set globally, the original 5-minute GET cache mode will be overwritten. - -## Disable caching mode globally - -If you don't want to use any request cache in your project, you can turn it off globally. If you want to use it only in a few specific requests, you can also turn it off globally and set it in the specified `Method` instance . - -```javascript -const alovaInstance = createAlova({ - //... - // highlight-start - // Set to null to disable all request caching globally - localCache: null - // highlight-end -}); -``` - -## Expiration time type - -There are two types of expiration time to choose from, namely **relative time** and **absolute time** - -### Relative Time - -That is, the time to expire when the cached data is saved, in **milliseconds**, the above examples are all of this type. - -```javascript -localCache: 60 * 10 * 1000; -``` - -```javascript -localCache: { -expire: 60 * 10 * 1000, -} -``` - -### absolute time - -With a specific time point as the expiration time, the cache will expire at the set time point - -```javascript -localCache: new Date('2030-01-01'); -``` - -```javascript -localCache: { - expire: new Date('2030-01-01'); -} -``` - -## Instruction for response automatic maintenance - -The key of the response data cache is uniquely identified by the combination of the request method (method), request address (url), request header parameters (headers), url parameters (params), and request body parameters (requestBody) of the method instance. Any information or Different positions will be treated as different keys. +--- +title: Cache Mode +--- + +import MemoryCache from '@site/example-links/MemoryCache'; +import StoragePlaceholder from '@site/example-links/StoragePlaceholder'; +import StorageRestore from '@site/example-links/StorageRestore'; + +The cache mode can be set at different granularities such as global or request level. When set globally, all Method instances created from the same alova instance will inherit the setting. + +:::info note + +Whether to use the cache mode and which cache mode to use depends on the scenario. The usage scenarios of different cache modes will be mentioned below when introducing different cache modes separately. + +::: + +## Memory mode (default) + +The memory mode puts the cache in the memory, which means that the page cache is invalidated when it is refreshed, and is the most commonly used cache mode. + +Memory mode is generally used to solve the performance consumption caused by frequent requests for the same data in a short period of time (minutes or seconds). For example, when you are writing a todo details page, you may think that users will frequently click on the todo list Check the details, if the user does not repeatedly request the interface when repeatedly viewing a certain detail, and can return the data immediately, the colleague who improves the response speed also reduces the pressure on the server. At this point we can set the response data cache for a todo detail `Method` instance. + +```javascript +alovaInstance.GET('/todo/list', { + //... + // highlight-start + localCache: { + // Set cache mode to memory mode + mode: 'memory', + + // unit is milliseconds + // When set to `Infinity`, it means that the data will never expire, and when it is set to 0 or a negative number, it means no caching + expire: 60 * 10 * 1000 + } + // highlight-end +}); +``` + +Memory mode is the default mode, you can abbreviate like this + +```javascript +alovaInstance.GET('/todo/list', { + //... + // highlight-start + localCache: 60 * 10 * 1000 + // highlight-end +}); +``` + +> GET requests will set the memory cache time of 300000ms (5 minutes) by default, and developers can also customize the settings. + +> If you need to set the caching mode globally, see [Global setting cache mode] at the bottom of this section (#Global setting cache mode) + +### Memory mode example + + + +## Cache placeholder mode + +This cache mode is used when you don't want to display the Loading icon every time the application is entered, but you want to use the old data instead, you can use the cache occupancy mode, which has a better experience than Loading. + +In the cache occupancy mode, `data` will be immediately assigned the old data of the last cache. You can judge that if there is old data, use it to replace the Loading display. At the same time, it will send a request to obtain the latest data and update the cache, so as to achieve In order to quickly display the actual data, and obtain the latest data. + +Set on `Method` instances: + +```javascript +const todoListGetter = alovaInstance.Get('/todo/list', { + //... + // highlight-start + localCache: { + // Set the cache mode to persistent placeholder mode + mode: 'placeholder', + // cache time + expire: 60 * 10 * 1000 + } + // highlight-end +}); +``` + +> If you need to set the caching mode globally, see [Global setting cache mode] at the bottom of this section (#Global setting cache mode) + +### Cache placeholder mode example + + + +## Restore mode + +In this mode, the server-side cached data will be persistent. If the expiration time is not reached, even if the page cache is refreshed, it will not be invalidated. It is generally used for some data that requires server-side management but is basically unchanged, such as the specific dates of annual holidays. It is different, but it will not change again. In this scenario, we only need to set the cache expiration time to the last moment of this year. + +Set on `Method` instances: + +```javascript +const todoListGetter = alovaInstance.Get('/todo/list', { + //... + // highlight-start + localCache: { + // Set the cache mode to persistent mode + mode: 'restore', + // cache time + expire: 60 * 10 * 1000 + } + // highlight-end +}); +``` + +:::warning Caution + +When request body is special data such as **FormData**, **Blob**, **ArrayBuffer**, **URLSearchParams**, **ReadableStream**, it will be considered that you intend to communicate with server. In this case would not cache data. + +::: + +> If you need to set the caching mode globally, see [Global setting cache mode] at the bottom of this section (#Global setting cache mode) + +### Restore mode example + + + +### What should I do if the data changes in restore mode? + +When the `Method` instance in restore mode is set, it may be due to the change of the interface data or the logic change of the front-end processing response data. At this time, it is necessary to let the user re-cache the changed data after publishing the application. At this time, you can use `tag` The attribute sets the cache tag. Each piece of persistent data contains a `tag` identifier. When the `tag` changes, the original persistent data will become invalid, and new data will be obtained again, and the new `tag` will be used for identification . + +```javascript +const todoListGetter = alovaInstance.Get('/todo/list', { + //... + localCache: { + mode: 'restore', + expire: 60 * 10 * 1000, + + // highlight-start + // Add or modify the tag parameter, the cached data will be invalid + // It is recommended to use version number management + tag: 'v1' + // highlight-end + } +}); +``` + +## Global setting cache mode + +:::info version required +v1.3.0+ +::: +The above settings are all set separately on `Method`. If you need to set the cache mode globally, you can do it as follows: + +```javascript +const alovaInstance = createAlova({ + //... + // highlight-start + localCache: { + // Uniformly set the cache mode of POST + POST: { + mode: 'placeholder', + expire: 60 * 10 * 1000 + }, + // Uniformly set the cache mode of the HEAD request + HEAD: 60 * 10 * 1000 + } + // highlight-end +}); +``` + +Henceforth, the `Method` instance created by `alovaInstance` instance will use this cache setting by default, and it can also be overridden in the `Method` instance. + +> Note: When the cache mode is set globally, the original 5-minute GET cache mode will be overwritten. + +## Disable caching mode globally + +If you don't want to use any request cache in your project, you can turn it off globally. If you want to use it only in a few specific requests, you can also turn it off globally and set it in the specified `Method` instance . + +```javascript +const alovaInstance = createAlova({ + //... + // highlight-start + // Set to null to disable all request caching globally + localCache: null + // highlight-end +}); +``` + +## Expiration time type + +There are two types of expiration time to choose from, namely **relative time** and **absolute time** + +### Relative Time + +That is, the time to expire when the cached data is saved, in **milliseconds**, the above examples are all of this type. + +```javascript +localCache: 60 * 10 * 1000; +``` + +```javascript +localCache: { +expire: 60 * 10 * 1000, +} +``` + +### absolute time + +With a specific time point as the expiration time, the cache will expire at the set time point + +```javascript +localCache: new Date('2030-01-01'); +``` + +```javascript +localCache: { + expire: new Date('2030-01-01'); +} +``` + +## Instruction for response automatic maintenance + +The key of the response data cache is uniquely identified by the combination of the request method (method), request address (url), request header parameters (headers), url parameters (params), and request body parameters (requestBody) of the method instance. Any information or Different positions will be treated as different keys. diff --git a/versioned_docs/version-2.x/tutorial/04-cache/02-auto-invalidate.md b/versioned_docs/version-2.x/tutorial/04-cache/02-auto-invalidate.md index 9bbdfe62c..a9b8fbeb8 100644 --- a/versioned_docs/version-2.x/tutorial/04-cache/02-auto-invalidate.md +++ b/versioned_docs/version-2.x/tutorial/04-cache/02-auto-invalidate.md @@ -1,91 +1,90 @@ ---- -title: Auto Invalidate -sidebar_position: 20 ---- - -There is a scenario where when the user clicks on an item in the todo list, enters the todo details page and edits it, at this time we hope that the todo list data on the previous page will also be updated with the edited content. Usually The approach is to trigger content updates on the previous page through events, which increases maintenance costs. And `alova` provides 3 ways to achieve this goal very elegantly: - -1. Use `useFetcher` to immediately re-request the latest data, which will be explained in the [Data Fetching](/tutorial/advanced/use-fetcher) chapter; -2. Update the cache. This method will be explained in detail in the [Cache set and query](/tutorial/cache/set-and-query) chapter later; -3. Invalidate the response cache. When requested again, the data will be requested again due to cache invalidation. This is also what this chapter will explain. - -Automatic cache invalidation is to set invalidation source rules in the target cache. As long as the rules match, the target cache can be automatically invalidated. This saves the trouble of manually clearing the cache in many cases. - -## scenes to be used - -Setting up automatic invalidation rules is convenient when the target cache is one-to-one or one-to-many with the invalidation source. - -```mermaid -flowchart - M1[method1 invalidation source points to] --> T1[target cache] - M11[method1 invalidation source points to] --> T2[target cache] - M2[method2 invalidation source points to] --> T2[target cache] - MN[methodN invalidation source points to] --> T2[target cache] -``` - -## Set automatic invalidation rules - -Setting this rule is very simple, you can set the `hitSource` parameter for it when creating a Method instance with caching. - -### Invalidation source is set to method instance - -With a fixed method instance as the invalidation source, as long as the method instance or its clone instance succeeds, the target cache will be cleared automatically. - -```javascript -alova.Get('/todo/1', { - //... - hitSource: alova.Post('/todo', {}) -}); -``` - -### Match invalidating source by method name - -Like the method instance matcher, you can specify the name of the method in hitSource to match the failure source. Multiple failure sources can be set to the same name. When the method instance request with this name succeeds, the target cache will be automatically cleared. - -```javascript -const methodSubmitTodo = data => - alova.Post('/todo', data, { - name: 'submitTodo' - }); - -alova.Get('/todo/1', { - //... - // Match the failure source whose method instance name is submitTodo - hitSource: 'submitTodo' -}); -``` - -### Match invalidating source by regexp of method name - -If the method instance name is not fixed, you can specify a regular expression in hitSource to match the method name, and the target cache will be automatically cleared when the matched method instance succeeds in the request. - -```javascript -const methodSubmitTodo = data => - alova.Post('/todo', data, { - name: 'prefix-submitTodo' - }); - -alova.Get('/todo/1', { - //... - // Match all instances whose method instance name starts with prefix - hitSource: /^prefix/ -}); -``` - -### Combination setting validating source - -If you want to use the above multiple rules to match failure sources, you can specify hitSource as an array, and the array item is any one of the above three rules, and the method instance that meets any one of the rules in the array will be matched. - -```javascript -alova.Get('/todo/1', { - //... - // When the method instance request that satisfies any matching rule in the array succeeds, the cache will be invalidated - hitSource: [alova.Post('/todo', {}), 'submitTodo', /^prefix/] -}); -``` - -## hitSource data type - -```typescript -type hitSource = string | RegExp | Method | (string | RegExp | Method)[]; -``` +--- +title: Auto Invalidate +--- + +There is a scenario where when the user clicks on an item in the todo list, enters the todo details page and edits it, at this time we hope that the todo list data on the previous page will also be updated with the edited content. Usually The approach is to trigger content updates on the previous page through events, which increases maintenance costs. And `alova` provides 3 ways to achieve this goal very elegantly: + +1. Use `useFetcher` to immediately re-request the latest data, which will be explained in the [Data Fetching](/tutorial/advanced/use-fetcher) chapter; +2. Update the cache. This method will be explained in detail in the [Cache set and query](/tutorial/cache/set-and-query) chapter later; +3. Invalidate the response cache. When requested again, the data will be requested again due to cache invalidation. This is also what this chapter will explain. + +Automatic cache invalidation is to set invalidation source rules in the target cache. As long as the rules match, the target cache can be automatically invalidated. This saves the trouble of manually clearing the cache in many cases. + +## scenes to be used + +Setting up automatic invalidation rules is convenient when the target cache is one-to-one or one-to-many with the invalidation source. + +```mermaid +flowchart + M1[method1 invalidation source points to] --> T1[target cache] + M11[method1 invalidation source points to] --> T2[target cache] + M2[method2 invalidation source points to] --> T2[target cache] + MN[methodN invalidation source points to] --> T2[target cache] +``` + +## Set automatic invalidation rules + +Setting this rule is very simple, you can set the `hitSource` parameter for it when creating a Method instance with caching. + +### Invalidation source is set to method instance + +With a fixed method instance as the invalidation source, as long as the method instance or its clone instance succeeds, the target cache will be cleared automatically. + +```javascript +alova.Get('/todo/1', { + //... + hitSource: alova.Post('/todo', {}) +}); +``` + +### Match invalidating source by method name + +Like the method instance matcher, you can specify the name of the method in hitSource to match the failure source. Multiple failure sources can be set to the same name. When the method instance request with this name succeeds, the target cache will be automatically cleared. + +```javascript +const methodSubmitTodo = data => + alova.Post('/todo', data, { + name: 'submitTodo' + }); + +alova.Get('/todo/1', { + //... + // Match the failure source whose method instance name is submitTodo + hitSource: 'submitTodo' +}); +``` + +### Match invalidating source by regexp of method name + +If the method instance name is not fixed, you can specify a regular expression in hitSource to match the method name, and the target cache will be automatically cleared when the matched method instance succeeds in the request. + +```javascript +const methodSubmitTodo = data => + alova.Post('/todo', data, { + name: 'prefix-submitTodo' + }); + +alova.Get('/todo/1', { + //... + // Match all instances whose method instance name starts with prefix + hitSource: /^prefix/ +}); +``` + +### Combination setting validating source + +If you want to use the above multiple rules to match failure sources, you can specify hitSource as an array, and the array item is any one of the above three rules, and the method instance that meets any one of the rules in the array will be matched. + +```javascript +alova.Get('/todo/1', { + //... + // When the method instance request that satisfies any matching rule in the array succeeds, the cache will be invalidated + hitSource: [alova.Post('/todo', {}), 'submitTodo', /^prefix/] +}); +``` + +## hitSource data type + +```typescript +type hitSource = string | RegExp | Method | (string | RegExp | Method)[]; +``` diff --git a/versioned_docs/version-2.x/tutorial/04-cache/03-manually-invalidate.md b/versioned_docs/version-2.x/tutorial/04-cache/03-manually-invalidate.md index bd8dd546b..1dbaa8199 100644 --- a/versioned_docs/version-2.x/tutorial/04-cache/03-manually-invalidate.md +++ b/versioned_docs/version-2.x/tutorial/04-cache/03-manually-invalidate.md @@ -1,102 +1,101 @@ ---- -title: Manually Invalidate -sidebar_position: 30 ---- - -Generally, automatic invalidate cache is more concise, and it is recommended to use it first to invalidate the cache. When the automatic invalidate cache does not meet the needs, you can also invalidate the cache by calling `invalidateCache`. - -## Use method instance invalidate cache - -Pass in a method instance in the `invalidateCache` function, it will look for the cache under this instance to invalidate it. - -In the following example, when the submission is successful, the todo details data cache will be invalidated. - -```javascript -// Get the todo details data with id 1 -const getTodoDetail = id => - alovaInstance.Get(`/todo/${id}`, { - localCache: 1000000 - }); -const { loading, data } = useRequest(getTodoDetail(1)); -``` - -```javascript -// Submit the data and invalidate the todo details with id 1. -const { - // ... - send, - onSuccess -} = useRequest(createTodoPoster, { immediate: false }); - -// highlight-start -//Invalid cache after successful submission -onSuccess(() => { - invalidateCache(getTodoDetail(1)); -}); -// highlight-end - -const handleSubmit = () => { - send({ - title: 'new todo', - content: 'new todo content' - }); -}; -``` - -## Batch invalidate cache - -In the following example, we invalidate caches in batches by specifying the name of the cache or a regular expression of the name. - -```javascript -//The cache of the method named todoList will be invalidated -invalidateCache('todoList'); - -// The cache of methods whose names match the following regular expression will be invalidated -invalidateCache(/^todoList/); -``` - -## Dynamic invalidate cache - -Maybe sometimes you are not sure which cached data needs to be invalidated. We can use [Method instance matcher](/tutorial/advanced/method-matcher) to dynamically find the corresponding method instance. The following example shows how to invalidate the cache of the first 5 method instances named todoList. - -```javascript -const getTodoList = currentPage => { - return alovaInstance.Get('/todo/list', { - // highlight-start - // First set the name for the method instance, which is used to filter out the required Method instance when the Method instance cannot be specified directly. - name: 'todoList', - // highlight-end - params: { - currentPage, - pageSize: 10 - } - }); -}; - -const { - // ... - send, - onSuccess -} = useRequest(createTodoPoster, { immediate: false }); -// After successful submission, the todo data cache of the first page will be invalidated. -onSuccess(() => { - // highlight-start - //The cache of the first 5 Method instances whose invalid name is todoList - invalidateCache({ - name: 'todoList', - filter: (method, index, ary) => { - return index < 5; - } - }); - // highlight-end -}); -``` - -> For more methods of using method instance matcher, see [Method instance matcher](/tutorial/advanced/method-matcher) - -## Invalidate all caches - -```javascript -// When no parameters are passed, all response caches are invalidated -invalidateCache(); -``` +--- +title: Manually Invalidate +--- + +Generally, automatic invalidate cache is more concise, and it is recommended to use it first to invalidate the cache. When the automatic invalidate cache does not meet the needs, you can also invalidate the cache by calling `invalidateCache`. + +## Use method instance invalidate cache + +Pass in a method instance in the `invalidateCache` function, it will look for the cache under this instance to invalidate it. + +In the following example, when the submission is successful, the todo details data cache will be invalidated. + +```javascript +// Get the todo details data with id 1 +const getTodoDetail = id => + alovaInstance.Get(`/todo/${id}`, { + localCache: 1000000 + }); +const { loading, data } = useRequest(getTodoDetail(1)); +``` + +```javascript +// Submit the data and invalidate the todo details with id 1. +const { + // ... + send, + onSuccess +} = useRequest(createTodoPoster, { immediate: false }); + +// highlight-start +//Invalid cache after successful submission +onSuccess(() => { + invalidateCache(getTodoDetail(1)); +}); +// highlight-end + +const handleSubmit = () => { + send({ + title: 'new todo', + content: 'new todo content' + }); +}; +``` + +## Batch invalidate cache + +In the following example, we invalidate caches in batches by specifying the name of the cache or a regular expression of the name. + +```javascript +//The cache of the method named todoList will be invalidated +invalidateCache('todoList'); + +// The cache of methods whose names match the following regular expression will be invalidated +invalidateCache(/^todoList/); +``` + +## Dynamic invalidate cache + +Maybe sometimes you are not sure which cached data needs to be invalidated. We can use [Method instance matcher](/tutorial/advanced/method-matcher) to dynamically find the corresponding method instance. The following example shows how to invalidate the cache of the first 5 method instances named todoList. + +```javascript +const getTodoList = currentPage => { + return alovaInstance.Get('/todo/list', { + // highlight-start + // First set the name for the method instance, which is used to filter out the required Method instance when the Method instance cannot be specified directly. + name: 'todoList', + // highlight-end + params: { + currentPage, + pageSize: 10 + } + }); +}; + +const { + // ... + send, + onSuccess +} = useRequest(createTodoPoster, { immediate: false }); +// After successful submission, the todo data cache of the first page will be invalidated. +onSuccess(() => { + // highlight-start + //The cache of the first 5 Method instances whose invalid name is todoList + invalidateCache({ + name: 'todoList', + filter: (method, index, ary) => { + return index < 5; + } + }); + // highlight-end +}); +``` + +> For more methods of using method instance matcher, see [Method instance matcher](/tutorial/advanced/method-matcher) + +## Invalidate all caches + +```javascript +// When no parameters are passed, all response caches are invalidated +invalidateCache(); +``` diff --git a/versioned_docs/version-2.x/tutorial/04-cache/04-force-request.md b/versioned_docs/version-2.x/tutorial/04-cache/04-force-request.md index 3337a47a8..4c5a75861 100644 --- a/versioned_docs/version-2.x/tutorial/04-cache/04-force-request.md +++ b/versioned_docs/version-2.x/tutorial/04-cache/04-force-request.md @@ -1,75 +1,74 @@ ---- -title: Force Request -sidebar_position: 40 ---- - -Forced request refers to a mechanism that bypasses the cache check and triggers a request. It is useful when the latest data needs to be obtained under certain conditions. - -## Force request with method - -Force request by calling the send function of the method instance, and passing `true`. - -```javascript -const response = await alovaInstance.Get('/api/user').send(true); -``` - -## Force request in useHook - -Among the three core hooks of `useRequest/useWatcher/useFetcher`, force request parameters are supported. - -```javascript -// useRequest -useRequest(todoListGetter, { - // highlight-start - force: true - // highlight-end -}); - -// useWatcher -useWatcher(todoListGetter, [page], { - // highlight-start - force: true - // highlight-end -}); - -// useFetcher -useFetcher({ - // highlight-start - force: true - // highlight-end -}); -``` - -### Dynamically set force value - -In actual situations, we often need to set whether to force the request to be sent based on different situations. In this case, force can be set to a function, which will also receive parameters passed in from the `send` function. Please read the [receive params](/tutorial/combine-framework/receive-params) for details - -```javascript -useRequest(todoListGetter, { - // highlight-start - force: isForce => { - return isForce; - } - // highlight-end -}); - -// useWatcher -useWatcher(todoListGetter, [page], { - // highlight-start - force: isForce => { - return isForce; - } - // highlight-end -}); - -// useFetcher -useFetcher({ - // highlight-start - force: isForce => { - return isForce; - } - // highlight-end -}); -``` - -`useFetcher` is a useHook for data fetching, which will be discussed in the [Data Fetching](/tutorial/advanced/use-fetcher) chapter later. +--- +title: Force Request +--- + +Forced request refers to a mechanism that bypasses the cache check and triggers a request. It is useful when the latest data needs to be obtained under certain conditions. + +## Force request with method + +Force request by calling the send function of the method instance, and passing `true`. + +```javascript +const response = await alovaInstance.Get('/api/user').send(true); +``` + +## Force request in useHook + +Among the three core hooks of `useRequest/useWatcher/useFetcher`, force request parameters are supported. + +```javascript +// useRequest +useRequest(todoListGetter, { + // highlight-start + force: true + // highlight-end +}); + +// useWatcher +useWatcher(todoListGetter, [page], { + // highlight-start + force: true + // highlight-end +}); + +// useFetcher +useFetcher({ + // highlight-start + force: true + // highlight-end +}); +``` + +### Dynamically set force value + +In actual situations, we often need to set whether to force the request to be sent based on different situations. In this case, force can be set to a function, which will also receive parameters passed in from the `send` function. Please read the [receive params](/tutorial/combine-framework/receive-params) for details + +```javascript +useRequest(todoListGetter, { + // highlight-start + force: isForce => { + return isForce; + } + // highlight-end +}); + +// useWatcher +useWatcher(todoListGetter, [page], { + // highlight-start + force: isForce => { + return isForce; + } + // highlight-end +}); + +// useFetcher +useFetcher({ + // highlight-start + force: isForce => { + return isForce; + } + // highlight-end +}); +``` + +`useFetcher` is a useHook for data fetching, which will be discussed in the [Data Fetching](/tutorial/advanced/use-fetcher) chapter later. diff --git a/versioned_docs/version-2.x/tutorial/04-cache/05-set-and-query.md b/versioned_docs/version-2.x/tutorial/04-cache/05-set-and-query.md index c11798832..bfaf6d68a 100644 --- a/versioned_docs/version-2.x/tutorial/04-cache/05-set-and-query.md +++ b/versioned_docs/version-2.x/tutorial/04-cache/05-set-and-query.md @@ -1,284 +1,295 @@ ---- -title: Set & Query Cache -sidebar_position: 50 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -The cache also supports updating and querying, As we mentioned in [cache mode](/tutorial/cache/mode), each cached data is saved with the method instance that sends the request as the key, so the method instance will also be used when updating the cache manually to find the corresponding cached data. - -## Update static cache data - - - - -```html - - -``` - - - - -```jsx -import { setCache } from 'alova'; -import { useState } from 'react'; - -const getTodoListByDate = dateList => - alovaInstance.Get('/todo/list/dates', { - params: { dateList } - }); - -const App = () => { - // Get 5 days of data in batches during initialization - const [dates, setDates] = useState(['2022-05-01', '2022-05-02', '2022-05-03', '2022-05-04', '2022-05-05']); - const { - //... - onSuccess - } = useWatcher(() => getTodoListByDate(dates.join()), [dates], { - immediate: true - }); - onSuccess(todoListDates => { - if (todoListDates.length <= 1) { - return; - } - - // highlight-start - // By default, the data of these 5 days will be cached together in a key - // In order to make subsequent requests for data of a certain day also hit the cache, we can disassemble the data of 5 days into days, and manually set the response cache one by one through setCache - // The first parameter of setCache is the method instance object, which is used to specify the key of the cache - // The second parameter is the cached data - todoListDates.forEach(todoDate => { - setCache(getTodoListByDate(todoDate.date), [todoDate]); - }); - // highlight-end - }); - - // highlight-start - const handleTodolistToggle = () => { - // At this time, when the switching date is May 1, it will hit the response cache we manually set. - // The dates value is being monitored by useWatcher, so changing it can automatically trigger the request - setDates(['2022-05-01']); - }; - // highlight-end - - return ; -}; -``` - - - - -```html - - -``` - - - - -```html - - -``` - - - - -## Dynamically set cache data - -You can also pass in a callback function in `setCache` to dynamically calculate the cache data and return the cache data that needs to be updated. - -```javascript -setCache(getTodoListByDate('2022-10-01'), oldCache => { - // Return the data that needs to be cached - return { - ...oldCache, - expire: isAfter('2022-10-01', new Date()) - }; -}); -``` - -Similarly, you can also dynamically find method instances through [method instance matcher](/tutorial/advanced/method-matcher). - -```javascript -setCache( - { - name: 'todoList', - filter: (method, index, ary) => { - return index < 5; - } - }, - 'newCache' -); -``` - -## Abort to set cache - -Sometimes you need to dynamically determine whether to update the cache. If no data is returned in the callback function of `setCache`, or `undefined` is returned, the original cache data will not be updated at this time - -```javascript -setCache(getTodoListByDate('2022-10-01'), oldCache => { - const isExpired = isAfter('2022-10-01', new Date()); - if (!isExpired) { - return; // abort cache updating when return the undefined - } - return null; // update the cache to null -}); -``` - -## Cache query - -At the same time, we also provide a cache query method. - -```javascript -import { queryCache } from 'alova'; - -const cacheData = queryCache(getTodoListByDate('2022-10-01')); -``` - -You can also dynamically find method instances via [method instance matcher](/tutorial/advanced/method-matcher). - -```javascript -const cacheData = queryCache({ - name: 'todoList', - filter: (method, index, ary) => { - return index < 5; - } -}); -``` +--- +title: Set & Query Cache +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +The cache also supports updating and querying, As we mentioned in [cache mode](/tutorial/cache/mode), each cached data is saved with the method instance that sends the request as the key, so the method instance will also be used when updating the cache manually to find the corresponding cached data. + +## Update static cache data + + + + +```html + + +``` + + + + +```jsx +import { setCache } from 'alova'; +import { useState } from 'react'; + +const getTodoListByDate = dateList => + alovaInstance.Get('/todo/list/dates', { + params: { dateList } + }); + +const App = () => { + // Get 5 days of data in batches during initialization + const [dates, setDates] = useState([ + '2022-05-01', + '2022-05-02', + '2022-05-03', + '2022-05-04', + '2022-05-05' + ]); + const { + //... + onSuccess + } = useWatcher(() => getTodoListByDate(dates.join()), [dates], { + immediate: true + }); + onSuccess(todoListDates => { + if (todoListDates.length <= 1) { + return; + } + + // highlight-start + // By default, the data of these 5 days will be cached together in a key + // In order to make subsequent requests for data of a certain day also hit the cache, we can disassemble the data of 5 days into days, and manually set the response cache one by one through setCache + // The first parameter of setCache is the method instance object, which is used to specify the key of the cache + // The second parameter is the cached data + todoListDates.forEach(todoDate => { + setCache(getTodoListByDate(todoDate.date), [todoDate]); + }); + // highlight-end + }); + + // highlight-start + const handleTodolistToggle = () => { + // At this time, when the switching date is May 1, it will hit the response cache we manually set. + // The dates value is being monitored by useWatcher, so changing it can automatically trigger the request + setDates(['2022-05-01']); + }; + // highlight-end + + return ; +}; +``` + + + + +```html + + +``` + + + + +```html + + +``` + + + + +## Dynamically set cache data + +You can also pass in a callback function in `setCache` to dynamically calculate the cache data and return the cache data that needs to be updated. + +```javascript +setCache(getTodoListByDate('2022-10-01'), oldCache => { + // Return the data that needs to be cached + return { + ...oldCache, + expire: isAfter('2022-10-01', new Date()) + }; +}); +``` + +Similarly, you can also dynamically find method instances through [method instance matcher](/tutorial/advanced/method-matcher). + +```javascript +setCache( + { + name: 'todoList', + filter: (method, index, ary) => { + return index < 5; + } + }, + 'newCache' +); +``` + +## Abort to set cache + +Sometimes you need to dynamically determine whether to update the cache. If no data is returned in the callback function of `setCache`, or `undefined` is returned, the original cache data will not be updated at this time + +```javascript +setCache(getTodoListByDate('2022-10-01'), oldCache => { + const isExpired = isAfter('2022-10-01', new Date()); + if (!isExpired) { + return; // abort cache updating when return the undefined + } + return null; // update the cache to null +}); +``` + +## Cache query + +At the same time, we also provide a cache query method. + +```javascript +import { queryCache } from 'alova'; + +const cacheData = queryCache(getTodoListByDate('2022-10-01')); +``` + +You can also dynamically find method instances via [method instance matcher](/tutorial/advanced/method-matcher). + +```javascript +const cacheData = queryCache({ + name: 'todoList', + filter: (method, index, ary) => { + return index < 5; + } +}); +``` diff --git a/versioned_docs/version-2.x/tutorial/04-cache/06-controlled-cache.md b/versioned_docs/version-2.x/tutorial/04-cache/06-controlled-cache.md index 89dec2a39..62e915556 100644 --- a/versioned_docs/version-2.x/tutorial/04-cache/06-controlled-cache.md +++ b/versioned_docs/version-2.x/tutorial/04-cache/06-controlled-cache.md @@ -1,83 +1,82 @@ ---- -title: Controlled Cache -sidebar_position: 60 ---- - -:::info version requirements - -v2.1.0+ - -::: - -When sending a request, by default it will first check whether there is matching cache data. If it matches, it will use it as the response data to return. If in some scenarios, the user needs to use a custom cache, they must first use `setCache` to synchronize Setting up cached data can only work, which undoubtedly increases the burden on users. This is an uncontrolled cache. - -If you want to use **IndexedDB** to custom manage cached data under uncontrolled caching, you may first pre-set the hit cache for the upcoming request, like this: - -```javascript -const getFile = fileName => { - const fileGetter = alovaInstance.GET(`/file/${fileName}`); - const tx = db.transaction(['files']); - const getRequest = tx.objectStore('files').get(fileName); - getRequest.onsuccess = ({ result }) => { - setCache(fileGetter, result); - }; - return fileGetter; -}; -``` - -**❌ However, the above writing method is not recommended** for the following reasons: - -1. Each time `getFile` is called, a cache will be set, but fileGetter is not necessarily used to send requests; -2. IndexedDB is an asynchronous interface. If the cache matching step occurs before IndexedDB triggers onsuccess, then the cached data will not be matched, and their order is unpredictable; -3. Custom cache management tasks and methods are separated, but in fact they should be brought together; - -In this case, you can use controlled caching to solve the above problem. Using controlled caching is also very simple. You can set the localCache in the method to an asynchronous or synchronous function, and return custom data as a hit response. - -```javascript -const getFile = fileName => - alovaInstance.GET(`/file/${fileName}`, { - // Controlled cache functions support asynchronous and synchronous functions - localCache() { - return new Promise((resolve, reject) => { - const tx = db.transaction(['files']); - const getRequest = tx.objectStore('files').get(fileName); - getRequest.onsuccess = resolve; - getRequest.onerror = reject; - }); - } - }); -``` - -## Do not use caching - -If you wish to continue sending the request, you can return `undefined` or `void` in `localCache`, which is useful in the case of cache misses when customizing the cache. - -## Use transformData to set up cache - -Because the `transformData` function has the following two properties: - -- It is only triggered when responding, but will not be triggered when the response cache is hit; -- Support asynchronous functions; - -Therefore, you can also use it to save customized caches. For example, in a cache scenario where files are used as response data, you can use IndexedDB to cache file data. - -```javascript -const fileGetter = alovaInstance.Get('/file/file_name', { - // Use IndexedDB to cache files - async transformData(fileBlob) { - await new Promise((resolve, reject) => { - const tx = db.transaction(['files'], 'readwrite'); - const putRequest = tx.objectStore('files').put({ - file: fileBlob - }); - putRequest.onsuccess = resolve; - putRequest.onerror = reject; - }); - return fileBlob; - } -}); -``` - -## Notes - -When used in usehooks, throwing an error in the `localCache` function will trigger `onError`. When a request is made directly using a method instance, a promise instance with a reject status will be returned. +--- +title: Controlled Cache +--- + +:::info version requirements + +v2.1.0+ + +::: + +When sending a request, by default it will first check whether there is matching cache data. If it matches, it will use it as the response data to return. If in some scenarios, the user needs to use a custom cache, they must first use `setCache` to synchronize Setting up cached data can only work, which undoubtedly increases the burden on users. This is an uncontrolled cache. + +If you want to use **IndexedDB** to custom manage cached data under uncontrolled caching, you may first pre-set the hit cache for the upcoming request, like this: + +```javascript +const getFile = fileName => { + const fileGetter = alovaInstance.GET(`/file/${fileName}`); + const tx = db.transaction(['files']); + const getRequest = tx.objectStore('files').get(fileName); + getRequest.onsuccess = ({ result }) => { + setCache(fileGetter, result); + }; + return fileGetter; +}; +``` + +**❌ However, the above writing method is not recommended** for the following reasons: + +1. Each time `getFile` is called, a cache will be set, but fileGetter is not necessarily used to send requests; +2. IndexedDB is an asynchronous interface. If the cache matching step occurs before IndexedDB triggers onsuccess, then the cached data will not be matched, and their order is unpredictable; +3. Custom cache management tasks and methods are separated, but in fact they should be brought together; + +In this case, you can use controlled caching to solve the above problem. Using controlled caching is also very simple. You can set the localCache in the method to an asynchronous or synchronous function, and return custom data as a hit response. + +```javascript +const getFile = fileName => + alovaInstance.GET(`/file/${fileName}`, { + // Controlled cache functions support asynchronous and synchronous functions + localCache() { + return new Promise((resolve, reject) => { + const tx = db.transaction(['files']); + const getRequest = tx.objectStore('files').get(fileName); + getRequest.onsuccess = resolve; + getRequest.onerror = reject; + }); + } + }); +``` + +## Do not use caching + +If you wish to continue sending the request, you can return `undefined` or `void` in `localCache`, which is useful in the case of cache misses when customizing the cache. + +## Use transformData to set up cache + +Because the `transformData` function has the following two properties: + +- It is only triggered when responding, but will not be triggered when the response cache is hit; +- Support asynchronous functions; + +Therefore, you can also use it to save customized caches. For example, in a cache scenario where files are used as response data, you can use IndexedDB to cache file data. + +```javascript +const fileGetter = alovaInstance.Get('/file/file_name', { + // Use IndexedDB to cache files + async transformData(fileBlob) { + await new Promise((resolve, reject) => { + const tx = db.transaction(['files'], 'readwrite'); + const putRequest = tx.objectStore('files').put({ + file: fileBlob + }); + putRequest.onsuccess = resolve; + putRequest.onerror = reject; + }); + return fileBlob; + } +}); +``` + +## Notes + +When used in usehooks, throwing an error in the `localCache` function will trigger `onError`. When a request is made directly using a method instance, a promise instance with a reject status will be returned. diff --git a/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/02-virtual-data.md b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/02-virtual-data.md index ee27ca5d8..e14e5bd00 100644 --- a/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/02-virtual-data.md +++ b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/02-virtual-data.md @@ -1,177 +1,176 @@ ---- -title: Virtual data -sidebar_position: 20 ---- - -In fact, virtual data is a reference object with a unique id, and its tracking mechanism is realized by first generating a mapping between virtual data id and response data, and then finding and replacing it with the actual value through virtual data id. - -When the original value is a reference type, the performance is the same as the original value, but the virtual data of the basic type uses `Number, String, Boolean` encapsulation classes, as well as custom `Undefined, Null` encapsulation classes, and their expressions are the same as There are some deviations from the original value. The characteristics of the virtual data and the use of auxiliary functions for the virtual data are listed below. The details of the auxiliary functions will be introduced at the end of the chapter. - -## string concatenation - -When virtual data is concatenated, it will be converted to virtual data id for splicing. - -```javascript -const virtualData = createVirtualData(1); -'a' + virtualData; // a[vd:xxxxxx] -1 + virtualData; // 1[vd:xxxxxx] -``` - -## Data Comparison - -Virtual data cannot be directly used for comparison, but virtual data and actual data are often mixed and compared in actual scenarios. In this case, `equals` can be used for comparison. - -```javascript -import { equals } from '@alova/scene-*'; - -equals('a', 'a'); // true - -const virtualData1 = createVirtualData(1); -const virtualData2 = virtualData1.clone(); // clone virtual data -equals(virtualData1, virtualData2); // true when the ids of virtualData1 and virtualData2 are the same -equals(virtualData1, '[vd:xxxxxx]'); // true when the id of virtualData1 is also [vd:xxxxxx] -``` - -## Participate in operations - -When participating in operations such as `+-*/%`, numerical comparison, and bit operations, it cannot be automatically converted to the original value. It can be converted to the original value through `dehydrateVData` and then calculated. - -```javascript -import { dehydrateVData } from '@alova/scene-*'; - -const virtualData = createVirtualData(1); -dehydrateVData(virtualData) + 1; // 2 -dehydrateVData(virtualData) > 0; // true -``` - -## type operator - -Because the virtual data is implemented using the encapsulation class on the basic data type, `object` will always be returned when using `typeof` to get the type, and it can also be converted to the original value by `dehydrateVData` to get the type - -```javascript -const vNum = createVirtualResponse(1); -typeof vNum === 'object'; // true -const vUndef = createVirtualResponse(undefined); -typeof vUndef === 'object'; // true - -typeof dehydrateVData(vNum) === 'number'; // true -typeof dehydrateVData(vUndef) === 'undefined'; // true -``` - -To solve this problem, you can use the virtual data helper function **dehydrateVData**, which can get the original value of a virtual data, and return it unchanged when encountering non-virtual data - -```javascript -const vNum = createVirtualResponse(1); -typeof dehydrateVData(vNum) === 'number'; // true -dehydrateVData(vNum) === 1; // true -dehydrateVData('string') === 'string'; // true -``` - -## View display - -By default, `toString` will be called implicitly when the virtual data is displayed in the view, but sometimes you will encounter problems with display confusion, and rendering an object in **react** will report the following error: - -``` -Uncaught Error: Objects are not valid as a React child (found: object with keys {}). If you meant to render a collection of children, use an array instead. -``` - -Therefore, it is recommended to use `dehydrateVData` to convert to raw data for display. - -## virtual data helper functions - -### dehydrateVData - -Dehydrate virtual data and return its original value, if target is non-virtual data, return it as it is. - -```typescript -// type -function dehydrateVData(target: any): any; - -// example -dehydrateVData(1); // 1 -const virtualData = createVirtualData(1); -dehydrateVData(virtualData); // 1 -``` - -### stringifyVData - -Stringify virtual data, return virtual data id, when returnOriginalIfNotVData is set to false, non-virtual data will be returned as-is. - -```typescript -// type -function stringifyVData(target: any, returnOriginalIfNotVData?: boolean): any; - -// example -stringifyVData(1); // 1 -stringifyVData(1, false); // undefined - -const virtualData = createVirtualData(1); -stringifyVData(virtualData); // [vd:xxxxxx] -``` - -### equals - -Judge whether two values are equal in a way that is compatible with virtual data. When there is no virtual data to participate in the comparison, it will be strictly compared. Otherwise, it will be compared whether the virtual data id is the same. If there may be virtual data involved in the comparison data, it is recommended to use this function for comparison. - -```typescript -// type -function equals(prevValue: any, nextValue: any): boolean; - -// example -equals('a', 'a'); // true -const virtualData1 = createVirtualData(1); -const virtualData2 = virtualData1.clone(); // clone virtual data -equals(virtualData1, virtualData2); // true when the ids of virtualData1 and virtualData2 are the same -equals(virtualData1, '[vd:xxxxxx]'); // true when the id of virtualData1 is also [vd:xxxxxx] -``` - -###isVData - -Determine whether the target data is virtual data - -```typescript -// type -function isVData(target: any): boolean; - -// example -isVData(1); // false -const virtualData = createVirtualData(1); -isVData(virtualData); // true -isVData('[vd:xxxxxx]'); //true -``` - -## Replacement restrictions for virtual data - -The tracing mechanism of virtual data can only deeply traverse relevant data, and then replace the data with virtual data identifiers with actual data. If some data is generated by virtual data, it will not be recalculated after virtual data is replaced with actual data. - -In the following cases, even if the virtualId is replaced with actual data, the id of the request will not be recalculated. Therefore, if replacement is required, the virtualId must be directly used as a request parameter. The example is as follows: - -```javascript -const deleteTodo = virtualId => { - return alova.Delete('/deleteTodo', { - id: dehydrateVData(virtualId) === null ? 1 : 2 - }); -}; -``` - -But if virtual data is concatenated as string, it will be automatically converted to virtual data id to participate in string concatenation, which will work. In the following cases, the value of the request id at the beginning is `id_[vd:xxxxxx]`, and when virtualId is replaced with the response value (assuming it is replaced with 1), it will be automatically updated to `id_1`. - -```javascript -const deleteTodo = virtualId => { - return alova.Delete('/deleteTodo', { - id: 'id'_virtualId - }); -}; -``` - -## Next - -Although it is simple enough to realize non-inductive interaction, there are still some additional processing compared with conservative requests. The specific implementation is roughly divided into the following steps. - -1. Implement functions in a conservative request manner; -2. Manually update the list data to realize localized data compensation; -3. Record data operations so that you can manually add to the latest records when there are unsubmitted modifications; -4. Manually compensate the unsubmitted data to the list, so that the latest status can be displayed even if the data is not submitted; -5. When modifying unsubmitted data, intercept requests with virtual data; - -Next, we will demonstrate with a simple example. +--- +title: Virtual data +--- + +In fact, virtual data is a reference object with a unique id, and its tracking mechanism is realized by first generating a mapping between virtual data id and response data, and then finding and replacing it with the actual value through virtual data id. + +When the original value is a reference type, the performance is the same as the original value, but the virtual data of the basic type uses `Number, String, Boolean` encapsulation classes, as well as custom `Undefined, Null` encapsulation classes, and their expressions are the same as There are some deviations from the original value. The characteristics of the virtual data and the use of auxiliary functions for the virtual data are listed below. The details of the auxiliary functions will be introduced at the end of the chapter. + +## string concatenation + +When virtual data is concatenated, it will be converted to virtual data id for splicing. + +```javascript +const virtualData = createVirtualData(1); +'a' + virtualData; // a[vd:xxxxxx] +1 + virtualData; // 1[vd:xxxxxx] +``` + +## Data Comparison + +Virtual data cannot be directly used for comparison, but virtual data and actual data are often mixed and compared in actual scenarios. In this case, `equals` can be used for comparison. + +```javascript +import { equals } from '@alova/scene-*'; + +equals('a', 'a'); // true + +const virtualData1 = createVirtualData(1); +const virtualData2 = virtualData1.clone(); // clone virtual data +equals(virtualData1, virtualData2); // true when the ids of virtualData1 and virtualData2 are the same +equals(virtualData1, '[vd:xxxxxx]'); // true when the id of virtualData1 is also [vd:xxxxxx] +``` + +## Participate in operations + +When participating in operations such as `+-*/%`, numerical comparison, and bit operations, it cannot be automatically converted to the original value. It can be converted to the original value through `dehydrateVData` and then calculated. + +```javascript +import { dehydrateVData } from '@alova/scene-*'; + +const virtualData = createVirtualData(1); +dehydrateVData(virtualData) + 1; // 2 +dehydrateVData(virtualData) > 0; // true +``` + +## type operator + +Because the virtual data is implemented using the encapsulation class on the basic data type, `object` will always be returned when using `typeof` to get the type, and it can also be converted to the original value by `dehydrateVData` to get the type + +```javascript +const vNum = createVirtualResponse(1); +typeof vNum === 'object'; // true +const vUndef = createVirtualResponse(undefined); +typeof vUndef === 'object'; // true + +typeof dehydrateVData(vNum) === 'number'; // true +typeof dehydrateVData(vUndef) === 'undefined'; // true +``` + +To solve this problem, you can use the virtual data helper function **dehydrateVData**, which can get the original value of a virtual data, and return it unchanged when encountering non-virtual data + +```javascript +const vNum = createVirtualResponse(1); +typeof dehydrateVData(vNum) === 'number'; // true +dehydrateVData(vNum) === 1; // true +dehydrateVData('string') === 'string'; // true +``` + +## View display + +By default, `toString` will be called implicitly when the virtual data is displayed in the view, but sometimes you will encounter problems with display confusion, and rendering an object in **react** will report the following error: + +``` +Uncaught Error: Objects are not valid as a React child (found: object with keys {}). If you meant to render a collection of children, use an array instead. +``` + +Therefore, it is recommended to use `dehydrateVData` to convert to raw data for display. + +## virtual data helper functions + +### dehydrateVData + +Dehydrate virtual data and return its original value, if target is non-virtual data, return it as it is. + +```typescript +// type +function dehydrateVData(target: any): any; + +// example +dehydrateVData(1); // 1 +const virtualData = createVirtualData(1); +dehydrateVData(virtualData); // 1 +``` + +### stringifyVData + +Stringify virtual data, return virtual data id, when returnOriginalIfNotVData is set to false, non-virtual data will be returned as-is. + +```typescript +// type +function stringifyVData(target: any, returnOriginalIfNotVData?: boolean): any; + +// example +stringifyVData(1); // 1 +stringifyVData(1, false); // undefined + +const virtualData = createVirtualData(1); +stringifyVData(virtualData); // [vd:xxxxxx] +``` + +### equals + +Judge whether two values are equal in a way that is compatible with virtual data. When there is no virtual data to participate in the comparison, it will be strictly compared. Otherwise, it will be compared whether the virtual data id is the same. If there may be virtual data involved in the comparison data, it is recommended to use this function for comparison. + +```typescript +// type +function equals(prevValue: any, nextValue: any): boolean; + +// example +equals('a', 'a'); // true +const virtualData1 = createVirtualData(1); +const virtualData2 = virtualData1.clone(); // clone virtual data +equals(virtualData1, virtualData2); // true when the ids of virtualData1 and virtualData2 are the same +equals(virtualData1, '[vd:xxxxxx]'); // true when the id of virtualData1 is also [vd:xxxxxx] +``` + +###isVData + +Determine whether the target data is virtual data + +```typescript +// type +function isVData(target: any): boolean; + +// example +isVData(1); // false +const virtualData = createVirtualData(1); +isVData(virtualData); // true +isVData('[vd:xxxxxx]'); //true +``` + +## Replacement restrictions for virtual data + +The tracing mechanism of virtual data can only deeply traverse relevant data, and then replace the data with virtual data identifiers with actual data. If some data is generated by virtual data, it will not be recalculated after virtual data is replaced with actual data. + +In the following cases, even if the virtualId is replaced with actual data, the id of the request will not be recalculated. Therefore, if replacement is required, the virtualId must be directly used as a request parameter. The example is as follows: + +```javascript +const deleteTodo = virtualId => { + return alova.Delete('/deleteTodo', { + id: dehydrateVData(virtualId) === null ? 1 : 2 + }); +}; +``` + +But if virtual data is concatenated as string, it will be automatically converted to virtual data id to participate in string concatenation, which will work. In the following cases, the value of the request id at the beginning is `id_[vd:xxxxxx]`, and when virtualId is replaced with the response value (assuming it is replaced with 1), it will be automatically updated to `id_1`. + +```javascript +const deleteTodo = virtualId => { + return alova.Delete('/deleteTodo', { + id: 'id'_virtualId + }); +}; +``` + +## Next + +Although it is simple enough to realize non-inductive interaction, there are still some additional processing compared with conservative requests. The specific implementation is roughly divided into the following steps. + +1. Implement functions in a conservative request manner; +2. Manually update the list data to realize localized data compensation; +3. Record data operations so that you can manually add to the latest records when there are unsubmitted modifications; +4. Manually compensate the unsubmitted data to the list, so that the latest status can be displayed even if the data is not submitted; +5. When modifying unsubmitted data, intercept requests with virtual data; + +Next, we will demonstrate with a simple example. diff --git a/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/03-start-silent-factory.md b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/03-start-silent-factory.md index 0e7c87be9..7d066c062 100644 --- a/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/03-start-silent-factory.md +++ b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/03-start-silent-factory.md @@ -1,107 +1,106 @@ ---- -title: Boot silent factory -sidebar_position: 30 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -We put all scene request strategies in the js package called `@alova/scene-*`, you need to install it before using it. - - - - -```bash -# npm -npm install @alova/scene-vue --save -#yarn -yarn add @alova/scene-vue - -``` - - - - -```bash -# npm -npm install @alova/scene-react --save -#yarn -yarn add @alova/scene-react - -``` - - - - - -```bash -# npm -npm install @alova/scene-svelte --save -#yarn -yarn add @alova/scene-svelte - -``` - - - - -The silent queue is not started by default, and we need to specify the startup parameters for initialization. In general, call `bootSilentFactory` in the entry file to initialize the silent factory, which will read unexecuted requests to the corresponding silent through the specified configuration items queues and start those queues. - - - - -```javascript -import { bootSilentFactory } from '@alova/scene-vue'; -import { alovaInst } from '@/api'; - -bootSilentFactory({ - // Specify the alova instance at startup to request information storage and request sending - alova: alovaInst, - - // Delay start time, in milliseconds, the default is 2000ms, see the follow-up instructions for details - delay: 1000 -}); -``` - - - - - -```javascript -import { bootSilentFactory } from '@alova/scene-react'; -import { alovaInst } from '@/api'; - -bootSilentFactory({ - // Specify the alova instance at startup to request information storage and request sending - alova: alovaInst, - - // Delay start time, in milliseconds, the default is 2000ms, see the follow-up instructions for details - delay: 1000 -}); -``` - - - - - -```javascript -import { bootSilentFactory } from '@alova/scene-svelte'; -import { alovaInst } from '@/api'; - -bootSilentFactory({ - // Specify the alova instance at startup to request information storage and request sending - alova: alovaInst, - - // Delay start time, in milliseconds, the default is 2000ms, see the follow-up instructions for details - delay: 1000 -}); -``` - - - - -:::warning `delay` parameter description - -In actual scenarios, when entering the current page, a request is also sent to load the page data. In order to ensure that the user can see the page data faster, the request to load the data needs to be forwarded to the beginning of the queue, otherwise it may cause the loading data to fail. The request is placed at the end of the queue. At this time, it is necessary to wait until all the previous requests are completed before loading the page data. This is obviously inappropriate. Therefore, by delaying initialization for a period of time, the request for loading data enters the queue first to achieve "queue jumping" effect, the specific delay time depends on the time required for page rendering. - -::: +--- +title: Boot silent factory +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +We put all scene request strategies in the js package called `@alova/scene-*`, you need to install it before using it. + + + + +```bash +# npm +npm install @alova/scene-vue --save +#yarn +yarn add @alova/scene-vue + +``` + + + + +```bash +# npm +npm install @alova/scene-react --save +#yarn +yarn add @alova/scene-react + +``` + + + + + +```bash +# npm +npm install @alova/scene-svelte --save +#yarn +yarn add @alova/scene-svelte + +``` + + + + +The silent queue is not started by default, and we need to specify the startup parameters for initialization. In general, call `bootSilentFactory` in the entry file to initialize the silent factory, which will read unexecuted requests to the corresponding silent through the specified configuration items queues and start those queues. + + + + +```javascript +import { bootSilentFactory } from '@alova/scene-vue'; +import { alovaInst } from '@/api'; + +bootSilentFactory({ + // Specify the alova instance at startup to request information storage and request sending + alova: alovaInst, + + // Delay start time, in milliseconds, the default is 2000ms, see the follow-up instructions for details + delay: 1000 +}); +``` + + + + + +```javascript +import { bootSilentFactory } from '@alova/scene-react'; +import { alovaInst } from '@/api'; + +bootSilentFactory({ + // Specify the alova instance at startup to request information storage and request sending + alova: alovaInst, + + // Delay start time, in milliseconds, the default is 2000ms, see the follow-up instructions for details + delay: 1000 +}); +``` + + + + + +```javascript +import { bootSilentFactory } from '@alova/scene-svelte'; +import { alovaInst } from '@/api'; + +bootSilentFactory({ + // Specify the alova instance at startup to request information storage and request sending + alova: alovaInst, + + // Delay start time, in milliseconds, the default is 2000ms, see the follow-up instructions for details + delay: 1000 +}); +``` + + + + +:::warning `delay` parameter description + +In actual scenarios, when entering the current page, a request is also sent to load the page data. In order to ensure that the user can see the page data faster, the request to load the data needs to be forwarded to the beginning of the queue, otherwise it may cause the loading data to fail. The request is placed at the end of the queue. At this time, it is necessary to wait until all the previous requests are completed before loading the page data. This is obviously inappropriate. Therefore, by delaying initialization for a period of time, the request for loading data enters the queue first to achieve "queue jumping" effect, the specific delay time depends on the time required for page rendering. + +::: diff --git a/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/04-conservative-request.md b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/04-conservative-request.md index a57c1774f..b6c811cf1 100644 --- a/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/04-conservative-request.md +++ b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/04-conservative-request.md @@ -1,136 +1,135 @@ ---- -title: Step 1 - Implement features with conservative requests -sidebar_position: 40 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -Take Todo management as an example to realize the creation, editing, deletion and other functions of Todo in the non-sense interaction mode, and the key code related to the request will be provided in the following chapters. - -> The [simple list page example](/tutorial/example/silent-submit-simple-list) here contains the complete code, you can enter the experience. - -In the `@alova/scene-*` js package, **useSQRequest** will be used to replace **useRequest** provided by alova, and then the most common conservative request mode will be implemented first, and then the process will be done step by step Interactive mode compatibility. - -## Create an alova instance and related methods - -```javascript title="api.js" -import { createAlova } from 'alova'; - -export const alovaInst = createAlova({ - /*...*/ -}); - -/** Load todo list */ -const todoList = () => alovaInst.Get('/todo'); - -/** Load todo details */ -const todoDetail = id => - alovaInst.Get('/todo', { - params: { id } - }); - -/** Create and edit todo items */ -const createOrEditTodo = (data, id) => - alovaInst.Post('/todo', { - data, - id - }); - -/** Delete the todo item */ -const deleteTodo = id => alovaInst.Delete('/todo', { id }); -``` - -## Start the silent factory - -```javascript title="main.js" -import { bootSilentFactory } from '@alova/scene-*'; -import { alovaInst } from './api.js'; - -bootSilentFactory({ - alova: alovaInst -}); -``` - -## Load the Todo list - -Load and display page data in the simplest way - -```javascript -import { useSQRequest } from '@alova/scene-vue'; -import { todoList } from './api.js'; -const { data, loading, error } = useSQRequest(todoList, { - initialData: [] -}); -``` - -## Enter the Todo creation/editing page - -When creating a todo item, the id is empty, and no request for obtaining details is sent. When editing a todo item, if the id has a value, the detailed data will be obtained. - -```javascript -import { useSQRequest } from '@alova/scene-*'; -import { todoDetail } from './api.js'; - -const id = /* todo id */; -const { loading, data } = useSQRequest(() => todoDetail(id), { - initialData: { - title: '', - time: new Date() - }, - immediate: !!id -}); -``` - -## Create/Edit Todo Items - -By submitting an event trigger request, after the submission is successful, call fetch to re-fetch the latest list data, and the interface will automatically display the latest data. - -```javascript -import { useFetcher } from 'alova'; -import { useSQRequest } from '@alova/scene-*'; -import { createOrEditTodo, todoList } from './api.js'; - -const id = /* todo id */; -const { loading, data, send, onSuccess } = useSQRequest(createOrEditTodo, { - immediate: false, -}); - -const { fetch } = useFetcher(); -onSuccess(() => { - // Re-fetch list data - fetch(todoList); -}) - -// submit event callback function -const handleSubmit = newData => { - send(newData, id); -}; - -``` - -## Delete Todo item - -Delete the corresponding todo item by id - -```javascript -import { useSQRequest } from '@alova/scene-*'; -import { deleteTodo, todoList } from './api.js'; - -const { loading, data, send, onSuccess } = useSQRequest(deleteTodo, { - immediate: false -}); - -const { fetch } = useFetcher(); -onSuccess(() => { - // Re-fetch list data - fetch(todoList); -}); - -// Event callback triggers delete request -const handleDelete = deletingId => { - send(deletingId); -}; -``` - -So far, a simple Todo list management related request function has been completed, and then we will start to transform it to be compatible with the non-sense interaction mode. +--- +title: Step 1 - Implement features with conservative requests +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +Take Todo management as an example to realize the creation, editing, deletion and other functions of Todo in the non-sense interaction mode, and the key code related to the request will be provided in the following chapters. + +> The [simple list page example](/tutorial/example/silent-submit-simple-list) here contains the complete code, you can enter the experience. + +In the `@alova/scene-*` js package, **useSQRequest** will be used to replace **useRequest** provided by alova, and then the most common conservative request mode will be implemented first, and then the process will be done step by step Interactive mode compatibility. + +## Create an alova instance and related methods + +```javascript title="api.js" +import { createAlova } from 'alova'; + +export const alovaInst = createAlova({ + /*...*/ +}); + +/** Load todo list */ +const todoList = () => alovaInst.Get('/todo'); + +/** Load todo details */ +const todoDetail = id => + alovaInst.Get('/todo', { + params: { id } + }); + +/** Create and edit todo items */ +const createOrEditTodo = (data, id) => + alovaInst.Post('/todo', { + data, + id + }); + +/** Delete the todo item */ +const deleteTodo = id => alovaInst.Delete('/todo', { id }); +``` + +## Start the silent factory + +```javascript title="main.js" +import { bootSilentFactory } from '@alova/scene-*'; +import { alovaInst } from './api.js'; + +bootSilentFactory({ + alova: alovaInst +}); +``` + +## Load the Todo list + +Load and display page data in the simplest way + +```javascript +import { useSQRequest } from '@alova/scene-vue'; +import { todoList } from './api.js'; +const { data, loading, error } = useSQRequest(todoList, { + initialData: [] +}); +``` + +## Enter the Todo creation/editing page + +When creating a todo item, the id is empty, and no request for obtaining details is sent. When editing a todo item, if the id has a value, the detailed data will be obtained. + +```javascript +import { useSQRequest } from '@alova/scene-*'; +import { todoDetail } from './api.js'; + +const id = /* todo id */; +const { loading, data } = useSQRequest(() => todoDetail(id), { + initialData: { + title: '', + time: new Date() + }, + immediate: !!id +}); +``` + +## Create/Edit Todo Items + +By submitting an event trigger request, after the submission is successful, call fetch to re-fetch the latest list data, and the interface will automatically display the latest data. + +```javascript +import { useFetcher } from 'alova'; +import { useSQRequest } from '@alova/scene-*'; +import { createOrEditTodo, todoList } from './api.js'; + +const id = /* todo id */; +const { loading, data, send, onSuccess } = useSQRequest(createOrEditTodo, { + immediate: false, +}); + +const { fetch } = useFetcher(); +onSuccess(() => { + // Re-fetch list data + fetch(todoList); +}) + +// submit event callback function +const handleSubmit = newData => { + send(newData, id); +}; + +``` + +## Delete Todo item + +Delete the corresponding todo item by id + +```javascript +import { useSQRequest } from '@alova/scene-*'; +import { deleteTodo, todoList } from './api.js'; + +const { loading, data, send, onSuccess } = useSQRequest(deleteTodo, { + immediate: false +}); + +const { fetch } = useFetcher(); +onSuccess(() => { + // Re-fetch list data + fetch(todoList); +}); + +// Event callback triggers delete request +const handleDelete = deletingId => { + send(deletingId); +}; +``` + +So far, a simple Todo list management related request function has been completed, and then we will start to transform it to be compatible with the non-sense interaction mode. diff --git a/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/05-modify-response.md b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/05-modify-response.md index 9b616b930..8acba3b9a 100644 --- a/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/05-modify-response.md +++ b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/05-modify-response.md @@ -1,258 +1,259 @@ ---- -title: Step 2 - Adjust Response Handling -sidebar_position: 50 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -In the conservative request example in the previous section, we called `fetch` to re-fetch the refreshed page after creating, editing, and deleting requests for Todo items. In order to display the results immediately after the operation, we need to make the following adjustments: - -1. Set the behavior mode of create, edit and delete requests to `silent`, they will trigger the success callback immediately when the request is made; -2. Manually update the list, instead of pulling data, use virtual data to occupy the response data of the server; -3. Save operation records for data compensation when refreshing the page; - -## Set behavior mode - -Set by configuring the parameter `behavior`, the optional parameters are `queue`, `silent`, `static`, or a function that returns behavior data to dynamically set the behavior mode, the default is `queue`. - -The following sets the behavior parameters statically. - -```javascript -useSQRequest(createOrEditTodo, { - // highlight-start - behavior: 'silent', - // highlight-end - immediate: false -}); -``` - -The following is to dynamically set the behavior parameter. - -```javascript -const { send } = useSQRequest(createOrEditTodo, { - // highlight-start - // The arg parameter can be passed in through the send function - behavior: arg => { - if (arg === 0) return 'silent'; - return 'queue'; - }, - // highlight-end - immediate: false -}); -``` - -> When behavior is set as a function, it will be called every time a request is initiated to determine which behavior to process this request. - -## Silent queue description - -After setting the behavior parameter to `queue` or `silent`, the request will enter the silent queue and wait for the request to be initiated. By default, they will enter the queue named `default`. You can also specify other queues to save silentMethod instances. without interfering with each other. - -```javascript -useSQRequest(createOrEditTodo, { - // highlight-start - // The specified request information enters the queue named queue-2 - queue: 'queue-2', - // highlight-end - behavior: 'silent', - immediate: false -}); -``` - -## Manually update the list in the callback - -### Update the list after adding/editing - - - - -When the list page is not destroyed, such as using the modal box operation on the current page, or using `` (Vue) to keep the page components, the data will still exist. At this time, we use **updateStateEffect** to Update the list data. Compared with the **updateState** exported by alova, it has the function of tracking virtual data. When the response data is obtained, it will automatically track the virtual data in the list data and replace it with the actual data. - -```javascript -import { useSQRequest, updateStateEffect } from '@alova/scene-*'; -import { createOrEditTodo, todoList } from './api.js'; - -const { onSuccess } = useSQRequest(createOrEditTodo, { - behavior: 'silent', - immediate: false, - - // highlight-start - // Before processing list updates, it is necessary to construct virtual response data of the same structure according to the structure of the response data - // For example, when creating a Todo item, the id of this piece of data will be returned. - silentDefaultResponse: () => { - return { - id: '--' - }; - } - // highlight-end -}); - -// highlight-start -onSuccess(({ data, silentMethod }) => { - // Construct list data items - const editingItem = { - ...detail, - - // When editing, use the original id, otherwise use the id in the response data - // When submitting silently, data.id is virtual data, and when in static behavior mode, data.id is the actual id value - id: id || data.id - }; - - // use updateStateEffect instead of updateState - updateStateEffect(todoList(), todoListRaw => { - if (id) { - todoListRaw = todoListRaw.map(item => (item.id === id ? editingItem : item)); - } else { - todoListRaw.unshift(editingItem); - } - return todoListRaw; - }); -}); -// highlight-end -``` - -> updateStateEffect is used in the same way as [updateState](/tutorial/advanced/update-across-components) - - - - -When the list page has been destroyed and the data has been released, such as jumping to a new page, use **setCache** to update the cache data. When the list page is returned, the request will be re-initiated and the updated cache will be hit. - -```javascript -import { useSQRequest, setCache, equals } from '@alova/scene-*'; -import { createOrEditTodo, todoList } from './api.js'; - -const urlParams = new URLSearchParams(window.location.search); -const id = urlParams.get('id') || ''; -const { onSuccess } = useSQRequest(createOrEditTodo, { - behavior: 'silent', - immediate: false, - - // highlight-start - // Before processing list updates, it is necessary to construct virtual response data of the same structure according to the structure of the response data - // For example, when creating a Todo item, the id of this piece of data will be returned. - silentDefaultResponse: () => { - return { - id: '--' - }; - } - // highlight-end -}); -// highlight-start -onSuccess(({ data, silentMethod }) => { - // Construct list data items - const editingItem = { - ...detail, - - // When editing, use the original id, otherwise use the id in the response data - // When submitting silently, data.id is virtual data, and when in static behavior mode, data.id is the actual id value - id: id || data.id - }; - - const method TodoList = todoList(); - setCache(methodTodoList, todoListRaw => { - if (id) { - todoListRaw = todoListRaw.map(item => (equals(item.id, id) ? editingItem : item)); - } else { - todoListRaw.unshift(editingItem); - } - return todoListRaw; - }); - // Call setUpdateState to set response data tracking, so as to achieve the same delayed update effect as updateStateEffect - if (silentMethod) { - silentMethod.setUpdateState(methodTodoList); - silentMethod.save(); - } -}); -// highlight-end -``` - - - - -### Update list after removal - -```javascript -import { useSQRequest, updateStateEffect } from '@alova/scene-*'; -import { deleteTodo, todoList } from './api.js'; - -const { loading, data, send, onSuccess } = useSQRequest(deleteTodo, { - immediate: false, - // highlight-start - behavior: 'silent' - // highlight-end -}); - -onSuccess(({ sendArgs: [deletingId] }) => { - updateStateEffect(todoList(), todoListRaw => todoListRaw.filter(item => item.id !== deletingId)); -}); - -// Event callback triggers delete request -const handleDelete = deletingId => { - send(deletingId); -}; -``` - -## Save the operation record - -It is not enough to just update the list manually. We also need to consider that when the network is restored and there are still waiting requests in the request queue, the list data loaded at this time does not include the part of the unsubmitted request, which will cause certain problems for the user. Puzzled: - -> "I have clearly added multiple pieces of data, why is it not in the list?" - -Therefore, we need to record the operation and related data in the success callback, so that when the list data is loaded again, the uncommitted data will be manually compensated to the list, so that the list data will always be kept up-to-date. - -Saving operation records is also very simple, you only need to mount the relevant data to the silentMethod instance, and it will be persisted along with the instance. - -### create/edit success callback - -```javascript -//... -onSuccess(({ silentMethod }) => { - // Construct list data items - const editingItem = { - ...detail, - id: id || data.id - }; - //... - // highlight-start - if (silentMethod) { - // Set the name for subsequent queries - // If editingItem.id is virtual data will be automatically converted to its id - silentMethod.entity.setName('edit' + editingItem.id); - silentMethod.reviewData = { - operate: id ? 'edit' : 'add', - data: editingItem - }; - silentMethod.save(); - } - // highlight-end -}); -``` - -### delete success callback - -```javascript -//... -onSuccess(({ sendArgs: [deletingId], silentMethod }) => { - //... - // highlight-start - if (silentMethod) { - silentMethod.reviewData = { - operate: 'delete', - data: { - id: deletingId - } - }; - silentMethod.save(); - } - // highlight-end -}); -``` - -### Precautions - -1. In the onSuccess callback function, silentMethod has a value only in the `queue` and `silent` behavior modes; -2. Generally speaking, you can use `silentMethod.a = ...` or `silentMethod.b = ...` to save operation records, but it will report an error in typescript, so _reviewData_ is specially provided as a silent Submit the save attribute of the operation record; -3. After modifying the silentMethod data, you need to save the modification through `silentMethod.save()`; - -The next step is to set retry parameters on silent submit requests. +--- +title: Step 2 - Adjust Response Handling +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +In the conservative request example in the previous section, we called `fetch` to re-fetch the refreshed page after creating, editing, and deleting requests for Todo items. In order to display the results immediately after the operation, we need to make the following adjustments: + +1. Set the behavior mode of create, edit and delete requests to `silent`, they will trigger the success callback immediately when the request is made; +2. Manually update the list, instead of pulling data, use virtual data to occupy the response data of the server; +3. Save operation records for data compensation when refreshing the page; + +## Set behavior mode + +Set by configuring the parameter `behavior`, the optional parameters are `queue`, `silent`, `static`, or a function that returns behavior data to dynamically set the behavior mode, the default is `queue`. + +The following sets the behavior parameters statically. + +```javascript +useSQRequest(createOrEditTodo, { + // highlight-start + behavior: 'silent', + // highlight-end + immediate: false +}); +``` + +The following is to dynamically set the behavior parameter. + +```javascript +const { send } = useSQRequest(createOrEditTodo, { + // highlight-start + // The arg parameter can be passed in through the send function + behavior: arg => { + if (arg === 0) return 'silent'; + return 'queue'; + }, + // highlight-end + immediate: false +}); +``` + +> When behavior is set as a function, it will be called every time a request is initiated to determine which behavior to process this request. + +## Silent queue description + +After setting the behavior parameter to `queue` or `silent`, the request will enter the silent queue and wait for the request to be initiated. By default, they will enter the queue named `default`. You can also specify other queues to save silentMethod instances. without interfering with each other. + +```javascript +useSQRequest(createOrEditTodo, { + // highlight-start + // The specified request information enters the queue named queue-2 + queue: 'queue-2', + // highlight-end + behavior: 'silent', + immediate: false +}); +``` + +## Manually update the list in the callback + +### Update the list after adding/editing + + + + +When the list page is not destroyed, such as using the modal box operation on the current page, or using `` (Vue) to keep the page components, the data will still exist. At this time, we use **updateStateEffect** to Update the list data. Compared with the **updateState** exported by alova, it has the function of tracking virtual data. When the response data is obtained, it will automatically track the virtual data in the list data and replace it with the actual data. + +```javascript +import { useSQRequest, updateStateEffect } from '@alova/scene-*'; +import { createOrEditTodo, todoList } from './api.js'; + +const { onSuccess } = useSQRequest(createOrEditTodo, { + behavior: 'silent', + immediate: false, + + // highlight-start + // Before processing list updates, it is necessary to construct virtual response data of the same structure according to the structure of the response data + // For example, when creating a Todo item, the id of this piece of data will be returned. + silentDefaultResponse: () => { + return { + id: '--' + }; + } + // highlight-end +}); + +// highlight-start +onSuccess(({ data, silentMethod }) => { + // Construct list data items + const editingItem = { + ...detail, + + // When editing, use the original id, otherwise use the id in the response data + // When submitting silently, data.id is virtual data, and when in static behavior mode, data.id is the actual id value + id: id || data.id + }; + + // use updateStateEffect instead of updateState + updateStateEffect(todoList(), todoListRaw => { + if (id) { + todoListRaw = todoListRaw.map(item => (item.id === id ? editingItem : item)); + } else { + todoListRaw.unshift(editingItem); + } + return todoListRaw; + }); +}); +// highlight-end +``` + +> updateStateEffect is used in the same way as [updateState](/tutorial/advanced/update-across-components) + + + + +When the list page has been destroyed and the data has been released, such as jumping to a new page, use **setCache** to update the cache data. When the list page is returned, the request will be re-initiated and the updated cache will be hit. + +```javascript +import { useSQRequest, setCache, equals } from '@alova/scene-*'; +import { createOrEditTodo, todoList } from './api.js'; + +const urlParams = new URLSearchParams(window.location.search); +const id = urlParams.get('id') || ''; +const { onSuccess } = useSQRequest(createOrEditTodo, { + behavior: 'silent', + immediate: false, + + // highlight-start + // Before processing list updates, it is necessary to construct virtual response data of the same structure according to the structure of the response data + // For example, when creating a Todo item, the id of this piece of data will be returned. + silentDefaultResponse: () => { + return { + id: '--' + }; + } + // highlight-end +}); +// highlight-start +onSuccess(({ data, silentMethod }) => { + // Construct list data items + const editingItem = { + ...detail, + + // When editing, use the original id, otherwise use the id in the response data + // When submitting silently, data.id is virtual data, and when in static behavior mode, data.id is the actual id value + id: id || data.id + }; + + const method TodoList = todoList(); + setCache(methodTodoList, todoListRaw => { + if (id) { + todoListRaw = todoListRaw.map(item => (equals(item.id, id) ? editingItem : item)); + } else { + todoListRaw.unshift(editingItem); + } + return todoListRaw; + }); + // Call setUpdateState to set response data tracking, so as to achieve the same delayed update effect as updateStateEffect + if (silentMethod) { + silentMethod.setUpdateState(methodTodoList); + silentMethod.save(); + } +}); +// highlight-end +``` + + + + +### Update list after removal + +```javascript +import { useSQRequest, updateStateEffect } from '@alova/scene-*'; +import { deleteTodo, todoList } from './api.js'; + +const { loading, data, send, onSuccess } = useSQRequest(deleteTodo, { + immediate: false, + // highlight-start + behavior: 'silent' + // highlight-end +}); + +onSuccess(({ sendArgs: [deletingId] }) => { + updateStateEffect(todoList(), todoListRaw => + todoListRaw.filter(item => item.id !== deletingId) + ); +}); + +// Event callback triggers delete request +const handleDelete = deletingId => { + send(deletingId); +}; +``` + +## Save the operation record + +It is not enough to just update the list manually. We also need to consider that when the network is restored and there are still waiting requests in the request queue, the list data loaded at this time does not include the part of the unsubmitted request, which will cause certain problems for the user. Puzzled: + +> "I have clearly added multiple pieces of data, why is it not in the list?" + +Therefore, we need to record the operation and related data in the success callback, so that when the list data is loaded again, the uncommitted data will be manually compensated to the list, so that the list data will always be kept up-to-date. + +Saving operation records is also very simple, you only need to mount the relevant data to the silentMethod instance, and it will be persisted along with the instance. + +### create/edit success callback + +```javascript +//... +onSuccess(({ silentMethod }) => { + // Construct list data items + const editingItem = { + ...detail, + id: id || data.id + }; + //... + // highlight-start + if (silentMethod) { + // Set the name for subsequent queries + // If editingItem.id is virtual data will be automatically converted to its id + silentMethod.entity.setName('edit' + editingItem.id); + silentMethod.reviewData = { + operate: id ? 'edit' : 'add', + data: editingItem + }; + silentMethod.save(); + } + // highlight-end +}); +``` + +### delete success callback + +```javascript +//... +onSuccess(({ sendArgs: [deletingId], silentMethod }) => { + //... + // highlight-start + if (silentMethod) { + silentMethod.reviewData = { + operate: 'delete', + data: { + id: deletingId + } + }; + silentMethod.save(); + } + // highlight-end +}); +``` + +### Precautions + +1. In the onSuccess callback function, silentMethod has a value only in the `queue` and `silent` behavior modes; +2. Generally speaking, you can use `silentMethod.a = ...` or `silentMethod.b = ...` to save operation records, but it will report an error in typescript, so _reviewData_ is specially provided as a silent Submit the save attribute of the operation record; +3. After modifying the silentMethod data, you need to save the modification through `silentMethod.save()`; + +The next step is to set retry parameters on silent submit requests. diff --git a/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/06-request-retry.md b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/06-request-retry.md index 1b8ef082b..b9685ed3a 100644 --- a/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/06-request-retry.md +++ b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/06-request-retry.md @@ -1,144 +1,143 @@ ---- -title: Step 3 - Set Request Retry -sidebar_position: 60 ---- - -When a request enters the silent queue, you can set request retry parameters for it to ensure its request success rate. This is valid when the behavior mode is set to **queue** and **silent**. The difference is, The request under the behavior of **silent** is persistent by default, and the request will continue to be sent even if it is refreshed before the request succeeds, while the request under the behavior of **queue** will not be persisted and will be cleared after refreshing. - -## Maximum number of retries - -Set the maximum number of retries, no retries by default. - -```javascript -useSQRequest(createOrEditTodo, { - //... - // highlight-start - // The number of retries is 3 times - maxRetryTimes: 3 - // highlight-end -}); -``` - -## Request delay time - -By default, each retry interval is 1000ms, and we can customize the delay time of each retry in the avoidance strategy. - -```javascript -useSQRequest(createOrEditTodo, { - //... - maxRetryTimes: 3, - // highlight-start - // Requests are delayed by 2000ms each time - backoff: { - delay: 2000 - } - // highlight-end -}); -``` - -If you need to increase the delay time according to the rules, you can set a growth factor for it. - -```javascript -useSQRequest(createOrEditTodo, { - //... - maxRetryTimes: 3, - backoff: { - delay: 2000, - // highlight-start - // When multiplier is set to 2, the first retry delay is 2 seconds, the second is 4 seconds, and the third is 6 seconds - multiplier: 2 - // highlight-end - } -}); -``` - -not enough? You can even add a random jitter value to each delay to make it look less regular - -```javascript -useSQRequest(createOrEditTodo, { - //... - maxRetryTimes: 3, - backoff: { - delay: 2000, - multiplier: 2, - // highlight-start - /** - * The initial jitter percentage value of the delay request, the range is 0-1 - * When only startQuiver is set, endQuiver defaults to 1 - * For example set to 0.5, it will add 50% to 100% random time on the current delay time - * If endQuiver has a value, the delay time will be increased by a random value in the range of startQuiver and endQuiver - */ - startQuiver: 0.5, - - /** - * The jitter end percentage value of the delayed request, the range is 0-1 - * When only endQuiver is set, startQuiver defaults to 0 - * For example set to 0.8, it will add a random time from 0% to 80% on the current delay time - * If startQuiver has a value, the delay time will increase the random value in the range of startQuiver and endQuiver - */ - endQuiver: 0.8; - // highlight-end - } -}); -``` - -## set retry rules - -By default, as long as the request fails, it will be retried. The request failure is divided into the following situations: - -1. The request is wrong, and the error is not caught by the global `onError` hook; -2. The request was successful, but an error was thrown in the global `onSuccess` hook; - -But in reality, not all requests need to be retried. For example, when a server error occurs or the network is disconnected, it should not be retried. In this case, it is necessary to set a retry judgment rule. When a request fails, an instance of `Error` is usually obtained. We can set a regular expression to match `error.message` or `error.name`, and if the match passes, no retry will be made. - -```javascript -useSQRequest(createOrEditTodo, { - //... - // highlight-start - // When the thrown error name is 500, or the wrong message matches network error, do not retry - retryError: { - name: /^500$/, - message: /network error/i - } - // highlight-end -}); -``` - -You can also set one of the matching rules. When only setting the matching rules for `message`, it can be directly abbreviated as a regular expression. - -```javascript -// Only set the name that matches the error -useSQRequest(createOrEditTodo, { - //... - retryError: { - name: /^500$/ - } -}); - -// Only set the message that matches the error -useSQRequest(createOrEditTodo, { - //... - retryError: /network error/i -}); -``` - -In order not to pollute the error message, usually we will put the error code returned by the server in `error.name`, of course, you can also splice it into `error.message`, the error handling example of Response is as follows: - -```javascript -const alovaInst = createAlova({ - //... - responded: { - onSuccess(response) { - // Error thrown on 500 error - if (response.status === 500) { - const error = new Error(response.statusText); - error.name = response.status; - throw error; - } - return response.json(); - } - } -}); -``` - -In the next step, the saved operation records will be used to perform data compensation on the list data to achieve the latest state. +--- +title: Step 3 - Set Request Retry +--- + +When a request enters the silent queue, you can set request retry parameters for it to ensure its request success rate. This is valid when the behavior mode is set to **queue** and **silent**. The difference is, The request under the behavior of **silent** is persistent by default, and the request will continue to be sent even if it is refreshed before the request succeeds, while the request under the behavior of **queue** will not be persisted and will be cleared after refreshing. + +## Maximum number of retries + +Set the maximum number of retries, no retries by default. + +```javascript +useSQRequest(createOrEditTodo, { + //... + // highlight-start + // The number of retries is 3 times + maxRetryTimes: 3 + // highlight-end +}); +``` + +## Request delay time + +By default, each retry interval is 1000ms, and we can customize the delay time of each retry in the avoidance strategy. + +```javascript +useSQRequest(createOrEditTodo, { + //... + maxRetryTimes: 3, + // highlight-start + // Requests are delayed by 2000ms each time + backoff: { + delay: 2000 + } + // highlight-end +}); +``` + +If you need to increase the delay time according to the rules, you can set a growth factor for it. + +```javascript +useSQRequest(createOrEditTodo, { + //... + maxRetryTimes: 3, + backoff: { + delay: 2000, + // highlight-start + // When multiplier is set to 2, the first retry delay is 2 seconds, the second is 4 seconds, and the third is 6 seconds + multiplier: 2 + // highlight-end + } +}); +``` + +not enough? You can even add a random jitter value to each delay to make it look less regular + +```javascript +useSQRequest(createOrEditTodo, { + //... + maxRetryTimes: 3, + backoff: { + delay: 2000, + multiplier: 2, + // highlight-start + /** + * The initial jitter percentage value of the delay request, the range is 0-1 + * When only startQuiver is set, endQuiver defaults to 1 + * For example set to 0.5, it will add 50% to 100% random time on the current delay time + * If endQuiver has a value, the delay time will be increased by a random value in the range of startQuiver and endQuiver + */ + startQuiver: 0.5, + + /** + * The jitter end percentage value of the delayed request, the range is 0-1 + * When only endQuiver is set, startQuiver defaults to 0 + * For example set to 0.8, it will add a random time from 0% to 80% on the current delay time + * If startQuiver has a value, the delay time will increase the random value in the range of startQuiver and endQuiver + */ + endQuiver: 0.8; + // highlight-end + } +}); +``` + +## set retry rules + +By default, as long as the request fails, it will be retried. The request failure is divided into the following situations: + +1. The request is wrong, and the error is not caught by the global `onError` hook; +2. The request was successful, but an error was thrown in the global `onSuccess` hook; + +But in reality, not all requests need to be retried. For example, when a server error occurs or the network is disconnected, it should not be retried. In this case, it is necessary to set a retry judgment rule. When a request fails, an instance of `Error` is usually obtained. We can set a regular expression to match `error.message` or `error.name`, and if the match passes, no retry will be made. + +```javascript +useSQRequest(createOrEditTodo, { + //... + // highlight-start + // When the thrown error name is 500, or the wrong message matches network error, do not retry + retryError: { + name: /^500$/, + message: /network error/i + } + // highlight-end +}); +``` + +You can also set one of the matching rules. When only setting the matching rules for `message`, it can be directly abbreviated as a regular expression. + +```javascript +// Only set the name that matches the error +useSQRequest(createOrEditTodo, { + //... + retryError: { + name: /^500$/ + } +}); + +// Only set the message that matches the error +useSQRequest(createOrEditTodo, { + //... + retryError: /network error/i +}); +``` + +In order not to pollute the error message, usually we will put the error code returned by the server in `error.name`, of course, you can also splice it into `error.message`, the error handling example of Response is as follows: + +```javascript +const alovaInst = createAlova({ + //... + responded: { + onSuccess(response) { + // Error thrown on 500 error + if (response.status === 500) { + const error = new Error(response.statusText); + error.name = response.status; + throw error; + } + return response.json(); + } + } +}); +``` + +In the next step, the saved operation records will be used to perform data compensation on the list data to achieve the latest state. diff --git a/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/07-data-compensation.md b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/07-data-compensation.md index 48dcc797e..d526dd268 100644 --- a/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/07-data-compensation.md +++ b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/07-data-compensation.md @@ -1,56 +1,55 @@ ---- -title: Step 4 - Data Compensation -sidebar_position: 70 ---- - -The user may perform some data operations in a disconnected environment. At this time, the silent queue will be full of unsubmitted requests. When the network is restored, due to the limitation of the timing mechanism, it will take a little time to complete these requests. The list loaded at this time The data does not include unsubmitted requests, which can cause some confusion for users: - -> "I have clearly added multiple pieces of data, why is it not in the list?" - -Therefore, we need to manually compensate the unsubmitted data to the list, so that the list data is always kept up-to-date. In this step, the saved operation records will be used to compensate the list data. It is actually very simple. We It is only necessary to traverse the silentMethod instance of the relevant queue after the list request is successful, and update the operation records recorded in the previous step to the list data. - -```javascript -import { useSQRequest, filterSilentMethods, equals } from '@alova/scene-vue'; -import { todoList } from './api.js'; -const { data, loading, onSuccess } = useSQRequest(todoList, { - initialData: [] -}); - -onSuccess(() => { - // Get all silentMethod instances under the default queue - const silentMethods = filterSilentMethods(); - silentMethods.forEach(({ reviewData }) => { - if (!reviewData) { - return; - } - const { operate, data } = reviewData; - const index = todoListData.findIndex(({ id }) => equals(id, data.id)); - if ((operate === 'edit' || operate === 'remove') && index >= 0) { - operate === 'edit' ? todoListData.splice(index, 1, data) : todoListData.splice(index, 1); - } else if (operate === 'add' && index < 0) { - // There will be added uncommitted items when re-requesting and hitting the cache, these need to be filtered - todoListData.unshift(data); - } - }); -}); -``` - -## Explanation of related functions - -### filterSilentMethods - -Filter out the specified silentMethod instance by method name or regular expression, which is defined as follows: - -```typescript -function filterSilentMethods( - methodNameMatcher?: string | number | RegExp, - queueName?: string, - filterActive?: boolean -): SilentMethod[]; -``` - -**methodNameMatcher: **method name matcher, if it is a number or string, it will filter out the results that completely match the name, if it is a regular expression, it will filter out the matching results, if it is not passed, it will filter out all the results; - -**queueName**: Specify the queue to search for, if not uploaded, the _default_ queue will be searched by default; - -**filterActive**: whether to filter out the active state instance +--- +title: Step 4 - Data Compensation +--- + +The user may perform some data operations in a disconnected environment. At this time, the silent queue will be full of unsubmitted requests. When the network is restored, due to the limitation of the timing mechanism, it will take a little time to complete these requests. The list loaded at this time The data does not include unsubmitted requests, which can cause some confusion for users: + +> "I have clearly added multiple pieces of data, why is it not in the list?" + +Therefore, we need to manually compensate the unsubmitted data to the list, so that the list data is always kept up-to-date. In this step, the saved operation records will be used to compensate the list data. It is actually very simple. We It is only necessary to traverse the silentMethod instance of the relevant queue after the list request is successful, and update the operation records recorded in the previous step to the list data. + +```javascript +import { useSQRequest, filterSilentMethods, equals } from '@alova/scene-vue'; +import { todoList } from './api.js'; +const { data, loading, onSuccess } = useSQRequest(todoList, { + initialData: [] +}); + +onSuccess(() => { + // Get all silentMethod instances under the default queue + const silentMethods = filterSilentMethods(); + silentMethods.forEach(({ reviewData }) => { + if (!reviewData) { + return; + } + const { operate, data } = reviewData; + const index = todoListData.findIndex(({ id }) => equals(id, data.id)); + if ((operate === 'edit' || operate === 'remove') && index >= 0) { + operate === 'edit' ? todoListData.splice(index, 1, data) : todoListData.splice(index, 1); + } else if (operate === 'add' && index < 0) { + // There will be added uncommitted items when re-requesting and hitting the cache, these need to be filtered + todoListData.unshift(data); + } + }); +}); +``` + +## Explanation of related functions + +### filterSilentMethods + +Filter out the specified silentMethod instance by method name or regular expression, which is defined as follows: + +```typescript +function filterSilentMethods( + methodNameMatcher?: string | number | RegExp, + queueName?: string, + filterActive?: boolean +): SilentMethod[]; +``` + +**methodNameMatcher: **method name matcher, if it is a number or string, it will filter out the results that completely match the name, if it is a regular expression, it will filter out the matching results, if it is not passed, it will filter out all the results; + +**queueName**: Specify the queue to search for, if not uploaded, the _default_ queue will be searched by default; + +**filterActive**: whether to filter out the active state instance diff --git a/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/08-edit-item.md b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/08-edit-item.md index b2dd6c25d..08a361e62 100644 --- a/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/08-edit-item.md +++ b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/08-edit-item.md @@ -1,111 +1,110 @@ ---- -title: Step 5 - Edit Data -sidebar_position: 80 ---- - -> What should I do when the user needs to edit data when the network is disconnected? - -At this point, two situations need to be explained: - -1. The list data can meet the data echo of the edit page. At this time, the list data can be passed to the edit page without requesting. At this time, all list data supports editing in the silent submission mode; -2. The echo data on the edit page needs to be obtained through the api, and only the locally cached list items can echo the data normally, for example: - 1. For the list items that have been accessed before the network is disconnected, the request can hit the cache again; - 2. Created through the silent submission mode, but the list item has not been successfully submitted, and the submitted data still exists in the silentMethod instance; - -And here we will focus on the case of **2-2**. - -## Edit silent submit items - -In the previous chapters, we know that when the newly created data item has not been successfully submitted, the virtual data will be used as the placeholder for the id. Usually, we also get the data item through the id. At this time, we are in `useSQRequeset` Virtual data interception is implemented on the above, if a request is accompanied by virtual data information, it will be intercepted before sending and the data can be specified to replace the response data, and the request will be abandoned. - -Remember the **silentMethod.reviewData** saved in [Step 2 - Adjust Response Handling](/tutorial/strategy/sensorless-data-interaction/modify-response)? - -```javascript -onSuccess(({ silentMethod }) => { - // Construct list data items - const editingItem = { - ...detail, - id: id || data.id - }; - //... - if (silentMethod) { - // highlight-start - // Setting the name is to find the corresponding silentMethod instance when intercepting - silentMethod.entity.setName('edit' + editingItem.id); - silentMethod.reviewData = { - operate: id ? 'edit' : 'add', - data: editingItem - }; - // Don't forget to call save - silentMethod.save(); - // highlight-end - } -}); -``` - -It can be used not only for data compensation, but also for echoing data in edit pages. - -```javascript -const { loading, data } = useSQRequest(id => todoDetail(id), { - initialData: { - title: '', - time: new Date() - }, - immediate: false, - - // highlight-start - // Set the interception function, the function will be called when there is virtual data in this request - // If reviewData is returned, it will replace the response data and give up this request, otherwise the request will still be initiated - vDataCaptured: () => { - const targetSM = filterSilentMethods('edit' + todoId).pop(); - if (targetSM?.reviewData) { - return { ...targetSM.reviewData.data }; - } - } - // highlight-end -}); -``` - -:::warning Caution - -You can save enough data in **silentMethod.reviewData** to satisfy both list data compensation and edit page data echo. - -::: - -So far, data items created through silent submit mode also support editing! What's the problem, um... and one last one. - -## When the data item being edited is submitted successfully - -When the user is editing a data item that has not been successfully submitted, it suddenly submits successfully! At this time, we need to replace the virtual data used in the edit page with actual data, for example, replace the virtual id with the actual id, and use the actual id to submit in the next edit. This is also very simple, we only need to monitor This is done by silently submitting the success event, which will receive a data collection consisting of virtual data and real data. - -```javascript -import { onSilentSubmitSuccess, stringifyVData } from '@alova/scene-*'; - -//... -// id is virtual data during initialization -let id = /* todo virtual id */; - -// highlight-start -// Binding listener silently submits the successful event to update the id, and returns the unbind function, don't forget to call the unbind function when the component is destroyed -const unbindEvent = onSilentSubmitSuccess(event => { - const vDataId = stringifyVData(id); - if (event.vDataResponse[vDataId]) { - id = event.vDataResponse[vDataId]; - - // The following is to change the virtual id in the url to the actual id - history.replaceState(null, '', '?id=' + currentId); - } -}); -// highlight-end -``` - -Here, the `event.vDataResponse` value is a collection of virtual data id and actual data, and its format is as follows: - -```javascript -{ - '[vd:aaaaaa]': { id: 1 }, - '[vd:bbbbbb]': 1 -} -``` - -So far, we have completed all the content of a simple list of non-inductive interaction, but in other application scenarios such as editing applications, complex list management, etc., we may encounter more different needs. What else does alova have at this time? What are the features we can use? Please read the next chapter! +--- +title: Step 5 - Edit Data +--- + +> What should I do when the user needs to edit data when the network is disconnected? + +At this point, two situations need to be explained: + +1. The list data can meet the data echo of the edit page. At this time, the list data can be passed to the edit page without requesting. At this time, all list data supports editing in the silent submission mode; +2. The echo data on the edit page needs to be obtained through the api, and only the locally cached list items can echo the data normally, for example: + 1. For the list items that have been accessed before the network is disconnected, the request can hit the cache again; + 2. Created through the silent submission mode, but the list item has not been successfully submitted, and the submitted data still exists in the silentMethod instance; + +And here we will focus on the case of **2-2**. + +## Edit silent submit items + +In the previous chapters, we know that when the newly created data item has not been successfully submitted, the virtual data will be used as the placeholder for the id. Usually, we also get the data item through the id. At this time, we are in `useSQRequeset` Virtual data interception is implemented on the above, if a request is accompanied by virtual data information, it will be intercepted before sending and the data can be specified to replace the response data, and the request will be abandoned. + +Remember the **silentMethod.reviewData** saved in [Step 2 - Adjust Response Handling](/tutorial/strategy/sensorless-data-interaction/modify-response)? + +```javascript +onSuccess(({ silentMethod }) => { + // Construct list data items + const editingItem = { + ...detail, + id: id || data.id + }; + //... + if (silentMethod) { + // highlight-start + // Setting the name is to find the corresponding silentMethod instance when intercepting + silentMethod.entity.setName('edit' + editingItem.id); + silentMethod.reviewData = { + operate: id ? 'edit' : 'add', + data: editingItem + }; + // Don't forget to call save + silentMethod.save(); + // highlight-end + } +}); +``` + +It can be used not only for data compensation, but also for echoing data in edit pages. + +```javascript +const { loading, data } = useSQRequest(id => todoDetail(id), { + initialData: { + title: '', + time: new Date() + }, + immediate: false, + + // highlight-start + // Set the interception function, the function will be called when there is virtual data in this request + // If reviewData is returned, it will replace the response data and give up this request, otherwise the request will still be initiated + vDataCaptured: () => { + const targetSM = filterSilentMethods('edit' + todoId).pop(); + if (targetSM?.reviewData) { + return { ...targetSM.reviewData.data }; + } + } + // highlight-end +}); +``` + +:::warning Caution + +You can save enough data in **silentMethod.reviewData** to satisfy both list data compensation and edit page data echo. + +::: + +So far, data items created through silent submit mode also support editing! What's the problem, um... and one last one. + +## When the data item being edited is submitted successfully + +When the user is editing a data item that has not been successfully submitted, it suddenly submits successfully! At this time, we need to replace the virtual data used in the edit page with actual data, for example, replace the virtual id with the actual id, and use the actual id to submit in the next edit. This is also very simple, we only need to monitor This is done by silently submitting the success event, which will receive a data collection consisting of virtual data and real data. + +```javascript +import { onSilentSubmitSuccess, stringifyVData } from '@alova/scene-*'; + +//... +// id is virtual data during initialization +let id = /* todo virtual id */; + +// highlight-start +// Binding listener silently submits the successful event to update the id, and returns the unbind function, don't forget to call the unbind function when the component is destroyed +const unbindEvent = onSilentSubmitSuccess(event => { + const vDataId = stringifyVData(id); + if (event.vDataResponse[vDataId]) { + id = event.vDataResponse[vDataId]; + + // The following is to change the virtual id in the url to the actual id + history.replaceState(null, '', '?id=' + currentId); + } +}); +// highlight-end +``` + +Here, the `event.vDataResponse` value is a collection of virtual data id and actual data, and its format is as follows: + +```javascript +{ + '[vd:aaaaaa]': { id: 1 }, + '[vd:bbbbbb]': 1 +} +``` + +So far, we have completed all the content of a simple list of non-inductive interaction, but in other application scenarios such as editing applications, complex list management, etc., we may encounter more different needs. What else does alova have at this time? What are the features we can use? Please read the next chapter! diff --git a/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/09-what-more.md b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/09-what-more.md index 7f531391b..07ee94e6e 100644 --- a/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/09-what-more.md +++ b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/09-what-more.md @@ -1,342 +1,343 @@ ---- -title: What more? -sidebar_position: 90 ---- - -## Description of the role of virtual data - -In the previous chapters we used virtual data as the id placeholder, but its function is more than that, it can occupy any response data, for example, in a complex list, when creating a data item, the server needs to calculate additional Data, at this time, these additional data can also be occupied by virtual data, but this requires that the additional data needs to be returned together when the data item is created. See the following example: - -```javascript -const { onSuccess, send } = useSQRequest(createOrEditData, { - behavior: 'silent', - immediate: false, - - // Construct the same data structure as the response data - silentDefaultResponse: () => { - return { - id: '--', - extra1: '', - extra2: '' - }; - } -}); -onSuccess(event => { - event.data.id; // virtual data - event.data.extra1; //virtual data - event.data.extra2; //virtual data -}); -``` - -## New events in useSQRequest - -In order to better monitor the behavior of requests in the queue, `useSQRequset` also provides the following 3 additional event monitoring functions, you can obtain the binding functions in the following ways. - -```javascript -const { onBeforePushQueue, onPushedQueue, onFallback } = useSQRequest(/* ... */); -``` - -###onBeforePushQueue - -silentMethod is an event before entering the request queue. It is valid when the behavior mode is `queue` or `silent`. You can return `false` in this event callback to prevent the current silentMethod from entering the queue. For example, you may want to replace the current silentMethod with another one. It can be done like this: - -```javascript -//... -onBeforePushQueue(event => { - // Replace the old silentMethod with the specified id each time, reducing the number of requests - const prevSumbmitMethod = getSilentMethod('temp' + id); - if (event.silentMethod && prevSumbmitMethod) { - prevSumbmitMethod.replace(event.silentMethod); - return false; - } -}); -``` - -###onPushedQueue - -silentMethod The event after entering the queue. It is valid when the behavior mode is `queue` or `silent`. If the queue is blocked in the **onBeforePushQueue** event, this function will not trigger. - -### onFallback - -Similar to the traditional optimistic ui solution, we also provide a request rollback event, which will be triggered when the request reaches the maximum number of retries or the retry judgment fails. You can use it to handle some rollback operations. - -:::warning Warning - -When the fallback event is bound, even if the behavior mode is `silent`, the request will no longer be persisted, and it will be lost after refreshing the page. This is because the persistent silentMethod usually needs to ensure completion, not Rollbacks allow the user to re-process, in which case the rollback function should not be used. - -::: - -## Save additional operation data - -When creating or editing a data item, the previous chapters only saved the echo data to `silentMethod.reviewData`, if there are some additional data that need to be recorded, such as the menu options of the edit page, etc., we also need to record them to ensure They can also be selected when the network is disconnected. At this time, these data are mounted on the silentMethod instance and persisted together. - -Generally speaking, you can save persistent data with any property name, but an error will be reported in typescript, so the `silentMethod.extraData` attribute is specified for you as the storage field for extra data, remember to use `silentMethod.save()` for persistence data. - -## Custom serializer - -By default, alova uses localStorage for silentMethod data persistence, so it will call `JSON.stringify` to convert to a string when persisting, but json data only supports basic data types, pure objects and arrays, if you want Serialize special data structures such as Date instances, RegExp instances, functions, and custom class instances. Alova supports custom serializers to handle them. How to convert it to a data structure supported by json when storing it? How to convert to the original object structure. - -```javascript -const regExpSerializer = { - // forward is called when serializing - // Need to judge whether the data is the target value, if not, return undefined, indicating that it will not be processed - forward: data => data instanceof RegExp ? data.source : undefined, - - // backward is called during deserialization, data is the value returned in forward - backward: source => new RegExp(source); -}; - -bootSilentFactory({ - //... - // use this serializer via serializers - serializers: { - customRegExp: regExpSerializer - } -}) -``` - -SilentFactory provides Date and RegExp serializers by default, and you can also use the same key to override the default serializers - -```javascript -const defaultSerializers = { - Date: dateSerializer, - RegExp: regExpSerializer -}; -``` - -[Read Date serializer source code](https://github.com/alovajs/scene/blob/main/src/hooks/silent/serializer/date.ts) - -## Manipulate the silent queue - -Silent queues are used to ensure the timing of requests. We can create queues arbitrarily, and all requests entering the queue will be stored in the queue in the form of **SilentMethod** instances. Each **SilentMethod** not only contains request information, but also Contains relevant configuration for silent submission. Any number of silent queues can be generated, and it supports searching, modifying, and deleting silentMethod instances in the queue. - -### Using multiple silent queues - -Requests whose behavior mode is set to `queue` and `silent` will enter the silent queue. By default, the silentMethod instance will be assigned to the **default** queue. When it needs to be assigned to other queues, it can be assigned in _useSQRequest_ Specify the `queue` parameter. - -```javascript -useSQRequest(createOrEditTodo, { - //... - // Specify the silentMethod instance to enter the queue named customQueue - queue: 'customQueue', - behavior: 'silent' -}); -``` - -You can also specify `queue` as a function to return the name of the queue. This function will be called every time a request is made, and the function parameters come from the send function. - -```javascript -const { send } = useSQRequest(createOrEditTodo, { - //... - // Determine whether to enter the customQueue queue according to useCustomQueue - queue: useCustomQueue => (useCustomQueue ? 'customQueue' : 'default'), - behavior: 'silent', - immediate: false -}); -const handleClick = () => { - send(true); -}; -``` - -### Find silentMethod - -In the previous [data compensation](/tutorial/strategy/sensorless-data-interaction/data-compensation), we used [filterSilentMethods](/tutorial/strategy/sensorless-data-interaction/data-compensation#filtersilentmethods) to find the silentMethod of the specified queue instance, it will return all matching silentMethod instances, here are two more ways to find the queue: - -#### Find a silentMethod instance - -Use `getSilentMethod` to query the first matching silentMethod instance, the usage is the same as [filterSilentMethods](/tutorial/strategy/sensorless-data-interaction/data-compensation#filtersilentmethods). - -```typescript -function filterSilentMethods( - methodNameMatcher?: string | number | RegExp, - queueName?: string, - filterActive?: boolean -): SilentMethod | undefined; -``` - -#### Custom Lookup - -Customize the lookup through the exported `silentQueueMap` queue collection, the data structure of `silentQueueMap` is: - -```javascript -const silentQueueMap = { - default: [silentMethod1, silentMethod2 /* ... */], - queueName1: [silentMethod3, silentMethod4 /* ... */], - queueName2: [silentMethod5, silentMethod6 /* ... */] - //... -}; -``` - -### Change the silentMethod in the queue - -After finding the silentMethod instance you want, you can manipulate these waiting silentMethod instances. - -#### replace silentMethod - -Call `silentMethod.replace` to replace a silentMethod with another silentMethod in the queue. - -```javascript -oldSilentMethod.replace(newSilentMethod); -``` - -#### remove silentMethod - -Call `silentMethod.remove` to remove the current silentMethod from the queue. - -```javascript -oldSilentMethod.remove(); -``` - -#### Use silentQueueMap to change silentMethod - -You can also access `silentQueueMap` to custom change any data of any queue. - -```javascript -import { silentQueueMap } from '@alova/scene-*'; - -// Modify all silentMethods in the default queue -silentQueueMap.default.forEach(silentMethodItem => { - //... -}); -``` - -## Queue request delay - -Some applications need to submit data frequently, such as editor-type applications, which are saved in real time during the editing process without aborting the user's use. When using silent submission in this type of application, more request information will be generated, not only It will fill up the front-end cache and make the server receive too many requests. At this time, we may no longer need to synchronize all save operations, but send an operation within a period of time. There will be the following two solutions: - -1. Throttle the editing operation, and only initiate one submission within n seconds. This solution may lose the operation records during the delay period, resulting in only the status of the last submission being obtained when refreshing; -2. Delay the save request in the queue, and only keep the latest request information during the delay time, so that you can reduce the request while retaining the latest editing status; - -By default, the silentMethod in the queue will send the request immediately after the last response. We can set the delay sending time of the silentMethod through `requestWait` at startup, and keep the latest silentMethod by manipulating the queue during this time. - -### Set request delay - -You can set the delay time for the specified queue, or you can set multiple queues at once. - -```javascript -bootSilentFactory({ - alova: alovaInst, - // highlight-start - requestWait: [ - // Each silentMethod in the customQueue queue will delay 5000ms to initiate a request - { queue: 'customQueue', wait: 5000 }, - - //Use a regular expression to uniformly set a 3000ms delay request time for the queue whose name is prefixed with delay - { queue: /^delay/, wait: 3000 } - ] - // highlight-end -}); -``` - -When `requestWait` is directly set to a number, it is valid for the default queue by default. - -```javascript -bootSilentFactory({ - alova: alovaInst, - // highlight-start - // Each silentMethod in the default queue will delay 5000ms to initiate a request - requestWait: 5000 - // highlight-end -}); -``` - -### Dynamically set the request delay - -Many times we only want a specific silentMethod in the queue to set a delay request. At this time, a function can be used to dynamically set the request delay. Each silentMethod of the specified queue will call this function before initiating a request to determine the delay time. - -```javascript -bootSilentFactory({ - alova: alovaInst, - // highlight-start - requestWait: [ - { - queue: /^delay, - // Only delay 5000ms for post requests with url /edit - wait: (silentMethod, queueName) => { - const { type, url, data } = silentMethod.entity; - if (type === 'POST' && url === '/edit') { - return 5000; - } - } - }, - ] - // highlight-end -}); -``` - -Similarly, when `requestWait` is set directly to a function, it defaults to the default queue. - -```javascript -bootSilentFactory({ - alova: alovaInst, - // highlight-start - requestWait: (silentMethod, queueName) => { - const { type, url, data } = silentMethod.entity; - if (type === 'POST' && url === '/edit') { - return 5000; - } - } - // highlight-end -}); -``` - -## Dynamically set the method name - -For easy search, if you need to dynamically set the name of the method in silentMethod, you can call setName. - -```javascript -// Reset the name of silentMethod on successful request -onSuccess(({ data, silentMethod }) => { - silentMethod.entity.setName('name' + data.id); -}); -``` - -## Global silent submit event - -In the previous chapter, we touched `onSilentSubmitSuccess`, we provided a total of 5 global events. - -### onSilentSubmitBoot - -Silent factory start event, triggered after the silent factory is started. - -```typescript -function onSilentSubmitBoot(handler: () => void): OffEventCallback; -``` - -###onBeforeSilentSubmit - -Fired before a silentMethod request with `behavior=silent`. - -```typescript -function onBeforeSilentSubmit(handler: (event: GlobalSQEvent)): OffEventCallback; -``` - -###onSilentSubmitSuccess - -Fired when a silentMethod request with `behavior=silent` succeeds. - -```typescript -function onSilentSubmitSuccess(handler: (event: GlobalSQSuccessEvent) => void): OffEventCallback; -``` - -### onSilentSubmitError - -Fired when the silentMethod request with `behavior=silent` fails, but the maximum number of retries has not been reached. - -```typescript -function onSilentSubmitError(handler: (event: GlobalSQErrorEvent) => void): OffEventCallback; -``` - -### onSilentSubmitFail - -When the silentMethod of `behavior=silent` encounters a request failure, the following 3 situations will trigger this event: - -1. Triggered when the request reaches the maximum number of retries; -2. When the number of retries is not set, the first request failure will trigger; -3. Triggered when the request has not reached the maximum number of retries, but the retry is judged to be no more retries; - -```typescript -function onSilentSubmitFail(handler: (event: GlobalSQFailEvent) => void): OffEventCallback; -``` - -The above event binding functions will return the unbinding function, and you can unbind the event before the component is unmounted. +--- +title: What more? +--- + +## Description of the role of virtual data + +In the previous chapters we used virtual data as the id placeholder, but its function is more than that, it can occupy any response data, for example, in a complex list, when creating a data item, the server needs to calculate additional Data, at this time, these additional data can also be occupied by virtual data, but this requires that the additional data needs to be returned together when the data item is created. See the following example: + +```javascript +const { onSuccess, send } = useSQRequest(createOrEditData, { + behavior: 'silent', + immediate: false, + + // Construct the same data structure as the response data + silentDefaultResponse: () => { + return { + id: '--', + extra1: '', + extra2: '' + }; + } +}); +onSuccess(event => { + event.data.id; // virtual data + event.data.extra1; //virtual data + event.data.extra2; //virtual data +}); +``` + +## New events in useSQRequest + +In order to better monitor the behavior of requests in the queue, `useSQRequset` also provides the following 3 additional event monitoring functions, you can obtain the binding functions in the following ways. + +```javascript +const { onBeforePushQueue, onPushedQueue, onFallback } = useSQRequest(/* ... */); +``` + +###onBeforePushQueue + +silentMethod is an event before entering the request queue. It is valid when the behavior mode is `queue` or `silent`. You can return `false` in this event callback to prevent the current silentMethod from entering the queue. For example, you may want to replace the current silentMethod with another one. It can be done like this: + +```javascript +//... +onBeforePushQueue(event => { + // Replace the old silentMethod with the specified id each time, reducing the number of requests + const prevSumbmitMethod = getSilentMethod('temp' + id); + if (event.silentMethod && prevSumbmitMethod) { + prevSumbmitMethod.replace(event.silentMethod); + return false; + } +}); +``` + +###onPushedQueue + +silentMethod The event after entering the queue. It is valid when the behavior mode is `queue` or `silent`. If the queue is blocked in the **onBeforePushQueue** event, this function will not trigger. + +### onFallback + +Similar to the traditional optimistic ui solution, we also provide a request rollback event, which will be triggered when the request reaches the maximum number of retries or the retry judgment fails. You can use it to handle some rollback operations. + +:::warning Warning + +When the fallback event is bound, even if the behavior mode is `silent`, the request will no longer be persisted, and it will be lost after refreshing the page. This is because the persistent silentMethod usually needs to ensure completion, not Rollbacks allow the user to re-process, in which case the rollback function should not be used. + +::: + +## Save additional operation data + +When creating or editing a data item, the previous chapters only saved the echo data to `silentMethod.reviewData`, if there are some additional data that need to be recorded, such as the menu options of the edit page, etc., we also need to record them to ensure They can also be selected when the network is disconnected. At this time, these data are mounted on the silentMethod instance and persisted together. + +Generally speaking, you can save persistent data with any property name, but an error will be reported in typescript, so the `silentMethod.extraData` attribute is specified for you as the storage field for extra data, remember to use `silentMethod.save()` for persistence data. + +## Custom serializer + +By default, alova uses localStorage for silentMethod data persistence, so it will call `JSON.stringify` to convert to a string when persisting, but json data only supports basic data types, pure objects and arrays, if you want Serialize special data structures such as Date instances, RegExp instances, functions, and custom class instances. Alova supports custom serializers to handle them. How to convert it to a data structure supported by json when storing it? How to convert to the original object structure. + +```javascript +const regExpSerializer = { + // forward is called when serializing + // Need to judge whether the data is the target value, if not, return undefined, indicating that it will not be processed + forward: data => data instanceof RegExp ? data.source : undefined, + + // backward is called during deserialization, data is the value returned in forward + backward: source => new RegExp(source); +}; + +bootSilentFactory({ + //... + // use this serializer via serializers + serializers: { + customRegExp: regExpSerializer + } +}) +``` + +SilentFactory provides Date and RegExp serializers by default, and you can also use the same key to override the default serializers + +```javascript +const defaultSerializers = { + Date: dateSerializer, + RegExp: regExpSerializer +}; +``` + +[Read Date serializer source code](https://github.com/alovajs/scene/blob/main/src/hooks/silent/serializer/date.ts) + +## Manipulate the silent queue + +Silent queues are used to ensure the timing of requests. We can create queues arbitrarily, and all requests entering the queue will be stored in the queue in the form of **SilentMethod** instances. Each **SilentMethod** not only contains request information, but also Contains relevant configuration for silent submission. Any number of silent queues can be generated, and it supports searching, modifying, and deleting silentMethod instances in the queue. + +### Using multiple silent queues + +Requests whose behavior mode is set to `queue` and `silent` will enter the silent queue. By default, the silentMethod instance will be assigned to the **default** queue. When it needs to be assigned to other queues, it can be assigned in _useSQRequest_ Specify the `queue` parameter. + +```javascript +useSQRequest(createOrEditTodo, { + //... + // Specify the silentMethod instance to enter the queue named customQueue + queue: 'customQueue', + behavior: 'silent' +}); +``` + +You can also specify `queue` as a function to return the name of the queue. This function will be called every time a request is made, and the function parameters come from the send function. + +```javascript +const { send } = useSQRequest(createOrEditTodo, { + //... + // Determine whether to enter the customQueue queue according to useCustomQueue + queue: useCustomQueue => (useCustomQueue ? 'customQueue' : 'default'), + behavior: 'silent', + immediate: false +}); +const handleClick = () => { + send(true); +}; +``` + +### Find silentMethod + +In the previous [data compensation](/tutorial/strategy/sensorless-data-interaction/data-compensation), we used [filterSilentMethods](/tutorial/strategy/sensorless-data-interaction/data-compensation#filtersilentmethods) to find the silentMethod of the specified queue instance, it will return all matching silentMethod instances, here are two more ways to find the queue: + +#### Find a silentMethod instance + +Use `getSilentMethod` to query the first matching silentMethod instance, the usage is the same as [filterSilentMethods](/tutorial/strategy/sensorless-data-interaction/data-compensation#filtersilentmethods). + +```typescript +function filterSilentMethods( + methodNameMatcher?: string | number | RegExp, + queueName?: string, + filterActive?: boolean +): SilentMethod | undefined; +``` + +#### Custom Lookup + +Customize the lookup through the exported `silentQueueMap` queue collection, the data structure of `silentQueueMap` is: + +```javascript +const silentQueueMap = { + default: [silentMethod1, silentMethod2 /* ... */], + queueName1: [silentMethod3, silentMethod4 /* ... */], + queueName2: [silentMethod5, silentMethod6 /* ... */] + //... +}; +``` + +### Change the silentMethod in the queue + +After finding the silentMethod instance you want, you can manipulate these waiting silentMethod instances. + +#### replace silentMethod + +Call `silentMethod.replace` to replace a silentMethod with another silentMethod in the queue. + +```javascript +oldSilentMethod.replace(newSilentMethod); +``` + +#### remove silentMethod + +Call `silentMethod.remove` to remove the current silentMethod from the queue. + +```javascript +oldSilentMethod.remove(); +``` + +#### Use silentQueueMap to change silentMethod + +You can also access `silentQueueMap` to custom change any data of any queue. + +```javascript +import { silentQueueMap } from '@alova/scene-*'; + +// Modify all silentMethods in the default queue +silentQueueMap.default.forEach(silentMethodItem => { + //... +}); +``` + +## Queue request delay + +Some applications need to submit data frequently, such as editor-type applications, which are saved in real time during the editing process without aborting the user's use. When using silent submission in this type of application, more request information will be generated, not only It will fill up the front-end cache and make the server receive too many requests. At this time, we may no longer need to synchronize all save operations, but send an operation within a period of time. There will be the following two solutions: + +1. Throttle the editing operation, and only initiate one submission within n seconds. This solution may lose the operation records during the delay period, resulting in only the status of the last submission being obtained when refreshing; +2. Delay the save request in the queue, and only keep the latest request information during the delay time, so that you can reduce the request while retaining the latest editing status; + +By default, the silentMethod in the queue will send the request immediately after the last response. We can set the delay sending time of the silentMethod through `requestWait` at startup, and keep the latest silentMethod by manipulating the queue during this time. + +### Set request delay + +You can set the delay time for the specified queue, or you can set multiple queues at once. + +```javascript +bootSilentFactory({ + alova: alovaInst, + // highlight-start + requestWait: [ + // Each silentMethod in the customQueue queue will delay 5000ms to initiate a request + { queue: 'customQueue', wait: 5000 }, + + //Use a regular expression to uniformly set a 3000ms delay request time for the queue whose name is prefixed with delay + { queue: /^delay/, wait: 3000 } + ] + // highlight-end +}); +``` + +When `requestWait` is directly set to a number, it is valid for the default queue by default. + +```javascript +bootSilentFactory({ + alova: alovaInst, + // highlight-start + // Each silentMethod in the default queue will delay 5000ms to initiate a request + requestWait: 5000 + // highlight-end +}); +``` + +### Dynamically set the request delay + +Many times we only want a specific silentMethod in the queue to set a delay request. At this time, a function can be used to dynamically set the request delay. Each silentMethod of the specified queue will call this function before initiating a request to determine the delay time. + +```javascript +bootSilentFactory({ + alova: alovaInst, + // highlight-start + requestWait: [ + { + queue: /^delay, + // Only delay 5000ms for post requests with url /edit + wait: (silentMethod, queueName) => { + const { type, url, data } = silentMethod.entity; + if (type === 'POST' && url === '/edit') { + return 5000; + } + } + }, + ] + // highlight-end +}); +``` + +Similarly, when `requestWait` is set directly to a function, it defaults to the default queue. + +```javascript +bootSilentFactory({ + alova: alovaInst, + // highlight-start + requestWait: (silentMethod, queueName) => { + const { type, url, data } = silentMethod.entity; + if (type === 'POST' && url === '/edit') { + return 5000; + } + } + // highlight-end +}); +``` + +## Dynamically set the method name + +For easy search, if you need to dynamically set the name of the method in silentMethod, you can call setName. + +```javascript +// Reset the name of silentMethod on successful request +onSuccess(({ data, silentMethod }) => { + silentMethod.entity.setName('name' + data.id); +}); +``` + +## Global silent submit event + +In the previous chapter, we touched `onSilentSubmitSuccess`, we provided a total of 5 global events. + +### onSilentSubmitBoot + +Silent factory start event, triggered after the silent factory is started. + +```typescript +function onSilentSubmitBoot(handler: () => void): OffEventCallback; +``` + +###onBeforeSilentSubmit + +Fired before a silentMethod request with `behavior=silent`. + +```typescript +function onBeforeSilentSubmit(handler: (event: GlobalSQEvent)): OffEventCallback; +``` + +###onSilentSubmitSuccess + +Fired when a silentMethod request with `behavior=silent` succeeds. + +```typescript +function onSilentSubmitSuccess( + handler: (event: GlobalSQSuccessEvent) => void +): OffEventCallback; +``` + +### onSilentSubmitError + +Fired when the silentMethod request with `behavior=silent` fails, but the maximum number of retries has not been reached. + +```typescript +function onSilentSubmitError(handler: (event: GlobalSQErrorEvent) => void): OffEventCallback; +``` + +### onSilentSubmitFail + +When the silentMethod of `behavior=silent` encounters a request failure, the following 3 situations will trigger this event: + +1. Triggered when the request reaches the maximum number of retries; +2. When the number of retries is not set, the first request failure will trigger; +3. Triggered when the request has not reached the maximum number of retries, but the retry is judged to be no more retries; + +```typescript +function onSilentSubmitFail(handler: (event: GlobalSQFailEvent) => void): OffEventCallback; +``` + +The above event binding functions will return the unbinding function, and you can unbind the event before the component is unmounted. diff --git a/versioned_docs/version-2.x/tutorial/05-strategy/02-usePagination.md b/versioned_docs/version-2.x/tutorial/05-strategy/02-usePagination.md index 4246e09a9..37ef09e95 100644 --- a/versioned_docs/version-2.x/tutorial/05-strategy/02-usePagination.md +++ b/versioned_docs/version-2.x/tutorial/05-strategy/02-usePagination.md @@ -1,6 +1,5 @@ --- title: Pagination request strategy -sidebar_position: 20 --- import Tabs from '@theme/Tabs'; diff --git a/versioned_docs/version-2.x/tutorial/05-strategy/03-useForm.md b/versioned_docs/version-2.x/tutorial/05-strategy/03-useForm.md index 07717e3a2..f858a7516 100644 --- a/versioned_docs/version-2.x/tutorial/05-strategy/03-useForm.md +++ b/versioned_docs/version-2.x/tutorial/05-strategy/03-useForm.md @@ -1,6 +1,5 @@ --- title: Form submit strategy -sidebar_position: 30 --- import Tabs from '@theme/Tabs'; diff --git a/versioned_docs/version-2.x/tutorial/05-strategy/04-tokenAuthentication.md b/versioned_docs/version-2.x/tutorial/05-strategy/04-tokenAuthentication.md index 68ccecbe9..893e6afe3 100644 --- a/versioned_docs/version-2.x/tutorial/05-strategy/04-tokenAuthentication.md +++ b/versioned_docs/version-2.x/tutorial/05-strategy/04-tokenAuthentication.md @@ -1,6 +1,5 @@ --- title: Token authentication interceptor -sidebar_position: 40 --- import Tabs from '@theme/Tabs'; diff --git a/versioned_docs/version-2.x/tutorial/05-strategy/05-useUploader.md b/versioned_docs/version-2.x/tutorial/05-strategy/05-useUploader.md index c98e2f027..b1a36b1ba 100644 --- a/versioned_docs/version-2.x/tutorial/05-strategy/05-useUploader.md +++ b/versioned_docs/version-2.x/tutorial/05-strategy/05-useUploader.md @@ -1,6 +1,5 @@ ---- -title: Universal upload strategy -sidebar_position: 40 ---- - -coming soon... +--- +title: Universal upload strategy +--- + +coming soon... diff --git a/versioned_docs/version-2.x/tutorial/05-strategy/06-useAutoRequest.md b/versioned_docs/version-2.x/tutorial/05-strategy/06-useAutoRequest.md index 0ba9f1eae..9a69384b3 100644 --- a/versioned_docs/version-2.x/tutorial/05-strategy/06-useAutoRequest.md +++ b/versioned_docs/version-2.x/tutorial/05-strategy/06-useAutoRequest.md @@ -1,6 +1,5 @@ --- title: Automatically refetch data -sidebar_position: 60 --- import Tabs from '@theme/Tabs'; diff --git a/versioned_docs/version-2.x/tutorial/05-strategy/07-useBreakpointUploader.md b/versioned_docs/version-2.x/tutorial/05-strategy/07-useBreakpointUploader.md index 8d06ed628..9a7887fa9 100644 --- a/versioned_docs/version-2.x/tutorial/05-strategy/07-useBreakpointUploader.md +++ b/versioned_docs/version-2.x/tutorial/05-strategy/07-useBreakpointUploader.md @@ -1,6 +1,5 @@ ---- -title: Breakpoint upload -sidebar_position: 60 ---- - -coming soon... +--- +title: Breakpoint upload +--- + +coming soon... diff --git a/versioned_docs/version-2.x/tutorial/05-strategy/08-useCaptcha.md b/versioned_docs/version-2.x/tutorial/05-strategy/08-useCaptcha.md index e2ad31223..d6cbf71e7 100644 --- a/versioned_docs/version-2.x/tutorial/05-strategy/08-useCaptcha.md +++ b/versioned_docs/version-2.x/tutorial/05-strategy/08-useCaptcha.md @@ -1,6 +1,5 @@ --- title: send captcha -sidebar_position: 40 --- import Tabs from '@theme/Tabs'; diff --git a/versioned_docs/version-2.x/tutorial/05-strategy/09-actionDelegationMiddleware.md b/versioned_docs/version-2.x/tutorial/05-strategy/09-actionDelegationMiddleware.md index c548c74b6..ad7be7d2f 100644 --- a/versioned_docs/version-2.x/tutorial/05-strategy/09-actionDelegationMiddleware.md +++ b/versioned_docs/version-2.x/tutorial/05-strategy/09-actionDelegationMiddleware.md @@ -1,6 +1,5 @@ --- title: Cross components to trigger request -sidebar_position: 50 --- import Tabs from '@theme/Tabs'; diff --git a/versioned_docs/version-2.x/tutorial/05-strategy/10-useSerialRequest.md b/versioned_docs/version-2.x/tutorial/05-strategy/10-useSerialRequest.md index bade49f1f..bc1b1c3db 100644 --- a/versioned_docs/version-2.x/tutorial/05-strategy/10-useSerialRequest.md +++ b/versioned_docs/version-2.x/tutorial/05-strategy/10-useSerialRequest.md @@ -1,6 +1,5 @@ --- title: useRequest with serial -sidebar_position: 60 --- import Tabs from '@theme/Tabs'; diff --git a/versioned_docs/version-2.x/tutorial/05-strategy/11-useSerialWatcher.md b/versioned_docs/version-2.x/tutorial/05-strategy/11-useSerialWatcher.md index ab30b621e..9eeb06d23 100644 --- a/versioned_docs/version-2.x/tutorial/05-strategy/11-useSerialWatcher.md +++ b/versioned_docs/version-2.x/tutorial/05-strategy/11-useSerialWatcher.md @@ -1,6 +1,5 @@ --- title: useWatcher with serial -sidebar_position: 70 --- import Tabs from '@theme/Tabs'; diff --git a/versioned_docs/version-2.x/tutorial/05-strategy/12-useRetriableRequest.md b/versioned_docs/version-2.x/tutorial/05-strategy/12-useRetriableRequest.md index 301fdcb95..f902aba4a 100644 --- a/versioned_docs/version-2.x/tutorial/05-strategy/12-useRetriableRequest.md +++ b/versioned_docs/version-2.x/tutorial/05-strategy/12-useRetriableRequest.md @@ -1,6 +1,5 @@ --- title: retriable request -sidebar_position: 80 --- import Tabs from '@theme/Tabs'; diff --git a/versioned_docs/version-2.x/tutorial/05-strategy/13-useSSE.md b/versioned_docs/version-2.x/tutorial/05-strategy/13-useSSE.md index 37eb9ac40..6e24ce943 100644 --- a/versioned_docs/version-2.x/tutorial/05-strategy/13-useSSE.md +++ b/versioned_docs/version-2.x/tutorial/05-strategy/13-useSSE.md @@ -1,6 +1,5 @@ --- title: request by server-send events -sidebar_position: 120 --- import Tabs from '@theme/Tabs'; diff --git a/versioned_docs/version-2.x/tutorial/05-strategy/14-rateLimitMiddleware.md b/versioned_docs/version-2.x/tutorial/05-strategy/14-rateLimitMiddleware.md index aacc9c0c4..e816220f7 100644 --- a/versioned_docs/version-2.x/tutorial/05-strategy/14-rateLimitMiddleware.md +++ b/versioned_docs/version-2.x/tutorial/05-strategy/14-rateLimitMiddleware.md @@ -1,8 +1,7 @@ ---- -title: Request rate limit -sidebar_position: 130 ---- - -Set the number of requests that should be executed immediately for each interval, and other requests will be automatically delayed. - -coming soon... +--- +title: Request rate limit +--- + +Set the number of requests that should be executed immediately for each interval, and other requests will be automatically delayed. + +coming soon... diff --git a/versioned_docs/version-2.x/tutorial/06-advanced/01-use-fetcher.md b/versioned_docs/version-2.x/tutorial/06-advanced/01-use-fetcher.md index f51eab080..ededcbd1f 100644 --- a/versioned_docs/version-2.x/tutorial/06-advanced/01-use-fetcher.md +++ b/versioned_docs/version-2.x/tutorial/06-advanced/01-use-fetcher.md @@ -1,307 +1,306 @@ ---- -title: Fetch Data -sidebar_position: 10 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -When you have the following needs: - -1. Preload the data that will be used in subsequent processes and store it in the cache, so that users no longer have to wait for the data loading process; -2. Conveniently implement cross-page data update (similar to global state), for example, modify an item in the todo list and then re-fetch the latest data, and the interface will be refreshed after the response. - -`useFetcher` is the hook used to implement the above scenario. The response data obtained through it cannot be received directly, but the data fetched through it will not only update the cache, but also update the corresponding state, thereby re-rendering the view. - -## Preload data - -Let's implement a paging list to automatically preload the next page of data. Before preloading data, please make sure that the Method instance used has enabled caching. - - - - -```html - - - -``` - - - - -```jsx -import { useState } from 'react'; - -//method instance creation function -const getTodoList = currentPage => { - return alovaInstance.Get('/todo/list', { - localCache: 60000, - params: { - currentPage, - pageSize: 10 - } - }); -}; - -const App = () => { - const { - // The fetching attribute is the same as loading. It is true when sending the fetch request and false after the request is completed. - fetching, - error, - onSuccess, - onError, - onComplete, - - // Only after calling fetch will a request be sent to fetch data. You can call fetch repeatedly to fetch data from different interfaces. - fetch - } = useFetcher({ - updateState: false - }); - const [currentPage, setCurrentPage] = useState(1); - const { data, onSuccess } = useWatcher(() => getTodoList(currentPage), [currentPage], { - immediate: true - }); - - // After the current page is loaded successfully, pass in the method instance of the next page to pre-fetch the data of the next page. - onSuccess(() => { - fetch(getTodoList(currentPage + 1)); - }); - - return ( - <> - {fetching ?
Fetching...
: null} - {/* list view */} - - ); -}; -``` - -
- - -```html - - -{#if fetching} -
Fetching...
-{/if} - -``` - -
- - -```html - - - -``` - - -
- -:::warning - -The above example is set `updateState` to false when calling `useFetcher`. This is because the data fetching will automatically trigger a states cross-component updating by default, causing the view to be re-rendered. When preload data is the same as the currently requested data. You can set it to false to avoid affecting view errors. - -::: - -## Update views across modules/components - -Next, we will modify a todo data and re-fetch the latest todo list data to update the view. We may not know which page the todo list is currently on. In this case, when using the `fetch` function, we can use [Method instance matcher](/tutorial/advanced/method-matcher) to dynamically fetch the data of the current page. - -> The Method instance matcher is used to find method instances that meet the conditions among the requested method instances. - -First, set a name for the method instance in the todo list, which is used to filter out the required Method instance when the Method instance cannot be specified directly. - -```javascript title="api/todoList.js" -const getTodoList = currentPage => { - return alovaInstance.Get('/todo/list', { - // highlight-start - name: 'todoList', - // highlight-end - params: { - currentPage, - pageSize: 10 - } - }); -}; -``` - -Then in the `EditTodo` component, use the `fetch` function to dynamically find the last name of `todoList` in the requested Method instance to fetch data. - -```javascript title="EditTodo Component" -const { fetch } = useFetcher(); - -// Trigger data fetch in event -const handleSubmit = () => { - // submit data... - await fetch({ - name: 'todoList', - filter: (method, index, ary) => { - // Return true to specify the Method instance that needs to be fetched - return index === ary.length - 1; - } - }); -}; -``` - -:::warning Notes - -useFetcher only updates the cache after the request is completed, and if this Method is foundIf the instance has been requested using useHook before, the `data` state created by this useHook will also be updated to ensure that the page data is consistent. This is the guarantee that `useFetcher` is used to update views across modules/components. - -::: - -> For more methods of using `Method` instance matcher, see [Method instance matcher](/tutorial/advanced/method-matcher). - -## Force sending request - -Same as `useRequest` and `useWatcher`, please read [Force Request](/tutorial/cache/force-request) for more information. - -## Bind response callback - -useFetcher also supports binding `onSuccess/onError/onComplete` callback functions. - -```javascript -const { onSuccess, onError, onComplete } = useFetcher(); -``` - -Please read [Response Processing](/tutorial/combine-framework/response) for details. - -## send function parameter passing rules - -Different from `useRequest` and `useWatcher`, the custom parameters of the fetch function start from the second parameter, and they will also be received by the event callback and `force` function respectively. - -```javascript -const { onSuccess, fetch } = useFetcher(); -onSuccess(({ sendArgs }) => { - //The value of sendArgs is ['test arg'] -}); - -fetch(getTodoList(), 'test arg'); -``` - -For details, please read [send function parameter passing rules](/tutorial/combine-framework/receive-params). - -## Comparison between useRequest and useFetcher - -1. useFetcher does not return the `data` field, the pre-fetched data will be saved in the cache, and the status data of the corresponding location will be updated; -2. Renamed `loading` to `fetching`; -3. There is no `send` function, but there is a `fetch` function. You can reuse the fetch function to fetch data from different interfaces. At this time, you can use the `fetching` and `error` states to uniformly render the view to achieve unified processing. the goal of; +--- +title: Fetch Data +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +When you have the following needs: + +1. Preload the data that will be used in subsequent processes and store it in the cache, so that users no longer have to wait for the data loading process; +2. Conveniently implement cross-page data update (similar to global state), for example, modify an item in the todo list and then re-fetch the latest data, and the interface will be refreshed after the response. + +`useFetcher` is the hook used to implement the above scenario. The response data obtained through it cannot be received directly, but the data fetched through it will not only update the cache, but also update the corresponding state, thereby re-rendering the view. + +## Preload data + +Let's implement a paging list to automatically preload the next page of data. Before preloading data, please make sure that the Method instance used has enabled caching. + + + + +```html + + + +``` + + + + +```jsx +import { useState } from 'react'; + +//method instance creation function +const getTodoList = currentPage => { + return alovaInstance.Get('/todo/list', { + localCache: 60000, + params: { + currentPage, + pageSize: 10 + } + }); +}; + +const App = () => { + const { + // The fetching attribute is the same as loading. It is true when sending the fetch request and false after the request is completed. + fetching, + error, + onSuccess, + onError, + onComplete, + + // Only after calling fetch will a request be sent to fetch data. You can call fetch repeatedly to fetch data from different interfaces. + fetch + } = useFetcher({ + updateState: false + }); + const [currentPage, setCurrentPage] = useState(1); + const { data, onSuccess } = useWatcher(() => getTodoList(currentPage), [currentPage], { + immediate: true + }); + + // After the current page is loaded successfully, pass in the method instance of the next page to pre-fetch the data of the next page. + onSuccess(() => { + fetch(getTodoList(currentPage + 1)); + }); + + return ( + <> + {fetching ?
Fetching...
: null} + {/* list view */} + + ); +}; +``` + +
+ + +```html + + +{#if fetching} +
Fetching...
+{/if} + +``` + +
+ + +```html + + + +``` + + +
+ +:::warning + +The above example is set `updateState` to false when calling `useFetcher`. This is because the data fetching will automatically trigger a states cross-component updating by default, causing the view to be re-rendered. When preload data is the same as the currently requested data. You can set it to false to avoid affecting view errors. + +::: + +## Update views across modules/components + +Next, we will modify a todo data and re-fetch the latest todo list data to update the view. We may not know which page the todo list is currently on. In this case, when using the `fetch` function, we can use [Method instance matcher](/tutorial/advanced/method-matcher) to dynamically fetch the data of the current page. + +> The Method instance matcher is used to find method instances that meet the conditions among the requested method instances. + +First, set a name for the method instance in the todo list, which is used to filter out the required Method instance when the Method instance cannot be specified directly. + +```javascript title="api/todoList.js" +const getTodoList = currentPage => { + return alovaInstance.Get('/todo/list', { + // highlight-start + name: 'todoList', + // highlight-end + params: { + currentPage, + pageSize: 10 + } + }); +}; +``` + +Then in the `EditTodo` component, use the `fetch` function to dynamically find the last name of `todoList` in the requested Method instance to fetch data. + +```javascript title="EditTodo Component" +const { fetch } = useFetcher(); + +// Trigger data fetch in event +const handleSubmit = () => { + // submit data... + await fetch({ + name: 'todoList', + filter: (method, index, ary) => { + // Return true to specify the Method instance that needs to be fetched + return index === ary.length - 1; + } + }); +}; +``` + +:::warning Notes + +useFetcher only updates the cache after the request is completed, and if this Method is foundIf the instance has been requested using useHook before, the `data` state created by this useHook will also be updated to ensure that the page data is consistent. This is the guarantee that `useFetcher` is used to update views across modules/components. + +::: + +> For more methods of using `Method` instance matcher, see [Method instance matcher](/tutorial/advanced/method-matcher). + +## Force sending request + +Same as `useRequest` and `useWatcher`, please read [Force Request](/tutorial/cache/force-request) for more information. + +## Bind response callback + +useFetcher also supports binding `onSuccess/onError/onComplete` callback functions. + +```javascript +const { onSuccess, onError, onComplete } = useFetcher(); +``` + +Please read [Response Processing](/tutorial/combine-framework/response) for details. + +## send function parameter passing rules + +Different from `useRequest` and `useWatcher`, the custom parameters of the fetch function start from the second parameter, and they will also be received by the event callback and `force` function respectively. + +```javascript +const { onSuccess, fetch } = useFetcher(); +onSuccess(({ sendArgs }) => { + //The value of sendArgs is ['test arg'] +}); + +fetch(getTodoList(), 'test arg'); +``` + +For details, please read [send function parameter passing rules](/tutorial/combine-framework/receive-params). + +## Comparison between useRequest and useFetcher + +1. useFetcher does not return the `data` field, the pre-fetched data will be saved in the cache, and the status data of the corresponding location will be updated; +2. Renamed `loading` to `fetching`; +3. There is no `send` function, but there is a `fetch` function. You can reuse the fetch function to fetch data from different interfaces. At this time, you can use the `fetching` and `error` states to uniformly render the view to achieve unified processing. the goal of; diff --git a/versioned_docs/version-2.x/tutorial/06-advanced/02-update-across-components.md b/versioned_docs/version-2.x/tutorial/06-advanced/02-update-across-components.md index a8a814256..bc982e6d8 100644 --- a/versioned_docs/version-2.x/tutorial/06-advanced/02-update-across-components.md +++ b/versioned_docs/version-2.x/tutorial/06-advanced/02-update-across-components.md @@ -1,102 +1,101 @@ ---- -title: Update states across components -sidebar_position: 20 ---- - -There is a scenario where when the user clicks on an item in the todo list, enters the todo details page and edits it, at this time we hope that the todo list data on the previous page will also be updated without resetting the situation. For edited content, `useFetcher` is no longer applicable. - -At this time, you can use `updateState` to update the existing responsive state under any module/page. It can find and modify the responsive state in other modules. - -[Here is an example of `updateState`](/tutorial/example/update-state) - -## Use method instance to find response states - -When determining the method instance corresponding to the updated response state, you can pass in this method instance in `updateState`. It will find whether there is a corresponding response state under this instance and provide it to you for modification in the callback function. Finally Just return the modified data. - -```javascript -import { updateState } from 'alova'; - -//Todo item being edited -const editingTodo = { - id: 1, - title: 'todo1', - time: '09:00' -}; - -const { send, onSuccess } = useRequest(createTodoPoster, { immediate: false }); -onSuccess(() => { - // highlight-start - // Fixed modification of todo data on the first page - // updateState will return whether the update is successful - const updated = updateState(getTodoList(1), todoList => { - return todoList.map(item => { - if (item.id === editingTodo.id) { - return { - ...item, - ...editingTodo - }; - } - return item; - }); - }); - // highlight-end -}); -``` - -:::warning note - -1. When updating the state through `updateState`, if the cache (memory cache and persistent cache) is detected, the new data update cache will also be updated. -2. Only when a request has been initiated using useRequest or useWatcher, alova will manage the states returned by the hook. The reason is that the response states is generated and saved through a Method instance, but when no request is initiated, the url and URL in the Method instance are Parameters such as params, query, and headers are still uncertain. - -::: - -## Dynamically update response states - -Maybe sometimes you are not sure that you need to update the response states under the method, but you know how to find the cached data that needs to be invalidated. We can use [Method instance matcher](/tutorial/advanced/method-matcher) to dynamically Find the corresponding method instance. The following example shows adding a piece of data to the list corresponding to the method instance named todoList. - -```javascript -updateState('todoList', todoListRaw => { - todoListRaw.push({ - title: 'new todo', - time: '10:00' - }); - return todoListRaw; -}); -``` - -The [Method instance matcher](/tutorial/advanced/method-matcher) will be introduced in detail in subsequent chapters. - -## Listen for matching events - -When dynamically updating the response state, sometimes you may want to do some processing when a method instance is matched, or you may want to obtain the matching method instance. `updateState` can also pass in a third parameter to set a matching event to achieve these purposes. . - -```javascript -updateState( - 'todoList', - todoListRaw => { - // ... - }, - { - // Called when a method instance is matched, and the parameter is the matched method instance. - onMatch: method => { - // ... - } - } -); -``` - -:::warning ⚠️ Please make sure the component is not destroyed - -By default, `updateState` will look for the response state created by alova's useHooks when sending a request. However, to prevent memory overflow, the destruction of a component will also recycle all the states created internally, so please make sure you use `updateState` It is hoped that the container component corresponding to the updated response states has not been destroyed, otherwise the corresponding response states will not be found and the update will fail. - -This problem often occurs when updating states across pages. What we tend to overlook is that by default, the previous page has been destroyed when the page jumps. Therefore, if you want to update states across pages, here are two suggestions: - -1. Persist the page components to ensure that the updated states can still be found; -2. Use [setCache](/tutorial/cache/set-and-query) instead of `updateState`. The principle is that when the request for the previous page exists in the cache, update its cache to ensure that when the page is created again, the The request can hit the updated cache to achieve the same effect. - -::: - -## Notes - -1. In actual use, whether you use `useRequest` or `useWatcher` to send a request, you can call the `send` function to specify different parameters to send the request repeatedly. The response states returned by these use hooks will be used by multiple method instances. Reference, so you can choose any method instance to match the same response states value; -2. When dynamically searching and updating the response states, the method instance matcher finds multiple method instances, and the first instance will prevail; +--- +title: Update states across components +--- + +There is a scenario where when the user clicks on an item in the todo list, enters the todo details page and edits it, at this time we hope that the todo list data on the previous page will also be updated without resetting the situation. For edited content, `useFetcher` is no longer applicable. + +At this time, you can use `updateState` to update the existing responsive state under any module/page. It can find and modify the responsive state in other modules. + +[Here is an example of `updateState`](/tutorial/example/update-state) + +## Use method instance to find response states + +When determining the method instance corresponding to the updated response state, you can pass in this method instance in `updateState`. It will find whether there is a corresponding response state under this instance and provide it to you for modification in the callback function. Finally Just return the modified data. + +```javascript +import { updateState } from 'alova'; + +//Todo item being edited +const editingTodo = { + id: 1, + title: 'todo1', + time: '09:00' +}; + +const { send, onSuccess } = useRequest(createTodoPoster, { immediate: false }); +onSuccess(() => { + // highlight-start + // Fixed modification of todo data on the first page + // updateState will return whether the update is successful + const updated = updateState(getTodoList(1), todoList => { + return todoList.map(item => { + if (item.id === editingTodo.id) { + return { + ...item, + ...editingTodo + }; + } + return item; + }); + }); + // highlight-end +}); +``` + +:::warning note + +1. When updating the state through `updateState`, if the cache (memory cache and persistent cache) is detected, the new data update cache will also be updated. +2. Only when a request has been initiated using useRequest or useWatcher, alova will manage the states returned by the hook. The reason is that the response states is generated and saved through a Method instance, but when no request is initiated, the url and URL in the Method instance are Parameters such as params, query, and headers are still uncertain. + +::: + +## Dynamically update response states + +Maybe sometimes you are not sure that you need to update the response states under the method, but you know how to find the cached data that needs to be invalidated. We can use [Method instance matcher](/tutorial/advanced/method-matcher) to dynamically Find the corresponding method instance. The following example shows adding a piece of data to the list corresponding to the method instance named todoList. + +```javascript +updateState('todoList', todoListRaw => { + todoListRaw.push({ + title: 'new todo', + time: '10:00' + }); + return todoListRaw; +}); +``` + +The [Method instance matcher](/tutorial/advanced/method-matcher) will be introduced in detail in subsequent chapters. + +## Listen for matching events + +When dynamically updating the response state, sometimes you may want to do some processing when a method instance is matched, or you may want to obtain the matching method instance. `updateState` can also pass in a third parameter to set a matching event to achieve these purposes. . + +```javascript +updateState( + 'todoList', + todoListRaw => { + // ... + }, + { + // Called when a method instance is matched, and the parameter is the matched method instance. + onMatch: method => { + // ... + } + } +); +``` + +:::warning ⚠️ Please make sure the component is not destroyed + +By default, `updateState` will look for the response state created by alova's useHooks when sending a request. However, to prevent memory overflow, the destruction of a component will also recycle all the states created internally, so please make sure you use `updateState` It is hoped that the container component corresponding to the updated response states has not been destroyed, otherwise the corresponding response states will not be found and the update will fail. + +This problem often occurs when updating states across pages. What we tend to overlook is that by default, the previous page has been destroyed when the page jumps. Therefore, if you want to update states across pages, here are two suggestions: + +1. Persist the page components to ensure that the updated states can still be found; +2. Use [setCache](/tutorial/cache/set-and-query) instead of `updateState`. The principle is that when the request for the previous page exists in the cache, update its cache to ensure that when the page is created again, the The request can hit the updated cache to achieve the same effect. + +::: + +## Notes + +1. In actual use, whether you use `useRequest` or `useWatcher` to send a request, you can call the `send` function to specify different parameters to send the request repeatedly. The response states returned by these use hooks will be used by multiple method instances. Reference, so you can choose any method instance to match the same response states value; +2. When dynamically searching and updating the response states, the method instance matcher finds multiple method instances, and the first instance will prevail; diff --git a/versioned_docs/version-2.x/tutorial/06-advanced/03-method-matcher.md b/versioned_docs/version-2.x/tutorial/06-advanced/03-method-matcher.md index 1666b3b22..b582e48b4 100644 --- a/versioned_docs/version-2.x/tutorial/06-advanced/03-method-matcher.md +++ b/versioned_docs/version-2.x/tutorial/06-advanced/03-method-matcher.md @@ -1,141 +1,140 @@ ---- -title: Method Matcher -sidebar_position: 30 ---- - -A method matcher is a method that dynamically finds a method instance in a list of requested method snapshots. It is generally used. When developers are not sure which method to use, they can use the method matcher to search according to certain rules. - -## Matching rules - -When a request is made using a method instance, it will be saved as a snapshot. The method matcher looks in these method snapshots based on the `name` attribute set by the method instance. Multiple matchers are allowed to set the same `name`. - -Method instance matching types are as follows: - -```typescript -type MethodFilter = - | string - | RegExp - | { - name: string | RegExp; - filter: (method: Method, index: number, methods: Method[]) => boolean; - - // Optional parameter, if the alova object is passed in, it will only match the Method instance created by this alova, otherwise it will match the Method instances of all alova instances. - alova?: Alova; - }; -``` - -The method instance matcher can be used in the following functions. - -- [setCache](/tutorial/cache/set-and-query) -- [queryCache](/tutorial/cache/set-and-query) -- [invalidateCache](/tutorial/cache/manually-invalidate) -- [updateState](/tutorial/advanced/update-across-components) -- [useFetcher.fetch](/tutorial/advanced/use-fetcher) - -## Match by name attribute - -Matching is performed by passing in the complete instance name, and its matching result is an array. - -```javascript -// Each time getTodoList is called, a new method instance will be generated, and their names are the same. -const getTodoList = currentPage => - alova.Get('/todo/list', { - // highlight-start - name: 'todoList' - // highlight-end - // ... - }); - -//The following means invalidating the cache of all Method instances with name 'todoList' -invalidateCache('todoList'); -``` - -## Match by regular expression - -By passing in a regular expression for matching, any method instance whose name matches the regular expression will be matched, and its result is also an array. - -```javascript -// The following means invalidating the cache of all Method instances whose names start with 'todo' -invalidateCache(/^todo/); -``` - -## Filter matching results - -Further filter method instances that do not meet the conditions by specifying `filter`. The filter function is used in the same way as Array.prototype.filter. Returning true indicates successful matching, and returning false indicates failure. See the type declaration above for details. - -Let's look at a few examples. - -**Invalidate the cache of the last method instance with a specific name** - -```javascript -invalidateCache({ - name: 'todoList', - filter: (method, index, methods) => index === methods.length - 1 -}); -``` - -**Set the cache of the last method instance of a specific name created by `alovaInst`** - -```javascript -setCache( - { - name: /^todo/, - filter: (method, index, methods) => index === methods.length - 1, - - // If the alova parameter is passed, only the Method instances created by this alova instance will be matched, otherwise it will be matched in all Method instances. - alova: alovaInst - }, - newCache -); -``` - -**Repulse the last requested data from the todo list** - -```javascript -const { fetch } = useFetcher(); -fetch({ - name: 'todoList', - filter: (method, index, methods) => index === methods.length - 1 -}); -``` - -> The alova parameter can further narrow the matching scope. - -## Differences in use in different functions - -### invalidateCache - -Apply the set of all matching Method instances, that is, invalidate the cache corresponding to all matching Method instances. - -### setCache - -All matching Method instance collections are applied. When static data is passed in, all Method instance caches are set to the same value. When the callback function is passed in, this function will be called cyclically and the return value will be used as cache data. - -### updateState - -The first matching Method instance is applied. - -### fetch - -The first matching Method instance is applied, i.e. the data will only be pulled once. - -## Limit instance snapshots - -`[v2.20.0+]` By default, 1000 method instance snapshots will be saved, otherwise memory overflow may occur in frequent request scenarios. You can also adjust the limit as needed. - -```js -import { globalConfig } from 'alova'; - -globalConfig({ - // Limit saving to 500 instance snapshots - limitSnapshots: 500 -}); -``` - -When set to 0, instance snapshots are no longer saved and the method matcher cannot be used. - -```js -globalConfig({ - limitSnapshots: 0 -}); -``` +--- +title: Method Matcher +--- + +A method matcher is a method that dynamically finds a method instance in a list of requested method snapshots. It is generally used. When developers are not sure which method to use, they can use the method matcher to search according to certain rules. + +## Matching rules + +When a request is made using a method instance, it will be saved as a snapshot. The method matcher looks in these method snapshots based on the `name` attribute set by the method instance. Multiple matchers are allowed to set the same `name`. + +Method instance matching types are as follows: + +```typescript +type MethodFilter = + | string + | RegExp + | { + name: string | RegExp; + filter: (method: Method, index: number, methods: Method[]) => boolean; + + // Optional parameter, if the alova object is passed in, it will only match the Method instance created by this alova, otherwise it will match the Method instances of all alova instances. + alova?: Alova; + }; +``` + +The method instance matcher can be used in the following functions. + +- [setCache](/tutorial/cache/set-and-query) +- [queryCache](/tutorial/cache/set-and-query) +- [invalidateCache](/tutorial/cache/manually-invalidate) +- [updateState](/tutorial/advanced/update-across-components) +- [useFetcher.fetch](/tutorial/advanced/use-fetcher) + +## Match by name attribute + +Matching is performed by passing in the complete instance name, and its matching result is an array. + +```javascript +// Each time getTodoList is called, a new method instance will be generated, and their names are the same. +const getTodoList = currentPage => + alova.Get('/todo/list', { + // highlight-start + name: 'todoList' + // highlight-end + // ... + }); + +//The following means invalidating the cache of all Method instances with name 'todoList' +invalidateCache('todoList'); +``` + +## Match by regular expression + +By passing in a regular expression for matching, any method instance whose name matches the regular expression will be matched, and its result is also an array. + +```javascript +// The following means invalidating the cache of all Method instances whose names start with 'todo' +invalidateCache(/^todo/); +``` + +## Filter matching results + +Further filter method instances that do not meet the conditions by specifying `filter`. The filter function is used in the same way as Array.prototype.filter. Returning true indicates successful matching, and returning false indicates failure. See the type declaration above for details. + +Let's look at a few examples. + +**Invalidate the cache of the last method instance with a specific name** + +```javascript +invalidateCache({ + name: 'todoList', + filter: (method, index, methods) => index === methods.length - 1 +}); +``` + +**Set the cache of the last method instance of a specific name created by `alovaInst`** + +```javascript +setCache( + { + name: /^todo/, + filter: (method, index, methods) => index === methods.length - 1, + + // If the alova parameter is passed, only the Method instances created by this alova instance will be matched, otherwise it will be matched in all Method instances. + alova: alovaInst + }, + newCache +); +``` + +**Repulse the last requested data from the todo list** + +```javascript +const { fetch } = useFetcher(); +fetch({ + name: 'todoList', + filter: (method, index, methods) => index === methods.length - 1 +}); +``` + +> The alova parameter can further narrow the matching scope. + +## Differences in use in different functions + +### invalidateCache + +Apply the set of all matching Method instances, that is, invalidate the cache corresponding to all matching Method instances. + +### setCache + +All matching Method instance collections are applied. When static data is passed in, all Method instance caches are set to the same value. When the callback function is passed in, this function will be called cyclically and the return value will be used as cache data. + +### updateState + +The first matching Method instance is applied. + +### fetch + +The first matching Method instance is applied, i.e. the data will only be pulled once. + +## Limit instance snapshots + +`[v2.20.0+]` By default, 1000 method instance snapshots will be saved, otherwise memory overflow may occur in frequent request scenarios. You can also adjust the limit as needed. + +```js +import { globalConfig } from 'alova'; + +globalConfig({ + // Limit saving to 500 instance snapshots + limitSnapshots: 500 +}); +``` + +When set to 0, instance snapshots are no longer saved and the method matcher cannot be used. + +```js +globalConfig({ + limitSnapshots: 0 +}); +``` diff --git a/versioned_docs/version-2.x/tutorial/06-advanced/04-middleware.md b/versioned_docs/version-2.x/tutorial/06-advanced/04-middleware.md index 2ffa7b652..ac41e8e79 100644 --- a/versioned_docs/version-2.x/tutorial/06-advanced/04-middleware.md +++ b/versioned_docs/version-2.x/tutorial/06-advanced/04-middleware.md @@ -1,405 +1,404 @@ ---- -title: Middleware -sidebar_position: 40 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -Request middleware is an asynchronous function. it provides a powerful ability to control almost all behaviors of a request. If you just use alova, then you probably don't need to use request middleware, because it is mainly used to complete custom request strategies, no matter simple or complex request strategies, you may use it, let's look at it next What magical powers does it have. - -## Middleware function - -Request middleware is an async function, You can define request middleware in `useRequest`, `useWatcher`, `useFetcher`. The following is a simple request middleware, which prints some information before and after the request without changing any request behavior. - -```javascript -useRequest(todoList, { - async middleware(_, next) { - console.log('before request'); - await next(); - console.log('after request'); - } -}); -``` - -Here are a few things you need to know about the `next` function call. This function is also an asynchronous function. Calling it can continue to send requests. At this time, the _loading_ state will be set to true, and then the request will be sent. The return value of next is a Promise instance with the response data, you can manipulate the return value in the middleware function. - -## Control response data - -The return value of the middleware function will be used as the response data of this request to participate in subsequent processing. If the middleware does not return any data but calls `next`, the response data of this request will be used for subsequent processing. - -```javascript -// The modified result will be used as the response data -useRequest(todoList, { - async middleware(_, next) { - const result = await next(); - result.code = 500; - return result; - } -}); - -// Will participate in subsequent processing with the response data of this request -useRequest(todoList, { - async middleware(_, next) { - await next(); - } -}); - -// will respond with the string abc -useRequest(todoList, { - async middleware(_, next) { - await next(); - return 'abc'; - } -}); -``` - -There is also a special case here. When `next` is not called and there is no return value, subsequent processing will not be performed, which means that _onSuccess_, _onError_, _onComplete_ response events will not be triggered. - -```javascript -useRequest(todoList, { - async middleware() {} -}); -``` - -## change request - -Sometimes you want to change the request. At this time, you can specify another method instance in `next`, and the information in this method will be requested when sending the request. At the same time, you can also set whether to force the request through `next` Penetrating the cache is also very simple. - -```javascript -useRequest(todoList, { - async middleware(_, next) { - await next({ - // Change the requested method instance - method: newMethodInstance, - - // Whether to force the request this time - force: true - }); - } -}); -``` - -## Control errors - -### Catch errors - -In the middleware, you can capture the request error generated in `next`, after capturing, the global `onError` hook will no longer be triggered. - -```javascript -useRequest(todoList, { - async middleware(_, next) { - try { - await next(); - } catch (e) { - console.error('Error caught', e); - } - } -}); -``` - -### Throw an error - -Of course, you can also throw a custom error in the middleware, even if the request is normal, it will enter the request error process. - -```javascript -// No request is sent, and global and request-level onError will be triggered at the same time. If the request is sent through `method.send`, the promise instance of rejection will be returned -useRequest(todoList, { - async middleware(_, next) { - throw new Error('error on before request'); - await next(); - } -}); - -// After request is success, global and request-level onError will be triggered at the same time. If the request is sent through `method.send`, the promise instance of rejection will be returned -useRequest(todoList, { - async middleware(_, next) { - await next(); - throw new Error('error on after request'); - } -}); -``` - -## Control response delay - -In the middleware, we can delay the response or respond in advance. In the case of advance, although the response data cannot be obtained, some other data can be returned as the response data to participate in subsequent processing. - -```javascript -// Delay response for 1 second -useRequest(todoList, { - async middleware(_, next) { - await new Promise(resolve => { - setTimeout(resolve, 1000); - }); - return next(); - } -}); - -// Respond immediately and use the string abc as the response data -useRequest(todoList, { - async middleware(_, next) { - return 'abc'; - } -}); -``` - -## More than that - -**So far, all we have mentioned is the use of the second parameter `next` of the middleware, so what is the first parameter for? ** - -The first parameter of the middleware contains some information about this request, as well as the control functions for the status and events returned in useHook such as `loading`, `data` and `onSuccess`. Let's move on! - -## Included request information - - - - -The following is the request information contained in the middleware of useRequest and useWatcher - -```javascript -async function alovaFrontMiddleware(context, next) { - // The method instance of this request - context.method; - - // The parameter array sent by the send function, the default is [] - context.sendArgs; - - // The cache data hit by this request - context.cachedResponse; - - // configuration collection of useHook - context.config; - - // The various states returned by useHook, including the following attributes - // loading, data, error, downloading, uploading, and additional states managed by managedStates - context.frontStates; - //... -} -``` - - - - -The following is the request information contained in the middleware of useFetcher - -```javascript -async function alovaFetcherMiddleware(context, next) { - // The method instance of this request - context.method; - - // The parameter group passed in by the fetch of useFetcher, the default is [] - context.fetchArgs; - - // The cache data hit by this request - context.cachedResponse; - - // configuration collection of useHook - context.config; - - // The various states returned by useHook, including the following attributes - // fetching, error, downloading, uploading - context.fetchStates; - //... -} -``` - - - - -Next, let's take a look at what controls are available. - -## Modify responsive data - -Use `context.update` to modify reactive data. - - - - -```javascript -async function alovaFrontMiddleware(context, next) { - context.update({ - // Modify the loading status to true in advance - loading: true, - - // Modify the data value, such as setting custom initialization data - data: { - /* ... */ - } - }); - //... -} -``` - - - - -```javascript -async function alovaFetcherMiddleware(context, next) { - context.update({ - // Modify the fetching status to true in advance - fetching: true, - - // Modify the value of error - error: new Error('custom midleware error') - }); - //... -} -``` - - - - -## Decorate events - -You can also decorate _onSuccess_, _onError_, _onComplete_ callback functions in middleware to make them richer, such as changing the parameters of the callback function, or receiving the return value of the callback function to achieve more functions. - -You can use `decorateSuccess`, `decorateError`, `decorateComplete` functions to decorate callback functions. The following takes the success callback as an example, which is decorated in 3 places: - -1. Added `custom` attribute to event object; -2. Added a second parameter to the success callback function, the value is `extra data`; -3. Receive the value of the second success callback function and print it; - -```javascript -const { onSuccess } = useRequest(todoList, { - //... - async middleware(context, next) { - // Decorate the successful callback function, the following function parameters are explained: - // handler: bound callback function - // event: the event object corresponding to the callback function - // index: The subscript of the callback function, indicating which callback function is currently being executed - // length: the number of callback functions bound - context.decorateSuccess((handler, event, index, length) => { - event.custom = 1; - const received = handler(event, 'extra data'); - if (index === 1) { - console.log(`received the return value of ${index + 1} callback function:`, received); - // [Print information] Received the return value of the second callback function: I'm second handler - } - }); - //... - } -}); -onSuccess((event, extra) => { - console.log(event.custom); // 1 - console.log(extra); // extra data -}); -onSuccess((event, extra) => { - return "I'm second handler"; -}); -``` - -The usage of `decorateError`, `decorateComplete` is the same as `decorateSuccess`. - -## Abort or repeat send request - -In the middleware, you can also receive `abort` and `send` functions returned by use hooks (`fetch` in useFetcher), and you can also send multiple requests when triggering a request intent. - -A typical usage example is request retry. After sending a request, if the request fails, it will automatically request again according to a certain strategy, and `onSuccess` will be triggered after the retry is successful. The following is a sample code for a simple request retry. - - - - -```javascript -async function alovaFrontMiddleware(context, next) { - return next().catch(error => { - if (needRetry) { - setTimeout(() => { - context.send(...context.sendArgs); - }, retryDelay); - } - return Promise.reject(error); - }); -} -``` - - - - -```javascript -async function alovaFetcherMiddleware(context, next) { - return next().catch(error => { - if (needRetry) { - setTimeout(() => { - context.fetch(context.method, ...context.fetchArgs); - }, retryDelay); - } - return Promise.reject(error); - }); -} -``` - - - - -If you need to abort the request inside the middleware, you can call `context.abort()`. - -## Controlled loading state - -In the above content, we know that you can customize and modify the responsive data through `context.update`, but when you modify the loading status value (`loading` or `fetching`), it will be hindered, because in normal circumstances Next, the loading status value will be automatically set to true when `next` is called, and false will be automatically set in the response process, which will overwrite the loading status value modified by `context.update`, at this time we can turn on the controlled loading status , after it is turned on, the `next` function and the response process will no longer modify the loading status value, but we have full control over it. - -Let's take request retry as an example. We hope that the loading status will remain true after the request is retried until the request ends. - - - - -In the middleware of useRequest and useWatcher, use `context.controlLoading` to enable custom control loading status. - -```javascript -async function alovaFrontMiddleware(context, next) { - context.controlLoading(); - - // Set to true when the request starts - context.update({ loading: true }); - return next() - .then(value => { - // set to false after successful request - context.update({ loading: false }); - return value; - }) - .catch(error => { - if (needRetry) { - setTimeout(() => { - context.send(...context.sendArgs); - }, retryDelay); - } else { - // Also set to false when not retrying again - context.update({ loading: false }); - } - return Promise.reject(error); - }); -} -``` - - - - -In the middleware of useFetching, use `context.controlFetching` to enable custom control loading state. - -```javascript -async function alovaFetcherMiddleware(context, next) { - context.controlFetching(); - - // Set to true when the request starts - context.update({ fetching: true }); - return next() - .then(value => { - // set to false after successful request - context.update({ fetching: false }); - return value; - }) - .catch(error => { - if (needRetry) { - setTimeout(() => { - context.fetch(context.method, ...context.fetchArgs); - }, retryDelay); - } else { - // Also set to false when not retrying again - context.update({ fetching: false }); - } - return Promise.reject(error); - }); -} -``` - - - +--- +title: Middleware +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +Request middleware is an asynchronous function. it provides a powerful ability to control almost all behaviors of a request. If you just use alova, then you probably don't need to use request middleware, because it is mainly used to complete custom request strategies, no matter simple or complex request strategies, you may use it, let's look at it next What magical powers does it have. + +## Middleware function + +Request middleware is an async function, You can define request middleware in `useRequest`, `useWatcher`, `useFetcher`. The following is a simple request middleware, which prints some information before and after the request without changing any request behavior. + +```javascript +useRequest(todoList, { + async middleware(_, next) { + console.log('before request'); + await next(); + console.log('after request'); + } +}); +``` + +Here are a few things you need to know about the `next` function call. This function is also an asynchronous function. Calling it can continue to send requests. At this time, the _loading_ state will be set to true, and then the request will be sent. The return value of next is a Promise instance with the response data, you can manipulate the return value in the middleware function. + +## Control response data + +The return value of the middleware function will be used as the response data of this request to participate in subsequent processing. If the middleware does not return any data but calls `next`, the response data of this request will be used for subsequent processing. + +```javascript +// The modified result will be used as the response data +useRequest(todoList, { + async middleware(_, next) { + const result = await next(); + result.code = 500; + return result; + } +}); + +// Will participate in subsequent processing with the response data of this request +useRequest(todoList, { + async middleware(_, next) { + await next(); + } +}); + +// will respond with the string abc +useRequest(todoList, { + async middleware(_, next) { + await next(); + return 'abc'; + } +}); +``` + +There is also a special case here. When `next` is not called and there is no return value, subsequent processing will not be performed, which means that _onSuccess_, _onError_, _onComplete_ response events will not be triggered. + +```javascript +useRequest(todoList, { + async middleware() {} +}); +``` + +## change request + +Sometimes you want to change the request. At this time, you can specify another method instance in `next`, and the information in this method will be requested when sending the request. At the same time, you can also set whether to force the request through `next` Penetrating the cache is also very simple. + +```javascript +useRequest(todoList, { + async middleware(_, next) { + await next({ + // Change the requested method instance + method: newMethodInstance, + + // Whether to force the request this time + force: true + }); + } +}); +``` + +## Control errors + +### Catch errors + +In the middleware, you can capture the request error generated in `next`, after capturing, the global `onError` hook will no longer be triggered. + +```javascript +useRequest(todoList, { + async middleware(_, next) { + try { + await next(); + } catch (e) { + console.error('Error caught', e); + } + } +}); +``` + +### Throw an error + +Of course, you can also throw a custom error in the middleware, even if the request is normal, it will enter the request error process. + +```javascript +// No request is sent, and global and request-level onError will be triggered at the same time. If the request is sent through `method.send`, the promise instance of rejection will be returned +useRequest(todoList, { + async middleware(_, next) { + throw new Error('error on before request'); + await next(); + } +}); + +// After request is success, global and request-level onError will be triggered at the same time. If the request is sent through `method.send`, the promise instance of rejection will be returned +useRequest(todoList, { + async middleware(_, next) { + await next(); + throw new Error('error on after request'); + } +}); +``` + +## Control response delay + +In the middleware, we can delay the response or respond in advance. In the case of advance, although the response data cannot be obtained, some other data can be returned as the response data to participate in subsequent processing. + +```javascript +// Delay response for 1 second +useRequest(todoList, { + async middleware(_, next) { + await new Promise(resolve => { + setTimeout(resolve, 1000); + }); + return next(); + } +}); + +// Respond immediately and use the string abc as the response data +useRequest(todoList, { + async middleware(_, next) { + return 'abc'; + } +}); +``` + +## More than that + +**So far, all we have mentioned is the use of the second parameter `next` of the middleware, so what is the first parameter for? ** + +The first parameter of the middleware contains some information about this request, as well as the control functions for the status and events returned in useHook such as `loading`, `data` and `onSuccess`. Let's move on! + +## Included request information + + + + +The following is the request information contained in the middleware of useRequest and useWatcher + +```javascript +async function alovaFrontMiddleware(context, next) { + // The method instance of this request + context.method; + + // The parameter array sent by the send function, the default is [] + context.sendArgs; + + // The cache data hit by this request + context.cachedResponse; + + // configuration collection of useHook + context.config; + + // The various states returned by useHook, including the following attributes + // loading, data, error, downloading, uploading, and additional states managed by managedStates + context.frontStates; + //... +} +``` + + + + +The following is the request information contained in the middleware of useFetcher + +```javascript +async function alovaFetcherMiddleware(context, next) { + // The method instance of this request + context.method; + + // The parameter group passed in by the fetch of useFetcher, the default is [] + context.fetchArgs; + + // The cache data hit by this request + context.cachedResponse; + + // configuration collection of useHook + context.config; + + // The various states returned by useHook, including the following attributes + // fetching, error, downloading, uploading + context.fetchStates; + //... +} +``` + + + + +Next, let's take a look at what controls are available. + +## Modify responsive data + +Use `context.update` to modify reactive data. + + + + +```javascript +async function alovaFrontMiddleware(context, next) { + context.update({ + // Modify the loading status to true in advance + loading: true, + + // Modify the data value, such as setting custom initialization data + data: { + /* ... */ + } + }); + //... +} +``` + + + + +```javascript +async function alovaFetcherMiddleware(context, next) { + context.update({ + // Modify the fetching status to true in advance + fetching: true, + + // Modify the value of error + error: new Error('custom midleware error') + }); + //... +} +``` + + + + +## Decorate events + +You can also decorate _onSuccess_, _onError_, _onComplete_ callback functions in middleware to make them richer, such as changing the parameters of the callback function, or receiving the return value of the callback function to achieve more functions. + +You can use `decorateSuccess`, `decorateError`, `decorateComplete` functions to decorate callback functions. The following takes the success callback as an example, which is decorated in 3 places: + +1. Added `custom` attribute to event object; +2. Added a second parameter to the success callback function, the value is `extra data`; +3. Receive the value of the second success callback function and print it; + +```javascript +const { onSuccess } = useRequest(todoList, { + //... + async middleware(context, next) { + // Decorate the successful callback function, the following function parameters are explained: + // handler: bound callback function + // event: the event object corresponding to the callback function + // index: The subscript of the callback function, indicating which callback function is currently being executed + // length: the number of callback functions bound + context.decorateSuccess((handler, event, index, length) => { + event.custom = 1; + const received = handler(event, 'extra data'); + if (index === 1) { + console.log(`received the return value of ${index + 1} callback function:`, received); + // [Print information] Received the return value of the second callback function: I'm second handler + } + }); + //... + } +}); +onSuccess((event, extra) => { + console.log(event.custom); // 1 + console.log(extra); // extra data +}); +onSuccess((event, extra) => { + return "I'm second handler"; +}); +``` + +The usage of `decorateError`, `decorateComplete` is the same as `decorateSuccess`. + +## Abort or repeat send request + +In the middleware, you can also receive `abort` and `send` functions returned by use hooks (`fetch` in useFetcher), and you can also send multiple requests when triggering a request intent. + +A typical usage example is request retry. After sending a request, if the request fails, it will automatically request again according to a certain strategy, and `onSuccess` will be triggered after the retry is successful. The following is a sample code for a simple request retry. + + + + +```javascript +async function alovaFrontMiddleware(context, next) { + return next().catch(error => { + if (needRetry) { + setTimeout(() => { + context.send(...context.sendArgs); + }, retryDelay); + } + return Promise.reject(error); + }); +} +``` + + + + +```javascript +async function alovaFetcherMiddleware(context, next) { + return next().catch(error => { + if (needRetry) { + setTimeout(() => { + context.fetch(context.method, ...context.fetchArgs); + }, retryDelay); + } + return Promise.reject(error); + }); +} +``` + + + + +If you need to abort the request inside the middleware, you can call `context.abort()`. + +## Controlled loading state + +In the above content, we know that you can customize and modify the responsive data through `context.update`, but when you modify the loading status value (`loading` or `fetching`), it will be hindered, because in normal circumstances Next, the loading status value will be automatically set to true when `next` is called, and false will be automatically set in the response process, which will overwrite the loading status value modified by `context.update`, at this time we can turn on the controlled loading status , after it is turned on, the `next` function and the response process will no longer modify the loading status value, but we have full control over it. + +Let's take request retry as an example. We hope that the loading status will remain true after the request is retried until the request ends. + + + + +In the middleware of useRequest and useWatcher, use `context.controlLoading` to enable custom control loading status. + +```javascript +async function alovaFrontMiddleware(context, next) { + context.controlLoading(); + + // Set to true when the request starts + context.update({ loading: true }); + return next() + .then(value => { + // set to false after successful request + context.update({ loading: false }); + return value; + }) + .catch(error => { + if (needRetry) { + setTimeout(() => { + context.send(...context.sendArgs); + }, retryDelay); + } else { + // Also set to false when not retrying again + context.update({ loading: false }); + } + return Promise.reject(error); + }); +} +``` + + + + +In the middleware of useFetching, use `context.controlFetching` to enable custom control loading state. + +```javascript +async function alovaFetcherMiddleware(context, next) { + context.controlFetching(); + + // Set to true when the request starts + context.update({ fetching: true }); + return next() + .then(value => { + // set to false after successful request + context.update({ fetching: false }); + return value; + }) + .catch(error => { + if (needRetry) { + setTimeout(() => { + context.fetch(context.method, ...context.fetchArgs); + }, retryDelay); + } else { + // Also set to false when not retrying again + context.update({ fetching: false }); + } + return Promise.reject(error); + }); +} +``` + + + diff --git a/versioned_docs/version-2.x/tutorial/06-advanced/05-custom-method-key.md b/versioned_docs/version-2.x/tutorial/06-advanced/05-custom-method-key.md index 031585771..a4dbccb56 100644 --- a/versioned_docs/version-2.x/tutorial/06-advanced/05-custom-method-key.md +++ b/versioned_docs/version-2.x/tutorial/06-advanced/05-custom-method-key.md @@ -1,26 +1,25 @@ ---- -title: custom method key -sidebar_position: 50 ---- - -:::info version required - -v2.20.0+ - -::: - -Method key is used to identify all data associated with method instances and has a great effect, for example: - -- Caching of associated response data -- Identity sharing request - -Associate the status value returned by useRequest and other useHook - -By default, the method key is generated from the relevant request parameters of the method instance, which can accurately identify a request. - -But sometimes you want to change it so that the above three situations can be recognized as the same method in different requests. - -```javascript -//The method key is generated when creating, you can customize it through __key__ -const methodInst = alovaInstance.Get('/api/user', {}); -methodInst.__key__ = 'my-custom-method-key'; -``` +--- +title: custom method key +--- + +:::info version required + +v2.20.0+ + +::: + +Method key is used to identify all data associated with method instances and has a great effect, for example: + +- Caching of associated response data +- Identity sharing request + -Associate the status value returned by useRequest and other useHook + +By default, the method key is generated from the relevant request parameters of the method instance, which can accurately identify a request. + +But sometimes you want to change it so that the above three situations can be recognized as the same method in different requests. + +```javascript +//The method key is generated when creating, you can customize it through __key__ +const methodInst = alovaInstance.Get('/api/user', {}); +methodInst.__key__ = 'my-custom-method-key'; +``` diff --git a/versioned_docs/version-2.x/tutorial/06-advanced/06-error-logger.md b/versioned_docs/version-2.x/tutorial/06-advanced/06-error-logger.md index 7e96a6998..9c7b2da6a 100644 --- a/versioned_docs/version-2.x/tutorial/06-advanced/06-error-logger.md +++ b/versioned_docs/version-2.x/tutorial/06-advanced/06-error-logger.md @@ -1,37 +1,36 @@ ---- -title: Error logger -sidebar_position: 60 ---- - -:::info version required - -v2.6.0+ - -::: - -For the convenience of debugging, when using use hooks to request or respond to an error, the error logger will be printed on the console by default. If you do not want to print an error message or customize the control to print an error message in some cases (such as a production environment), alova also Support for them is provided. - -## Disable error logger - -Log printing can be turned off by setting `errorLogger` to `false or null` when creating an alova instance. - -```javascript -const alovaInstance = createAlova({ - //... - errorLogger: false -}); -``` - -## Custom print error logger - -The error logger is printed by `console.error` by default. If `console.error` is not supported in your project environment, or if you want to collect error information, you can specify `errorLogger` as a function to customize error logger. - -```javascript -const alovaInstance = createAlova({ - //... - // error is the error instance, methodInstance is the method instance corresponding to the error - errorLogger(error, methodInstance) { - reportError(`${methodInstance.url}: ${error.message}`); - } -}); -``` +--- +title: Error logger +--- + +:::info version required + +v2.6.0+ + +::: + +For the convenience of debugging, when using use hooks to request or respond to an error, the error logger will be printed on the console by default. If you do not want to print an error message or customize the control to print an error message in some cases (such as a production environment), alova also Support for them is provided. + +## Disable error logger + +Log printing can be turned off by setting `errorLogger` to `false or null` when creating an alova instance. + +```javascript +const alovaInstance = createAlova({ + //... + errorLogger: false +}); +``` + +## Custom print error logger + +The error logger is printed by `console.error` by default. If `console.error` is not supported in your project environment, or if you want to collect error information, you can specify `errorLogger` as a function to customize error logger. + +```javascript +const alovaInstance = createAlova({ + //... + // error is the error instance, methodInstance is the method instance corresponding to the error + errorLogger(error, methodInstance) { + reportError(`${methodInstance.url}: ${error.message}`); + } +}); +``` diff --git a/versioned_docs/version-2.x/tutorial/06-advanced/07-cache-logger.md b/versioned_docs/version-2.x/tutorial/06-advanced/07-cache-logger.md index 311c0d67a..afc6086e9 100644 --- a/versioned_docs/version-2.x/tutorial/06-advanced/07-cache-logger.md +++ b/versioned_docs/version-2.x/tutorial/06-advanced/07-cache-logger.md @@ -1,60 +1,59 @@ ---- -title: cache logger -sidebar_position: 70 ---- - -:::info version required - -v2.8.0+ - -::: - -In order to facilitate debugging when using the interface cache, when the request hits the cache without sending a network request, the hit cache information will be printed on the console by default, which can solve some confusion when using the cache. - -If you don't want to print cache information or custom control print cache information in some cases (such as production environment), alova also provides support for them. - -## Close cache logger printing - -Console printing can be turned off by setting `cacheLogger` to `false or null` when creating an alova instance. - -```javascript -const alovaInstance = createAlova({ - //... - cacheLogger: false -}); -``` - -You can also dynamically turn it on and off according to different environments. - -```javascript -const alovaInstance = createAlova({ - //... - // Enable cache logger in the development environment - cacheLogger: process.env.NODE_ENV === 'development' -}); -``` - -## Custom print cache logger - -The cache logger is printed via `console.log` by default. If `console.log` or other purposes are not supported in your project environment, `cacheLogger` can be specified as a function to customize the logger for processing cache hits. - -```javascript -const alovaInstance = createAlova({ - //... - /** - * Custom cache logger function - * @param response hit cache data - * @param method the current method instance - * @param cacheMode cache mode memory or restore - * @param tag The tag in the restore mode has a value only when the tag is set in the corresponding cache - */ - cacheLogger(response, method, cacheMode, tag) { - saveHitCache({ - response, - method, - cacheMode, - tag - }); - } -}); -``` +--- +title: cache logger +--- + +:::info version required + +v2.8.0+ + +::: + +In order to facilitate debugging when using the interface cache, when the request hits the cache without sending a network request, the hit cache information will be printed on the console by default, which can solve some confusion when using the cache. + +If you don't want to print cache information or custom control print cache information in some cases (such as production environment), alova also provides support for them. + +## Close cache logger printing + +Console printing can be turned off by setting `cacheLogger` to `false or null` when creating an alova instance. + +```javascript +const alovaInstance = createAlova({ + //... + cacheLogger: false +}); +``` + +You can also dynamically turn it on and off according to different environments. + +```javascript +const alovaInstance = createAlova({ + //... + // Enable cache logger in the development environment + cacheLogger: process.env.NODE_ENV === 'development' +}); +``` + +## Custom print cache logger + +The cache logger is printed via `console.log` by default. If `console.log` or other purposes are not supported in your project environment, `cacheLogger` can be specified as a function to customize the logger for processing cache hits. + +```javascript +const alovaInstance = createAlova({ + //... + /** + * Custom cache logger function + * @param response hit cache data + * @param method the current method instance + * @param cacheMode cache mode memory or restore + * @param tag The tag in the restore mode has a value only when the tag is set in the corresponding cache + */ + cacheLogger(response, method, cacheMode, tag) { + saveHitCache({ + response, + method, + cacheMode, + tag + }); + } +}); +``` diff --git a/versioned_docs/version-2.x/tutorial/06-advanced/08-manage-extra-states.md b/versioned_docs/version-2.x/tutorial/06-advanced/08-manage-extra-states.md index 8de90c559..02eb05adc 100644 --- a/versioned_docs/version-2.x/tutorial/06-advanced/08-manage-extra-states.md +++ b/versioned_docs/version-2.x/tutorial/06-advanced/08-manage-extra-states.md @@ -1,198 +1,197 @@ ---- -title: Manage extra states -sidebar_position: 80 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -In the previous [Cross Page/Module Update Response States](/tutorial/advanced/update-across-components) chapter, we introduced how to update the response status across pages or modules, but in this chapter we It only introduces updating the `data` state returned by `useRequest` and `useWatcher` through `updateState`, the value of data is always consistent with the response data, but in many cases we will use additional states(such as state A) to display data, and After the request is successful, the data data is appended to the additional state A, such as the pagination scheme of pull-down loading. In this case, we need to manage the additional state A so that it can be updated across pages/modules. - -## Update a single state - -Additional states can be managed via `managedStates` when called by the use hook, and automatically assigned the state name to update it when `updateState` is called in other modules/pages. - - - - -```javascript title="A.vue" -const todoList = page => - alova.Get('/todo', { - name: 'todoList' - }); - -const allTodo = ref([]); -useRequest(todoList, { - //... - - // highlight-start - // manage allTodo as additional state - managedStates: { - allTodo - } - // highlight-end -}); -``` - -```javascript title="B.vue" -const handleSuccess = () => { - // highlight-start - // Pass in an object and specify the state name to look up - updateState('todoList', { - allTodo: allTodoData => { - // Add a new todo item - allTodoData.push({ - title: 'new todo', - time: '10:00' - }); - return allTodoData; - } - }); - // highlight-end -}; -``` - - - - - -```javascript title="A.jsx" -const PageA = () => { - const todoList = page => - alova.Get('/todo', { - name: 'todoList' - }); - - const [allTodo, setAllTodo] = allTodoState = useState([]); - useRequest(todoList, { - //... - - // highlight-start - // manage allTodo as additional state - managedStates: { - allTodo: allTodoState - } - // highlight-end - }); - - return ( - //... - ); -} -``` - -```javascript title="B.jsx" -const PageB = () => { - //... - const handleSuccess = () => { - // highlight-start - // Pass in an object and specify the state name to look up - updateState('todoList', { - allTodo: allTodoData => { - // Add a new todo item - allTodoData.push({ - title: 'new todo', - time: '10:00' - }); - return allTodoData; - } - }); - // highlight-end - }; - - return ( - //... - ); -} -``` - - - - - -```javascript title="A.svelte" -const todoList = page => - alova.Get('/todo', { - name: 'todoList' - }); - -const allTodo = ref([]); -useRequest(todoList, { - //... - - // highlight-start - // manage allTodo as additional state - managedStates: { - allTodo - } - // highlight-end -}); -``` - -```javascript title="B.svelte" -const handleSuccess = () => { - // highlight-start - // Pass in an object and specify the state name to look up - updateState('todoList', { - allTodo: allTodoData => { - // Add a new todo item - allTodoData.push({ - title: 'new todo', - time: '10:00' - }); - return allTodoData; - } - }); - // highlight-end -}; -``` - - - - -:::info Note - -Not support to manage additional states. - -::: - - - - -## Update multiple states - -In the above example, we implemented the update of a single `allTodo` state across pages. In fact, any number of states can be updated at the same time through the object description method of `updateState`. - -```javascript -updateState('todoList', { - state1: state1Data => { - //... - }, - state2: state2Data => { - //... - }, - state3: state3Data => { - //... - } - //... -}); -``` - -It should be noted that the above 3 additional states need to be managed through the `managedStates` property before updating. - -## shorthand for data status update - -When only updating the data state, you can directly pass in the callback function instead of specifying it as an object. - -```javascript -updateState('todoList', { - data: dataRaw => { - //... - } -}); - -// The following are shorthand -updateState('todoList', dataRaw => { - //... -}); -``` +--- +title: Manage extra states +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +In the previous [Cross Page/Module Update Response States](/tutorial/advanced/update-across-components) chapter, we introduced how to update the response status across pages or modules, but in this chapter we It only introduces updating the `data` state returned by `useRequest` and `useWatcher` through `updateState`, the value of data is always consistent with the response data, but in many cases we will use additional states(such as state A) to display data, and After the request is successful, the data data is appended to the additional state A, such as the pagination scheme of pull-down loading. In this case, we need to manage the additional state A so that it can be updated across pages/modules. + +## Update a single state + +Additional states can be managed via `managedStates` when called by the use hook, and automatically assigned the state name to update it when `updateState` is called in other modules/pages. + + + + +```javascript title="A.vue" +const todoList = page => + alova.Get('/todo', { + name: 'todoList' + }); + +const allTodo = ref([]); +useRequest(todoList, { + //... + + // highlight-start + // manage allTodo as additional state + managedStates: { + allTodo + } + // highlight-end +}); +``` + +```javascript title="B.vue" +const handleSuccess = () => { + // highlight-start + // Pass in an object and specify the state name to look up + updateState('todoList', { + allTodo: allTodoData => { + // Add a new todo item + allTodoData.push({ + title: 'new todo', + time: '10:00' + }); + return allTodoData; + } + }); + // highlight-end +}; +``` + + + + + +```javascript title="A.jsx" +const PageA = () => { + const todoList = page => + alova.Get('/todo', { + name: 'todoList' + }); + + const [allTodo, setAllTodo] = allTodoState = useState([]); + useRequest(todoList, { + //... + + // highlight-start + // manage allTodo as additional state + managedStates: { + allTodo: allTodoState + } + // highlight-end + }); + + return ( + //... + ); +} +``` + +```javascript title="B.jsx" +const PageB = () => { + //... + const handleSuccess = () => { + // highlight-start + // Pass in an object and specify the state name to look up + updateState('todoList', { + allTodo: allTodoData => { + // Add a new todo item + allTodoData.push({ + title: 'new todo', + time: '10:00' + }); + return allTodoData; + } + }); + // highlight-end + }; + + return ( + //... + ); +} +``` + + + + + +```javascript title="A.svelte" +const todoList = page => + alova.Get('/todo', { + name: 'todoList' + }); + +const allTodo = ref([]); +useRequest(todoList, { + //... + + // highlight-start + // manage allTodo as additional state + managedStates: { + allTodo + } + // highlight-end +}); +``` + +```javascript title="B.svelte" +const handleSuccess = () => { + // highlight-start + // Pass in an object and specify the state name to look up + updateState('todoList', { + allTodo: allTodoData => { + // Add a new todo item + allTodoData.push({ + title: 'new todo', + time: '10:00' + }); + return allTodoData; + } + }); + // highlight-end +}; +``` + + + + +:::info Note + +Not support to manage additional states. + +::: + + + + +## Update multiple states + +In the above example, we implemented the update of a single `allTodo` state across pages. In fact, any number of states can be updated at the same time through the object description method of `updateState`. + +```javascript +updateState('todoList', { + state1: state1Data => { + //... + }, + state2: state2Data => { + //... + }, + state3: state3Data => { + //... + } + //... +}); +``` + +It should be noted that the above 3 additional states need to be managed through the `managedStates` property before updating. + +## shorthand for data status update + +When only updating the data state, you can directly pass in the callback function instead of specifying it as an object. + +```javascript +updateState('todoList', { + data: dataRaw => { + //... + } +}); + +// The following are shorthand +updateState('todoList', dataRaw => { + //... +}); +``` diff --git a/versioned_docs/version-2.x/tutorial/06-advanced/09-ssr.md b/versioned_docs/version-2.x/tutorial/06-advanced/09-ssr.md index e2a09ab2e..7ab3625b6 100644 --- a/versioned_docs/version-2.x/tutorial/06-advanced/09-ssr.md +++ b/versioned_docs/version-2.x/tutorial/06-advanced/09-ssr.md @@ -1,228 +1,227 @@ ---- -title: Server-Side Rendering(SSR) -sidebar_position: 90 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -:::info version required - -2.8.0+ - -::: - -## Overview - -Although the positioning of alova is not to make requests in nodejs, we have also adapted it in order to combine the server-side rendering([Nuxt3.x](https://nuxt.com/) / [Nextjs](https://nextjs.org/) / [sveltekit](https://kit.svelte.dev/)) of the UI framework. Although built-in request functionality is provided in e.g. `Nuxt3.x`, `Sveltekit`, if you choose to use alova, you can use alova to manage requests in both server and client, instead of server and client separately. Use different request schemes to manage them. - -Here are some caveats for using alova in SSR, and examples of usage in SSR for different UI frameworks. - -## Call apis on server - -In SSR, it is necessary to get data on server and render it into HTML. In this case, we cannot use alova's use hooks (and do not need to use them) to obtain data. Below we will show the supported SSR frameworks respectively. - -### Nuxt3.x - -In Nuxt3.x, `useAsyncData` is provided to initialize page data on server, and `useFetch` and `$fetch` request functions are also provided. These request functions that can be used on both server and client are really convenient. However, if you want to use alova in nuxt, you can use the combination of **useAsyncData + alova.Method** to complete the server-side data fetching, which is no different from your usual `useAsyncData`. - -```html - -``` - -### Nextjs - -Nextjs provides fixed server-side initialization page data functions, such as `getStaticProps`, `getServerSideProps`, etc., you can [directly use the method instance](/tutorial/getting-started/quick-start) call apis in the function. - -```jsx -const todoListGetter = alovaInstance.Get('/todo/list', { - headers: { - 'Content-Type': 'application/json;charset=UTF-8' - } -}); - -export const getServerSideProps = async ctx => { - const list = await todoListGetter.send(); - return { - props: { - list - } - }; -}; -export default function App(props) { - return props.list.map(item => ( -
- {item.title} - {item.time} -
- )); -} -``` - -### Sveltekit - -Sveltekit also provides the `load` function to initialize the page data on server, and you can also [directly use the method instance](/tutorial/getting-started/quick-start) call apis in the function. For example, call apis in `+page.server.js`. - -```javascript title=+page.server.js -const todoListGetter = alovaInstance.Get('/todo/list', { - headers: { - 'Content-Type': 'application/json;charset=UTF-8' - } -}); - -/** @type {import('./$types').PageServerLoad} */ -export async function load({ params }) { - return { - list: todoListGetter.send() - }; -} -``` - -## Using usehooks in SSR - -Since each SSR framework has its own way to initialize data on server, when generating html in SSR, `useRequest` and `useWatcher` in the component will not be initiated even if `immediate` is set to `true` request, as this is more like client initialization data. - -However, if you need to initialize the data of the page as in the client, you can also set `immediate` to `true`, and when the page is running in the browser, you can use all the functions of alova as usual. - -## Precautions - -### Client and server caches are separate - -If you use alova's caching function, you may need to pay attention here that the client and server caches are not shared, which means that if you directly use **usehooks** to get data when initializing the page, you may Run into inconsistencies in client-side and server-side rendering, although few people do. - -Please see the following code snippet. - - - - -```html - - - -``` - - - - -```jsx -function App(props) { - const { loading, data } = useRequest(alovaGetter); - return ( - <> - {loading ?
loading
: null} -
{data}
- - ); -} -``` - -
- - -```html - - -{#if $loading} -
loading
-{/if} -
{{ data }}
-``` - -
-
- -The following code assumes that `alovaGetter` requests are cached on server, but not on the client. - -At this time, when the html is generated on server , `loading` is `false` and does not display `
loading
` because it hits the cache, but when the client is initialized, because it misses the cache, `loading` is `true` will cause `
loading
` to be displayed, and the SSR framework will prompt that the rendering of the two ends is inconsistent. - -**Solution** - -1. Try to put the page data initialization work in the acquisition function instead of the component; -2. If you must do this, you can avoid using the same apis on the client and server, or turn off the problematic api caches; -3. If caching is also required, you can clear the cache on server in the data initialization function of server. The sample code is as follows: - - - - -```html - - - -``` - - - - -```jsx -import { invalidateCache } from 'alova'; - -function App(props) { - const { loading, data } = useRequest(alovaGetter); - return ( - <> - {loading ?
loading
: null} -
{data}
- - ); -} - -export const getServerSideProps = async () => { - // Clear the cache on server - invalidateCache(alovaGetter); - return { - props: {} - }; -}; -``` - -
- - -```javascript title=+page.server.js -import { invalidateCache } from 'alova'; - -/** @type {import('./$types').PageServerLoad} */ -export async function load({ params }) { - // Clear the cache on server - invalidateCache(alovaGetter); - return {}; -} -``` - - -
+--- +title: Server-Side Rendering(SSR) +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info version required + +2.8.0+ + +::: + +## Overview + +Although the positioning of alova is not to make requests in nodejs, we have also adapted it in order to combine the server-side rendering([Nuxt3.x](https://nuxt.com/) / [Nextjs](https://nextjs.org/) / [sveltekit](https://kit.svelte.dev/)) of the UI framework. Although built-in request functionality is provided in e.g. `Nuxt3.x`, `Sveltekit`, if you choose to use alova, you can use alova to manage requests in both server and client, instead of server and client separately. Use different request schemes to manage them. + +Here are some caveats for using alova in SSR, and examples of usage in SSR for different UI frameworks. + +## Call apis on server + +In SSR, it is necessary to get data on server and render it into HTML. In this case, we cannot use alova's use hooks (and do not need to use them) to obtain data. Below we will show the supported SSR frameworks respectively. + +### Nuxt3.x + +In Nuxt3.x, `useAsyncData` is provided to initialize page data on server, and `useFetch` and `$fetch` request functions are also provided. These request functions that can be used on both server and client are really convenient. However, if you want to use alova in nuxt, you can use the combination of **useAsyncData + alova.Method** to complete the server-side data fetching, which is no different from your usual `useAsyncData`. + +```html + +``` + +### Nextjs + +Nextjs provides fixed server-side initialization page data functions, such as `getStaticProps`, `getServerSideProps`, etc., you can [directly use the method instance](/tutorial/getting-started/quick-start) call apis in the function. + +```jsx +const todoListGetter = alovaInstance.Get('/todo/list', { + headers: { + 'Content-Type': 'application/json;charset=UTF-8' + } +}); + +export const getServerSideProps = async ctx => { + const list = await todoListGetter.send(); + return { + props: { + list + } + }; +}; +export default function App(props) { + return props.list.map(item => ( +
+ {item.title} + {item.time} +
+ )); +} +``` + +### Sveltekit + +Sveltekit also provides the `load` function to initialize the page data on server, and you can also [directly use the method instance](/tutorial/getting-started/quick-start) call apis in the function. For example, call apis in `+page.server.js`. + +```javascript title=+page.server.js +const todoListGetter = alovaInstance.Get('/todo/list', { + headers: { + 'Content-Type': 'application/json;charset=UTF-8' + } +}); + +/** @type {import('./$types').PageServerLoad} */ +export async function load({ params }) { + return { + list: todoListGetter.send() + }; +} +``` + +## Using usehooks in SSR + +Since each SSR framework has its own way to initialize data on server, when generating html in SSR, `useRequest` and `useWatcher` in the component will not be initiated even if `immediate` is set to `true` request, as this is more like client initialization data. + +However, if you need to initialize the data of the page as in the client, you can also set `immediate` to `true`, and when the page is running in the browser, you can use all the functions of alova as usual. + +## Precautions + +### Client and server caches are separate + +If you use alova's caching function, you may need to pay attention here that the client and server caches are not shared, which means that if you directly use **usehooks** to get data when initializing the page, you may Run into inconsistencies in client-side and server-side rendering, although few people do. + +Please see the following code snippet. + + + + +```html + + + +``` + + + + +```jsx +function App(props) { + const { loading, data } = useRequest(alovaGetter); + return ( + <> + {loading ?
loading
: null} +
{data}
+ + ); +} +``` + +
+ + +```html + + +{#if $loading} +
loading
+{/if} +
{{ data }}
+``` + +
+
+ +The following code assumes that `alovaGetter` requests are cached on server, but not on the client. + +At this time, when the html is generated on server , `loading` is `false` and does not display `
loading
` because it hits the cache, but when the client is initialized, because it misses the cache, `loading` is `true` will cause `
loading
` to be displayed, and the SSR framework will prompt that the rendering of the two ends is inconsistent. + +**Solution** + +1. Try to put the page data initialization work in the acquisition function instead of the component; +2. If you must do this, you can avoid using the same apis on the client and server, or turn off the problematic api caches; +3. If caching is also required, you can clear the cache on server in the data initialization function of server. The sample code is as follows: + + + + +```html + + + +``` + + + + +```jsx +import { invalidateCache } from 'alova'; + +function App(props) { + const { loading, data } = useRequest(alovaGetter); + return ( + <> + {loading ?
loading
: null} +
{data}
+ + ); +} + +export const getServerSideProps = async () => { + // Clear the cache on server + invalidateCache(alovaGetter); + return { + props: {} + }; +}; +``` + +
+ + +```javascript title=+page.server.js +import { invalidateCache } from 'alova'; + +/** @type {import('./$types').PageServerLoad} */ +export async function load({ params }) { + // Clear the cache on server + invalidateCache(alovaGetter); + return {}; +} +``` + + +
diff --git a/versioned_docs/version-2.x/tutorial/07-best-practice/01-manage-apis.md b/versioned_docs/version-2.x/tutorial/07-best-practice/01-manage-apis.md index 54127698e..97e6be845 100644 --- a/versioned_docs/version-2.x/tutorial/07-best-practice/01-manage-apis.md +++ b/versioned_docs/version-2.x/tutorial/07-best-practice/01-manage-apis.md @@ -1,154 +1,153 @@ ---- -title: Manage APIs -sidebar_position: 10 ---- - -In a project, we may need to use hundreds or thousands of request APIs, so managing these request APIs becomes particularly important. - -You may write the request code like the code snippet in [quick start](/tutorial/getting-started/quick-start). all codes in one file. - -```javascript -const { loading, data, error } = useRequest( - alovaInstance.Get('https://api.alovajs.org/profile', { - params: { - id: 1 - } - }) -); -``` - -This is just for beginners to understand, but in actual projects, we do not recommend this, because the method instance is not only used to send requests, it can also be used to operate cache and state, the above usage will make these request api become It's unmanageable, and if you think it's wrong, you might forget a little: - -> The key of the response data cache is uniquely identified by the combination of the method instance’s request method (method), request address (url), request header parameters (headers), url parameters (params), and request body parameters (requestBody). Or different positions will be treated as different keys. - -Therefore, in actual projects, method instances should be managed, and alova instances can also be managed uniformly. - -## api file structure - -First of all, your project needs a folder that uniformly stores method instances and alova instances, for example called `api`, the following is a common api management structure, and you can also use any structure suitable for the project. - -``` -|-api -| |-index.js -> contains all alova instances -| |-methods -| | |-user.js -| | |-article.js -| | |-order.js -| | |-... -|-... -``` - -In short, your project should use a suitable folder structure to organize them. - -> Next, take vue as an example to show the sample code - -## Manage alova instance - -Your project may need to communicate with different servers, or you may need to use special request schemes in specific requests, or use different response interceptors, etc. All of these require creating and maintaining multiple alova instances in the project. It is recommended to Use a separate file to manage them, for example in the above api management structure, will use `api/index.js` to manage. - -```javascript title=api/index.js -import { createAlova } from 'alova'; -import VueHook from 'alova/vue'; -import GlobalFetch from 'alova/GlobalFetch'; -import { axiosRequestAdapter } from '@alova/adapter-axios'; - -// user alova instance -export const userAlova = createAlova({ - baseURL: 'https://api-user.alovajs.org', - statesHook: VueHook, - requestAdapter: GlobalFetch(), - async responded(method) { - method.config.headers.token = 'user token'; - } -}); - -// order alova instance -export const orderAlova = createAlova({ - baseURL: 'https://api-order.alovajs.org', - statesHook: VueHook, - requestAdapter: GlobalFetch(), - async responded(method) { - method.config.headers.token = 'order token'; - } -}); - -// upload alova instance -export const uploadAlova = createAlova({ - baseURL: 'https://api-order.alovajs.org', - statesHook: VueHook, - requestAdapter: axiosRequestAdapter() -}); -``` - -## Manage method instances - -We can use different js files to classify and manage method instances. For example, in the above api management structure, `api/methods/user.js` will be used to manage method instances related to user information, and `api/methods/order.js` will be used `A method instance related to order management. - -In addition, as mentioned above, in addition to sending requests, method instances can also be used to operate caches and states. In order to ensure the number and order of request parameters, we can use a function to correspond to a request API, through The corresponding method instance is returned in the form of incoming request parameters. As long as the incoming parameters are the same, the request information and parameter order of the method instance are also the same, so as to ensure that the method instance used to operate the cache and state is correct. - -```javascript title=api/methods/user.js -import { userAlova } from '..'; - -// Get user information -export const getUserInfo = id => userAlova.Get('/user/' + id); - -// Edit user information -export const editUserInfo = (name, age, mobile) => - userAlova.Post('/user', { - name, - age, - mobile - }); - -// remove user -export const removeUser = id => userAlova.Delete('/user/' + id); - -//... -``` - -In the **user component**, the method function can be directly imported for use, and the method function can be used again to invalidate the corresponding cache after calling `invalidateCache`. - -```html title=views/user.vue - - -``` +--- +title: Manage APIs +--- + +In a project, we may need to use hundreds or thousands of request APIs, so managing these request APIs becomes particularly important. + +You may write the request code like the code snippet in [quick start](/tutorial/getting-started/quick-start). all codes in one file. + +```javascript +const { loading, data, error } = useRequest( + alovaInstance.Get('https://api.alovajs.org/profile', { + params: { + id: 1 + } + }) +); +``` + +This is just for beginners to understand, but in actual projects, we do not recommend this, because the method instance is not only used to send requests, it can also be used to operate cache and state, the above usage will make these request api become It's unmanageable, and if you think it's wrong, you might forget a little: + +> The key of the response data cache is uniquely identified by the combination of the method instance’s request method (method), request address (url), request header parameters (headers), url parameters (params), and request body parameters (requestBody). Or different positions will be treated as different keys. + +Therefore, in actual projects, method instances should be managed, and alova instances can also be managed uniformly. + +## api file structure + +First of all, your project needs a folder that uniformly stores method instances and alova instances, for example called `api`, the following is a common api management structure, and you can also use any structure suitable for the project. + +``` +|-api +| |-index.js -> contains all alova instances +| |-methods +| | |-user.js +| | |-article.js +| | |-order.js +| | |-... +|-... +``` + +In short, your project should use a suitable folder structure to organize them. + +> Next, take vue as an example to show the sample code + +## Manage alova instance + +Your project may need to communicate with different servers, or you may need to use special request schemes in specific requests, or use different response interceptors, etc. All of these require creating and maintaining multiple alova instances in the project. It is recommended to Use a separate file to manage them, for example in the above api management structure, will use `api/index.js` to manage. + +```javascript title=api/index.js +import { createAlova } from 'alova'; +import VueHook from 'alova/vue'; +import GlobalFetch from 'alova/GlobalFetch'; +import { axiosRequestAdapter } from '@alova/adapter-axios'; + +// user alova instance +export const userAlova = createAlova({ + baseURL: 'https://api-user.alovajs.org', + statesHook: VueHook, + requestAdapter: GlobalFetch(), + async responded(method) { + method.config.headers.token = 'user token'; + } +}); + +// order alova instance +export const orderAlova = createAlova({ + baseURL: 'https://api-order.alovajs.org', + statesHook: VueHook, + requestAdapter: GlobalFetch(), + async responded(method) { + method.config.headers.token = 'order token'; + } +}); + +// upload alova instance +export const uploadAlova = createAlova({ + baseURL: 'https://api-order.alovajs.org', + statesHook: VueHook, + requestAdapter: axiosRequestAdapter() +}); +``` + +## Manage method instances + +We can use different js files to classify and manage method instances. For example, in the above api management structure, `api/methods/user.js` will be used to manage method instances related to user information, and `api/methods/order.js` will be used `A method instance related to order management. + +In addition, as mentioned above, in addition to sending requests, method instances can also be used to operate caches and states. In order to ensure the number and order of request parameters, we can use a function to correspond to a request API, through The corresponding method instance is returned in the form of incoming request parameters. As long as the incoming parameters are the same, the request information and parameter order of the method instance are also the same, so as to ensure that the method instance used to operate the cache and state is correct. + +```javascript title=api/methods/user.js +import { userAlova } from '..'; + +// Get user information +export const getUserInfo = id => userAlova.Get('/user/' + id); + +// Edit user information +export const editUserInfo = (name, age, mobile) => + userAlova.Post('/user', { + name, + age, + mobile + }); + +// remove user +export const removeUser = id => userAlova.Delete('/user/' + id); + +//... +``` + +In the **user component**, the method function can be directly imported for use, and the method function can be used again to invalidate the corresponding cache after calling `invalidateCache`. + +```html title=views/user.vue + + +``` diff --git a/versioned_docs/version-2.x/tutorial/07-best-practice/02-skills.md b/versioned_docs/version-2.x/tutorial/07-best-practice/02-skills.md index ab20c258e..30c4bff52 100644 --- a/versioned_docs/version-2.x/tutorial/07-best-practice/02-skills.md +++ b/versioned_docs/version-2.x/tutorial/07-best-practice/02-skills.md @@ -1,6 +1,5 @@ --- title: Skills -sidebar_position: 20 --- import Tabs from '@theme/Tabs'; @@ -259,8 +258,16 @@ But such a request only applies to simple parallel requests. If you need to perf Manually create a promise object and use `Promise.all` to complete the effect. ```javascript -const { data: todoList, onSuccess: onListSuccess, onError: onListError } = useRequest(todoListGetter); -const { data: todoCounter, onSuccess: onCountSuccess, onError: onCountError } = useRequest(todoCountGetter); +const { + data: todoList, + onSuccess: onListSuccess, + onError: onListError +} = useRequest(todoListGetter); +const { + data: todoCounter, + onSuccess: onCountSuccess, + onError: onCountError +} = useRequest(todoCountGetter); // Manually create promise object const listPromise = new Promise((resolve, reject) => { @@ -302,7 +309,10 @@ Let the first request be sent automatically, and the second request be triggered ```javascript // const { data: todoList, onSuccess } = useRequest(todoListGetter); -const { data: todoDetail, send: sendTodoDetail } = useRequest(todoId => todoDetailGetter(todoId), { immediate: false }); +const { data: todoDetail, send: sendTodoDetail } = useRequest( + todoId => todoDetailGetter(todoId), + { immediate: false } +); // Get the list first, then get the details of the first todo onSuccess(event => { @@ -317,7 +327,9 @@ Using the `send` function returned by the `useRequest` function, calling `send` ```javascript // Let them not automatically send requests first const { send: sendList } = useRequest(todoListGetter, { immediate: false }); -const { send: sendTodoDetail } = useRequest(todoId => todoDetailGetter(todoId), { immediate: false }); +const { send: sendTodoDetail } = useRequest(todoId => todoDetailGetter(todoId), { + immediate: false +}); //Use the promise object returned by the send function const serialRequest = async () => { diff --git a/versioned_docs/version-2.x/tutorial/07-best-practice/03-manage-cache-by-indexeddb.md b/versioned_docs/version-2.x/tutorial/07-best-practice/03-manage-cache-by-indexeddb.md index c3cc9a96a..33678bd7a 100644 --- a/versioned_docs/version-2.x/tutorial/07-best-practice/03-manage-cache-by-indexeddb.md +++ b/versioned_docs/version-2.x/tutorial/07-best-practice/03-manage-cache-by-indexeddb.md @@ -1,123 +1,122 @@ ---- -title: Manage Cache with IndexedDB -sidebar_position: 30 ---- - -If you are developing applications that require extensive use of local cache, such as graphics editing applications, file management applications, etc., the low-capacity localStorage can no longer meet the development needs. At this time, you can use IndexedDB and alova for large-capacity local cache management. - -This feature is mainly due to alova's [Controlled Cache](/tutorial/cache/controlled-cache) function, which can implement custom cache management. Let's take a look at the practical steps. - -Here is an [example of managed cache with IndexedDB](/tutorial/example/controlled-cache-by-indexeddb) - -Let's take custom management of large image data as an example. - -## Create IndexedDB instance - -First create an IndexedDB instance to operate the local cache, and export the cache operate function. - -```javascript title=db.js -const dbVersion = 1; -let dbInstance; -const request = window.indexedDB.open('MyTestDatabase', dbVersion); -request.onupgradeneeded = ({ target }) => { - dbInstance = target.result; - const imgStore = dbInstance.createObjectStore('images', { - autoIncrement: true - }); - imgStore.createIndex('fileName', 'fileName', { - unique: true - }); -}; -request.onerror = () => { - throw new Error('Database open fail'); -}; -request.onsuccess = ({ target }) => { - dbInstance = target.result; -}; - -// Add new data to IndexedDB -export const addImage2Cache = async (fileName, data) => { - const tx = dbInstance.transaction(['images'], 'readwrite'); - const request = tx.objectStore('images').add({ - fileName, - data - }); - return new Promise((resolve, reject) => { - request.onerror = () => { - reject('data add fail'); - }; - request.onsuccess = ({ result }) => { - resolve(result); - }; - }); -}; - -// Get file data according to fileName -export const getImageFromCache = async fileName => { - const tx = dbInstance.transaction(['images']); - const request = tx.objectStore('images').index('fileName').get(fileName); - return new Promise((resolve, reject) => { - request.onerror = () => { - reject('data add fail'); - }; - request.onsuccess = ({ target }) => { - resolve(target.result); - }; - }); -}; -``` - -## save data - -When saving data, we can save the cache in the `transformData` of the method, because `transformData` will only be triggered when the network request responds, but will not be triggered when the cache is hit. In the sample code, convert the image blob instance to base64 data, cache and return this base64 data. - -```javascript-api.js -import { addImage2Cache } from './db'; - -export const image = fileName => - alovaInst.Get(`/image/${fileName}`, { - // highlight-start - async transformData(imgBlob) { - // Asynchronously convert the blob to base64 - const reader = new FileReader(); - reader.readAsDataURL(imgBlob); - const base64Img = await new Promise(resolve => { - reader.onload = ({ target }) => { - resolve(target.result); - }; - }); - - // Cache image data to IndexedDB - await addImage2Cache(fileName, base64Img); - return base64Img; - } - // highlight-end - }); -``` - -## retrieve data - -Specify `localCache` of this method instance as an asynchronous function to change the cache into a controlled state, match the cache in IndexedDB in this function, and return it if it matches, otherwise return `undefined` and continue to initiate a request to obtain data. - -```javascript title=api.js -import { getImageFromCache } from './db'; - -export const image = fileName => - alovaInst.Get(`/image/${fileName}`, { - async transformData(imgBlob) { - //... - }, - - // highlight-start - async localCache() { - // get cache - const cache = await getImageFromCache(fileName); - return cache && cache.data; - } - // highlight-end - }); -``` - -In this way, a basic custom cache management is basically completed. You can also save the expiration time of the cache, and judge whether it has expired when the cache is matched in `localCache`, so as to realize the cache expiration function. - -IndexedDB is just one example of managing caches asynchronously, you can also connect to your cache servers to manage them. +--- +title: Manage Cache with IndexedDB +--- + +If you are developing applications that require extensive use of local cache, such as graphics editing applications, file management applications, etc., the low-capacity localStorage can no longer meet the development needs. At this time, you can use IndexedDB and alova for large-capacity local cache management. + +This feature is mainly due to alova's [Controlled Cache](/tutorial/cache/controlled-cache) function, which can implement custom cache management. Let's take a look at the practical steps. + +Here is an [example of managed cache with IndexedDB](/tutorial/example/controlled-cache-by-indexeddb) + +Let's take custom management of large image data as an example. + +## Create IndexedDB instance + +First create an IndexedDB instance to operate the local cache, and export the cache operate function. + +```javascript title=db.js +const dbVersion = 1; +let dbInstance; +const request = window.indexedDB.open('MyTestDatabase', dbVersion); +request.onupgradeneeded = ({ target }) => { + dbInstance = target.result; + const imgStore = dbInstance.createObjectStore('images', { + autoIncrement: true + }); + imgStore.createIndex('fileName', 'fileName', { + unique: true + }); +}; +request.onerror = () => { + throw new Error('Database open fail'); +}; +request.onsuccess = ({ target }) => { + dbInstance = target.result; +}; + +// Add new data to IndexedDB +export const addImage2Cache = async (fileName, data) => { + const tx = dbInstance.transaction(['images'], 'readwrite'); + const request = tx.objectStore('images').add({ + fileName, + data + }); + return new Promise((resolve, reject) => { + request.onerror = () => { + reject('data add fail'); + }; + request.onsuccess = ({ result }) => { + resolve(result); + }; + }); +}; + +// Get file data according to fileName +export const getImageFromCache = async fileName => { + const tx = dbInstance.transaction(['images']); + const request = tx.objectStore('images').index('fileName').get(fileName); + return new Promise((resolve, reject) => { + request.onerror = () => { + reject('data add fail'); + }; + request.onsuccess = ({ target }) => { + resolve(target.result); + }; + }); +}; +``` + +## save data + +When saving data, we can save the cache in the `transformData` of the method, because `transformData` will only be triggered when the network request responds, but will not be triggered when the cache is hit. In the sample code, convert the image blob instance to base64 data, cache and return this base64 data. + +```javascript-api.js +import { addImage2Cache } from './db'; + +export const image = fileName => + alovaInst.Get(`/image/${fileName}`, { + // highlight-start + async transformData(imgBlob) { + // Asynchronously convert the blob to base64 + const reader = new FileReader(); + reader.readAsDataURL(imgBlob); + const base64Img = await new Promise(resolve => { + reader.onload = ({ target }) => { + resolve(target.result); + }; + }); + + // Cache image data to IndexedDB + await addImage2Cache(fileName, base64Img); + return base64Img; + } + // highlight-end + }); +``` + +## retrieve data + +Specify `localCache` of this method instance as an asynchronous function to change the cache into a controlled state, match the cache in IndexedDB in this function, and return it if it matches, otherwise return `undefined` and continue to initiate a request to obtain data. + +```javascript title=api.js +import { getImageFromCache } from './db'; + +export const image = fileName => + alovaInst.Get(`/image/${fileName}`, { + async transformData(imgBlob) { + //... + }, + + // highlight-start + async localCache() { + // get cache + const cache = await getImageFromCache(fileName); + return cache && cache.data; + } + // highlight-end + }); +``` + +In this way, a basic custom cache management is basically completed. You can also save the expiration time of the cache, and judge whether it has expired when the cache is matched in `localCache`, so as to realize the cache expiration function. + +IndexedDB is just one example of managing caches asynchronously, you can also connect to your cache servers to manage them. diff --git a/versioned_docs/version-2.x/tutorial/07-best-practice/04-multiple-servers.md b/versioned_docs/version-2.x/tutorial/07-best-practice/04-multiple-servers.md index a08908d29..da125fc16 100644 --- a/versioned_docs/version-2.x/tutorial/07-best-practice/04-multiple-servers.md +++ b/versioned_docs/version-2.x/tutorial/07-best-practice/04-multiple-servers.md @@ -1,20 +1,19 @@ ---- -title: Multiple servers -sidebar_position: 40 ---- - -If your project needs to request multiple servers, you can create multiple alova instances to correspond to different servers. In order to easily distinguish between different environments, you can also use environment variables to manage the hosts of multiple servers. - -```ts -import { createAlova } from '@alova/core'; - -//Create user-related alova instance -const userAlova = createAlova({ - baseURL: VITE_API_USER -}); - -//Create order-related alova instances -const alova2 = createAlova({ - baseURL: VITE_API_ORDER -}); -``` +--- +title: Multiple servers +--- + +If your project needs to request multiple servers, you can create multiple alova instances to correspond to different servers. In order to easily distinguish between different environments, you can also use environment variables to manage the hosts of multiple servers. + +```ts +import { createAlova } from '@alova/core'; + +//Create user-related alova instance +const userAlova = createAlova({ + baseURL: VITE_API_USER +}); + +//Create order-related alova instances +const alova2 = createAlova({ + baseURL: VITE_API_ORDER +}); +``` diff --git a/versioned_docs/version-2.x/tutorial/07-best-practice/05-middleware.md b/versioned_docs/version-2.x/tutorial/07-best-practice/05-middleware.md index 0341d9f0c..67afb2eb3 100644 --- a/versioned_docs/version-2.x/tutorial/07-best-practice/05-middleware.md +++ b/versioned_docs/version-2.x/tutorial/07-best-practice/05-middleware.md @@ -1,29 +1,28 @@ ---- -title: Common middleware practices -sidebar_position: 50 ---- - -## Delay update loading - -When the response is very fast, the loading status will flash once, which will bring a bad experience to the user. Delaying the loading update can make the loading status display after a period of time. If the response is completed within this period, it will not appear. Loading status. Let's implement a middleware with delayed update loading. - -```javascript -const delayLoadingMiddleware = - (delayTimer = 1000) => - async (ctx, next) => { - //Control loading by yourself - ctx.controlLoading(); - - //Delay updates for a specific time - const timer = setTimeout(() => { - ctx.update({ loading: true }); - }, delayTimer); - await next(); - ctx.update({ loading: false }); - clearTimeout(timer); - }; - -useRequest(methodInstance, { - middleware: delayLoadingMiddleware() -}); -``` +--- +title: Common middleware practices +--- + +## Delay update loading + +When the response is very fast, the loading status will flash once, which will bring a bad experience to the user. Delaying the loading update can make the loading status display after a period of time. If the response is completed within this period, it will not appear. Loading status. Let's implement a middleware with delayed update loading. + +```javascript +const delayLoadingMiddleware = + (delayTimer = 1000) => + async (ctx, next) => { + //Control loading by yourself + ctx.controlLoading(); + + //Delay updates for a specific time + const timer = setTimeout(() => { + ctx.update({ loading: true }); + }, delayTimer); + await next(); + ctx.update({ loading: false }); + clearTimeout(timer); + }; + +useRequest(methodInstance, { + middleware: delayLoadingMiddleware() +}); +``` diff --git a/versioned_docs/version-2.x/tutorial/08-request-adapter/01-alova-mock.md b/versioned_docs/version-2.x/tutorial/08-request-adapter/01-alova-mock.md index 612823839..395ac3c78 100644 --- a/versioned_docs/version-2.x/tutorial/08-request-adapter/01-alova-mock.md +++ b/versioned_docs/version-2.x/tutorial/08-request-adapter/01-alova-mock.md @@ -1,12 +1,11 @@ --- title: Mock data -sidebar_position: 10 --- import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -This mock plug-in is an alova request adapter. Different from the traditional Proxy form, you can control the scope of use of mock data. You can control the global scope, a group of interface scopes, and even the enabling and use of a certain interface. Disabled, which is very useful in our actual business scenarios. Each iteration will add or modify a set of interfaces. We hope that the previous functions will still follow the developed interfaces, and let the new or modified interfaces Taking the simulation data, at this time, each developer can group the interfaces involved in this iteration into a group, and turn them on or off. +This mock plug-in is an alova request adapter. Different from the traditional Proxy form, you can control the Scope of usage of mock data. You can control the global scope, a group of interface scopes, and even the enabling and use of a certain interface. Disabled, which is very useful in our actual business scenarios. Each iteration will add or modify a set of interfaces. We hope that the previous functions will still follow the developed interfaces, and let the new or modified interfaces Taking the simulation data, at this time, each developer can group the interfaces involved in this iteration into a group, and turn them on or off. ## Features diff --git a/versioned_docs/version-2.x/tutorial/08-request-adapter/02-alova-adapter-xhr.md b/versioned_docs/version-2.x/tutorial/08-request-adapter/02-alova-adapter-xhr.md index 4959a1b1f..930f63124 100644 --- a/versioned_docs/version-2.x/tutorial/08-request-adapter/02-alova-adapter-xhr.md +++ b/versioned_docs/version-2.x/tutorial/08-request-adapter/02-alova-adapter-xhr.md @@ -1,337 +1,336 @@ ---- -title: XMLHttpRequest Adapter -sidebar_position: 20 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -## Install - - - - -```bash -npm install @alova/adapter-xhr --save -``` - - - - -```bash -yarn add @alova/adapter-xhr -``` - - - - -## Instructions - -### create alova - -Use **xhrRequestAdapter** as request adapter for alova. - -```javascript -import { createAlova } from 'alova'; -import { xhrRequestAdapter } from '@alova/adapter-xhr'; - -const alovaInst = createAlova({ - //... - requestAdapter: xhrResponseAdapter() - //... -}); -``` - -### Request - -The XMLHttpRequest adapter provides basic configuration parameters, including `responseType`, `withCredentials`, `mimeType`, `auth`, as follows: - - - - -```html - -
Loading...
-
The request data is: {{ data }}
- - - -``` - -
- - -```jsx -const list = () => - alovaInst.Get('/list', { - /** - * Set the response data type - * Can be set to change the response type. Values are: "arraybuffer", "blob", "document", "json" and "text" - * defaults to "json" - */ - responseType: 'text', - - /** - * True when credentials are to be included in cross-origin requests. false when they are excluded from cross-origin requests and when cookies are ignored in their responses. Default is false - */ - withCredentials: true, - - /** - * Set the mimeType of the response data - */ - mimeType: 'text/plain; charset=x-user-defined', - - /** - * auth means use HTTP Basic authentication and provide credentials. - * This will set an `Authorization` header, overriding any existing - * Custom headers for `Authorization` set using `headers`. - * Note that only HTTP Basic authentication can be configured via this parameter. - * For Bearer tokens etc., use the `Authorization` custom header instead. - */ - auth: { - username: 'name1', - password: '123456' - } - }); - -const App = () => { - const { loading, data } = useRequest(list); - - return ( - { loading ?
Loading...
: null } -
The request data is: { JSON.stringify(data) }
- ) -}; -``` - -
- - -```html - - -{#if $loading} -
Loading...
-{/if} -
The request data is: { data }
-``` - -
-
- -### Upload - -Use `FormData` to upload files, and this `FormData` instance will be sent to the server through `xhr.send`. It will be set `Content-Type` automatically, you don't need to custom it with `multipart/form-data`. - -```javascript -const uploadFile = imageFile => { - const formData = new FormData(); - formData.append('file', imageFile); - return alovaInst.Post('/uploadImg', formData, { - // Start upload progress - enableUpload: true - }); -}; - -const { - loading, - data, - uploading, - send: sendUpload -} = useRequest(uploadFile, { - immediate: false -}); - -// Picture selection event callback -const handleImageChoose = ({ target }) => { - sendUpload(target.files[0]); -}; -``` - -### download - -Point the request url to the file address to download, you can also enable the download progress by setting `enableDownload` to true. - -```javascript -const downloadFile = () => - alovaInst.Get('/bigImage.jpg', { - // Start download progress - enableDownload: true, - responseType: 'blob' - }); - -const { loading, data, downloading, send, onSuccess } = useRequest(downloadFile, { - immediate: false -}); -onSuccess(({ data: imageBlob }) => { - // download image - const anchor = document.createElement('a'); - anchor.href = URL.createObjectURL(blob); - anchor.download = 'image.jpg'; - anchor.click(); - URL.revokeObjectURL(anchor.href); -}); -const handleImageDownload = () => { - send(); -}; -``` - -## Mock request adapter compatible - -When developing applications, we may still need to use simulated requests. Only by default, the response data of [Mock Request Adapter (@alova/mock)](/tutorial/request-adapter/alova-mock) is a `Response` instance, which is compatible with the `GlobalFetch` request adapter by default. When using the XMLHttpRequest adapter, we You need to adapt the response data of the mock request adapter to the XMLHttpRequest adapter. In this case, you need to use the `xhrMockResponse` exported in the **@alova/adapter-xhr** package as the response adapter. - -```javascript -import { defineMock, createAlovaMockAdapter } from '@alova/mock'; -import { xhrRequestAdapter, xhrMockResponse } from '@alova/adapter-xhr'; - -const mocks = defineMock({ - //... -}); - -// mock data request adapter -export default createAlovaMockAdapter([mocks], { - // After specifying the request adapter, requests that do not match the simulated interface will use this adapter to send requests - httpAdapter: xhrRequestAdapter(), - - // Use xhrMockResponse to adapt the simulated data to the XMLHttpRequest adapter - onMockResponse: xhrMockResponse -}); - -export const alovaInst = createAlova({ - //... - // Control whether to use the simulated request adapter through environment variables - requestAdapter: process.env.NODE_ENV === 'development' ? mockAdapter : xhrRequestAdapter() -}); -``` - -## Typescript - -The XMLHttpRequest request adapter provides complete type adaptation. - -### method configuration - -When creating a method instance, in addition to the common configuration items in the method, you can also use the configuration items in `AlovaXHRRequestConfig`. - -```typescript -/** - * xhr request configuration parameters - */ -interface AlovaXHRRequestConfig { - /** - * Set the response data type. - * - * Can be set to change the response type. Values are: "arraybuffer", "blob", "document", "json", and "text". - * Setting 1: If the current global object is not a Window object, the setting to "document" is ignored. - * Setup 2: Throw an "InvalidStateError" DOMException if the state is loading or complete. - * Setting 3: Throws an "InvalidAccessError" DOMException if the sync flag is set and the current global object is a Window object. - * @default "json" - */ - responseType?: XMLHttpRequestResponseType; - - /** - * True when credentials are to be included in cross-origin requests. false when they are excluded from cross-origin requests and when cookies are ignored in their responses. The default is false. - * An 'InvalidStateError' DOMException is thrown if the state is not sent or not opened, or if the send() flag is set. - * @default false - */ - withCredentials?: boolean; - - /** - * Set the mimeType of the response data - */ - mimeType?: string; - - /** - * `auth` indicates that HTTP Basic authentication should be used, and credentials are provided. - * This will set an `Authorization` header, overriding any existing - * Custom headers for `Authorization` set using `headers`. - * Note that only HTTP Basic authentication can be configured via this parameter. - * For Bearer tokens etc., use the `Authorization` custom header instead. - */ - auth?: { - username: string; - password: string; - }; -} -``` - -### Response data - -XMLHttpRequest adapter response data is as follows: - -```typescript -interface AlovaXHRResponseHeaders { - [x: string]: any; -} -interface AlovaXHRResponse { - status: number; - statusText: string; - data: T; - headers: AlovaXHRResponseHeaders; -} -``` +--- +title: XMLHttpRequest Adapter +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## Install + + + + +```bash +npm install @alova/adapter-xhr --save +``` + + + + +```bash +yarn add @alova/adapter-xhr +``` + + + + +## Instructions + +### create alova + +Use **xhrRequestAdapter** as request adapter for alova. + +```javascript +import { createAlova } from 'alova'; +import { xhrRequestAdapter } from '@alova/adapter-xhr'; + +const alovaInst = createAlova({ + //... + requestAdapter: xhrResponseAdapter() + //... +}); +``` + +### Request + +The XMLHttpRequest adapter provides basic configuration parameters, including `responseType`, `withCredentials`, `mimeType`, `auth`, as follows: + + + + +```html + +
Loading...
+
The request data is: {{ data }}
+ + + +``` + +
+ + +```jsx +const list = () => + alovaInst.Get('/list', { + /** + * Set the response data type + * Can be set to change the response type. Values are: "arraybuffer", "blob", "document", "json" and "text" + * defaults to "json" + */ + responseType: 'text', + + /** + * True when credentials are to be included in cross-origin requests. false when they are excluded from cross-origin requests and when cookies are ignored in their responses. Default is false + */ + withCredentials: true, + + /** + * Set the mimeType of the response data + */ + mimeType: 'text/plain; charset=x-user-defined', + + /** + * auth means use HTTP Basic authentication and provide credentials. + * This will set an `Authorization` header, overriding any existing + * Custom headers for `Authorization` set using `headers`. + * Note that only HTTP Basic authentication can be configured via this parameter. + * For Bearer tokens etc., use the `Authorization` custom header instead. + */ + auth: { + username: 'name1', + password: '123456' + } + }); + +const App = () => { + const { loading, data } = useRequest(list); + + return ( + { loading ?
Loading...
: null } +
The request data is: { JSON.stringify(data) }
+ ) +}; +``` + +
+ + +```html + + +{#if $loading} +
Loading...
+{/if} +
The request data is: { data }
+``` + +
+
+ +### Upload + +Use `FormData` to upload files, and this `FormData` instance will be sent to the server through `xhr.send`. It will be set `Content-Type` automatically, you don't need to custom it with `multipart/form-data`. + +```javascript +const uploadFile = imageFile => { + const formData = new FormData(); + formData.append('file', imageFile); + return alovaInst.Post('/uploadImg', formData, { + // Start upload progress + enableUpload: true + }); +}; + +const { + loading, + data, + uploading, + send: sendUpload +} = useRequest(uploadFile, { + immediate: false +}); + +// Picture selection event callback +const handleImageChoose = ({ target }) => { + sendUpload(target.files[0]); +}; +``` + +### download + +Point the request url to the file address to download, you can also enable the download progress by setting `enableDownload` to true. + +```javascript +const downloadFile = () => + alovaInst.Get('/bigImage.jpg', { + // Start download progress + enableDownload: true, + responseType: 'blob' + }); + +const { loading, data, downloading, send, onSuccess } = useRequest(downloadFile, { + immediate: false +}); +onSuccess(({ data: imageBlob }) => { + // download image + const anchor = document.createElement('a'); + anchor.href = URL.createObjectURL(blob); + anchor.download = 'image.jpg'; + anchor.click(); + URL.revokeObjectURL(anchor.href); +}); +const handleImageDownload = () => { + send(); +}; +``` + +## Mock request adapter compatible + +When developing applications, we may still need to use simulated requests. Only by default, the response data of [Mock Request Adapter (@alova/mock)](/tutorial/request-adapter/alova-mock) is a `Response` instance, which is compatible with the `GlobalFetch` request adapter by default. When using the XMLHttpRequest adapter, we You need to adapt the response data of the mock request adapter to the XMLHttpRequest adapter. In this case, you need to use the `xhrMockResponse` exported in the **@alova/adapter-xhr** package as the response adapter. + +```javascript +import { defineMock, createAlovaMockAdapter } from '@alova/mock'; +import { xhrRequestAdapter, xhrMockResponse } from '@alova/adapter-xhr'; + +const mocks = defineMock({ + //... +}); + +// mock data request adapter +export default createAlovaMockAdapter([mocks], { + // After specifying the request adapter, requests that do not match the simulated interface will use this adapter to send requests + httpAdapter: xhrRequestAdapter(), + + // Use xhrMockResponse to adapt the simulated data to the XMLHttpRequest adapter + onMockResponse: xhrMockResponse +}); + +export const alovaInst = createAlova({ + //... + // Control whether to use the simulated request adapter through environment variables + requestAdapter: process.env.NODE_ENV === 'development' ? mockAdapter : xhrRequestAdapter() +}); +``` + +## Typescript + +The XMLHttpRequest request adapter provides complete type adaptation. + +### method configuration + +When creating a method instance, in addition to the common configuration items in the method, you can also use the configuration items in `AlovaXHRRequestConfig`. + +```typescript +/** + * xhr request configuration parameters + */ +interface AlovaXHRRequestConfig { + /** + * Set the response data type. + * + * Can be set to change the response type. Values are: "arraybuffer", "blob", "document", "json", and "text". + * Setting 1: If the current global object is not a Window object, the setting to "document" is ignored. + * Setup 2: Throw an "InvalidStateError" DOMException if the state is loading or complete. + * Setting 3: Throws an "InvalidAccessError" DOMException if the sync flag is set and the current global object is a Window object. + * @default "json" + */ + responseType?: XMLHttpRequestResponseType; + + /** + * True when credentials are to be included in cross-origin requests. false when they are excluded from cross-origin requests and when cookies are ignored in their responses. The default is false. + * An 'InvalidStateError' DOMException is thrown if the state is not sent or not opened, or if the send() flag is set. + * @default false + */ + withCredentials?: boolean; + + /** + * Set the mimeType of the response data + */ + mimeType?: string; + + /** + * `auth` indicates that HTTP Basic authentication should be used, and credentials are provided. + * This will set an `Authorization` header, overriding any existing + * Custom headers for `Authorization` set using `headers`. + * Note that only HTTP Basic authentication can be configured via this parameter. + * For Bearer tokens etc., use the `Authorization` custom header instead. + */ + auth?: { + username: string; + password: string; + }; +} +``` + +### Response data + +XMLHttpRequest adapter response data is as follows: + +```typescript +interface AlovaXHRResponseHeaders { + [x: string]: any; +} +interface AlovaXHRResponse { + status: number; + statusText: string; + data: T; + headers: AlovaXHRResponseHeaders; +} +``` diff --git a/versioned_docs/version-2.x/tutorial/08-request-adapter/03-alova-adapter-axios.md b/versioned_docs/version-2.x/tutorial/08-request-adapter/03-alova-adapter-axios.md index 93b936a8f..63cd84234 100644 --- a/versioned_docs/version-2.x/tutorial/08-request-adapter/03-alova-adapter-axios.md +++ b/versioned_docs/version-2.x/tutorial/08-request-adapter/03-alova-adapter-axios.md @@ -1,293 +1,292 @@ ---- -title: axios adapter -sidebar_position: 30 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -## Install - - - - -```bash -npm install @alova/adapter-axios --save -``` - - - - -```bash -yarn add @alova/adapter-axios -``` - - - - -## Instructions - -### create alova - -Use **axiosRequestAdapter** as request adapter for alova. - -```javascript -import { createAlova } from 'alova'; -import { axiosRequestAdapter } from '@alova/adapter-axios'; - -const alovaInst = createAlova({ - //... - requestAdapter: axiosRequestAdapter() - //... -}); -``` - -The adapter will use the default axios instance to make requests internally. If you set some global parameters for axios, you may need to pay attention to the following two points: - -1. Priority is given to using the `baseURL` and `timeout` parameters in the axios instance, so if you set these parameters on the axios instance, you do not need to set them in `createAlova`; -2. The `beforeRequest` hook of the alova instance will be triggered earlier than the `interceptor.request` of the axios instance, and the `responded` hook of the alova will be triggered later than the `interceptor.response` of the axios instance; - -> You can also [use custom axios instance](#use-custom-axios-instance) - -## usage - -### request - -The usage of request is exactly the same as that used in the web environment. Already fully compatible with **axios**, you can specify [all configuration items](https://axios-http.com/docs/req_config) supported by `axios` in _config_ of method instance creation. - - - - -```html - -
Loading...
-
The request data is: {{ data }}
- - - -``` - -
- - -```jsx -const list = () => - alovaInst.Get('/list', { - // The set parameters will be passed to axios - paramsSerializer: params => { - return Qs.stringify(params, {arrayFormat: 'brackets'}) - }, - }); - -const App = () => { - const { loading, data } = useRequest(list); - - return ( - { loading ?
Loading...
: null } -
The request data is: { JSON.stringify(data) }
- ) -}; -``` - -
- - -```html - - -{#if $loading} -
Loading...
-{/if} -
The request data is: { data }
-``` - -
-
- -### Upload - -Use `FormData` to upload files, and this `FormData` instance will be passed to axios, which is consistent with the usage of axios upload files. - -```javascript -const uploadFile = imageFile => { - const formData = new FormData(); - formData.append('file', imageFile); - return alovaInst.Post('/uploadImg', formData, { - // Start upload progress - enableUpload: true - }); -}; - -const { - loading, - data, - uploading, - send: sendUpload -} = useRequest(uploadFile, { - immediate: false -}); - -// Picture selection event callback -const handleImageChoose = ({ target }) => { - sendUpload(target.files[0]); -}; -``` - -### download - -Point the request url to the file address to download, you can also enable the download progress by setting `enableDownload` to true. - -```javascript -const downloadFile = () => - alovaInst.Get('/bigImage.jpg', { - // Start download progress - enableDownload: true, - responseType: 'blob' - }); - -const { loading, data, downloading, send, onSuccess } = useRequest(downloadFile, { - immediate: false -}); -onSuccess(({ data: imageBlob }) => { - // download image - const anchor = document.createElement('a'); - anchor.href = URL.createObjectURL(blob); - anchor.download = 'image.jpg'; - anchor.click(); - URL.revokeObjectURL(anchor.href); -}); -const handleImageDownload = () => { - send(); -}; -``` - -## Use custom axios instance - -By default, this adapter will use the default axios instance for requests, but in some cases you need to use a custom created axios instance. You can do this: - -```javascript -const customAxios = axios.create({ - // ... -}); - -const alovaInst = createAlova({ - // ... - // highlight-start - requestAdapter: axiosRequestAdapter({ - axios: customAxios - }) - // highlight-end -}); -``` - -## Mock request adapter compatible - -When developing applications, we may still need to use simulated requests. Only by default, the response data of [Mock Request Adapter (@alova/mock)](/tutorial/request-adapter/alova-mock) is a `Response` instance, which is compatible with the `GlobalFetch` request adapter by default. When using the axios adapter, we The response data of the mock request adapter needs to be compatible with **AxiosResponse**, and the error instance is **AxiosError**, so you need to use `axiosMockResponse` exported from the **@alova/adapter-axios** package as the response adapter . - -```javascript -import { defineMock, createAlovaMockAdapter } from '@alova/mock'; -import { axiosRequestAdapter, axiosMockResponse } from '@alova/adapter-axios'; - -const mocks = defineMock({ - //... -}); - -// mock data request adapter -export default createAlovaMockAdapter([mocks], { - // After specifying axios request adapter, requests that do not match the simulated interface will use this adapter to send requests - httpAdapter: axiosRequestAdapter(), - - // axiosMockResponse contains onMockResponse and onMockError - // Used to convert mock data to AxiosResponse and AxiosError compatible format - ...axiosMockResponse -}); - -export const alovaInst = createAlova({ - //... - // Control whether to use the simulated request adapter through environment variables - requestAdapter: process.env.NODE_ENV === 'development' ? mockAdapter : axiosRequestAdapter() -}); -``` - -##Typescript - -The axios request adapter provides complete type adaptation. The type of method configuration and response data will exactly match the type of axios. - -### method configuration - -When creating a method instance, in addition to the general configuration items in the method, you can also use the configuration items in `AxiosRequestConfig`, we have removed the items that conflict with the general configuration of the method instance in the type. - -```typescript -/** - * axios request configuration parameters - * Removed the attributes that conflicted with the method - */ -export type AlovaAxiosRequestConfig = Omit< - AxiosRequestConfig, - | 'url' - | 'method' - | 'baseURL' - | 'headers' - | 'params' - | 'data' - | 'timeout' - | 'cancelToken' - | 'signal' - | 'onUploadProgress' - | 'onDownloadProgress' ->; -``` - -### Response data - -The response data type of axios is `AxiosResponse`, when you use the axios adapter, you will also get the response data in the same format. In actual use, we usually need to process response data globally. A simple example is as follows: - -```typescript -const alovaInst = createAlova( - baseURL: 'https://api.alovajs.org', - requestAdapter: axiosRequestAdapter(), - responded(response) { - // response is automatically inferred as AxiosResponse type - return response.data; - } -}); -``` - -### Error - -When axios receives non-20x and 30x response status codes, it will throw an error. In order to contain more information, axios custom-designed the error instance into an `AxiosError` instance instead of a normal Error instance, so when encountering An error will be thrown when there is a server error or a network error, and you can catch it in the global error callback. - -```typescript -const alovaInst = createAlova( - baseURL: 'https://api.alovajs.org', - requestAdapter: axiosRequestAdapter(), - responded: { - onSuccess(response) { - // response is automatically inferred as AxiosResponse type - return response.data; - }, - onError(err: AxiosError) { - // err type is any by default, you can cast it to AxiosError - //... - } - } -}); -``` +--- +title: axios adapter +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## Install + + + + +```bash +npm install @alova/adapter-axios --save +``` + + + + +```bash +yarn add @alova/adapter-axios +``` + + + + +## Instructions + +### create alova + +Use **axiosRequestAdapter** as request adapter for alova. + +```javascript +import { createAlova } from 'alova'; +import { axiosRequestAdapter } from '@alova/adapter-axios'; + +const alovaInst = createAlova({ + //... + requestAdapter: axiosRequestAdapter() + //... +}); +``` + +The adapter will use the default axios instance to make requests internally. If you set some global parameters for axios, you may need to pay attention to the following two points: + +1. Priority is given to using the `baseURL` and `timeout` parameters in the axios instance, so if you set these parameters on the axios instance, you do not need to set them in `createAlova`; +2. The `beforeRequest` hook of the alova instance will be triggered earlier than the `interceptor.request` of the axios instance, and the `responded` hook of the alova will be triggered later than the `interceptor.response` of the axios instance; + +> You can also [use custom axios instance](#use-custom-axios-instance) + +## usage + +### request + +The usage of request is exactly the same as that used in the web environment. Already fully compatible with **axios**, you can specify [all configuration items](https://axios-http.com/docs/req_config) supported by `axios` in _config_ of method instance creation. + + + + +```html + +
Loading...
+
The request data is: {{ data }}
+ + + +``` + +
+ + +```jsx +const list = () => + alovaInst.Get('/list', { + // The set parameters will be passed to axios + paramsSerializer: params => { + return Qs.stringify(params, {arrayFormat: 'brackets'}) + }, + }); + +const App = () => { + const { loading, data } = useRequest(list); + + return ( + { loading ?
Loading...
: null } +
The request data is: { JSON.stringify(data) }
+ ) +}; +``` + +
+ + +```html + + +{#if $loading} +
Loading...
+{/if} +
The request data is: { data }
+``` + +
+
+ +### Upload + +Use `FormData` to upload files, and this `FormData` instance will be passed to axios, which is consistent with the usage of axios upload files. + +```javascript +const uploadFile = imageFile => { + const formData = new FormData(); + formData.append('file', imageFile); + return alovaInst.Post('/uploadImg', formData, { + // Start upload progress + enableUpload: true + }); +}; + +const { + loading, + data, + uploading, + send: sendUpload +} = useRequest(uploadFile, { + immediate: false +}); + +// Picture selection event callback +const handleImageChoose = ({ target }) => { + sendUpload(target.files[0]); +}; +``` + +### download + +Point the request url to the file address to download, you can also enable the download progress by setting `enableDownload` to true. + +```javascript +const downloadFile = () => + alovaInst.Get('/bigImage.jpg', { + // Start download progress + enableDownload: true, + responseType: 'blob' + }); + +const { loading, data, downloading, send, onSuccess } = useRequest(downloadFile, { + immediate: false +}); +onSuccess(({ data: imageBlob }) => { + // download image + const anchor = document.createElement('a'); + anchor.href = URL.createObjectURL(blob); + anchor.download = 'image.jpg'; + anchor.click(); + URL.revokeObjectURL(anchor.href); +}); +const handleImageDownload = () => { + send(); +}; +``` + +## Use custom axios instance + +By default, this adapter will use the default axios instance for requests, but in some cases you need to use a custom created axios instance. You can do this: + +```javascript +const customAxios = axios.create({ + // ... +}); + +const alovaInst = createAlova({ + // ... + // highlight-start + requestAdapter: axiosRequestAdapter({ + axios: customAxios + }) + // highlight-end +}); +``` + +## Mock request adapter compatible + +When developing applications, we may still need to use simulated requests. Only by default, the response data of [Mock Request Adapter (@alova/mock)](/tutorial/request-adapter/alova-mock) is a `Response` instance, which is compatible with the `GlobalFetch` request adapter by default. When using the axios adapter, we The response data of the mock request adapter needs to be compatible with **AxiosResponse**, and the error instance is **AxiosError**, so you need to use `axiosMockResponse` exported from the **@alova/adapter-axios** package as the response adapter . + +```javascript +import { defineMock, createAlovaMockAdapter } from '@alova/mock'; +import { axiosRequestAdapter, axiosMockResponse } from '@alova/adapter-axios'; + +const mocks = defineMock({ + //... +}); + +// mock data request adapter +export default createAlovaMockAdapter([mocks], { + // After specifying axios request adapter, requests that do not match the simulated interface will use this adapter to send requests + httpAdapter: axiosRequestAdapter(), + + // axiosMockResponse contains onMockResponse and onMockError + // Used to convert mock data to AxiosResponse and AxiosError compatible format + ...axiosMockResponse +}); + +export const alovaInst = createAlova({ + //... + // Control whether to use the simulated request adapter through environment variables + requestAdapter: process.env.NODE_ENV === 'development' ? mockAdapter : axiosRequestAdapter() +}); +``` + +##Typescript + +The axios request adapter provides complete type adaptation. The type of method configuration and response data will exactly match the type of axios. + +### method configuration + +When creating a method instance, in addition to the general configuration items in the method, you can also use the configuration items in `AxiosRequestConfig`, we have removed the items that conflict with the general configuration of the method instance in the type. + +```typescript +/** + * axios request configuration parameters + * Removed the attributes that conflicted with the method + */ +export type AlovaAxiosRequestConfig = Omit< + AxiosRequestConfig, + | 'url' + | 'method' + | 'baseURL' + | 'headers' + | 'params' + | 'data' + | 'timeout' + | 'cancelToken' + | 'signal' + | 'onUploadProgress' + | 'onDownloadProgress' +>; +``` + +### Response data + +The response data type of axios is `AxiosResponse`, when you use the axios adapter, you will also get the response data in the same format. In actual use, we usually need to process response data globally. A simple example is as follows: + +```typescript +const alovaInst = createAlova( + baseURL: 'https://api.alovajs.org', + requestAdapter: axiosRequestAdapter(), + responded(response) { + // response is automatically inferred as AxiosResponse type + return response.data; + } +}); +``` + +### Error + +When axios receives non-20x and 30x response status codes, it will throw an error. In order to contain more information, axios custom-designed the error instance into an `AxiosError` instance instead of a normal Error instance, so when encountering An error will be thrown when there is a server error or a network error, and you can catch it in the global error callback. + +```typescript +const alovaInst = createAlova( + baseURL: 'https://api.alovajs.org', + requestAdapter: axiosRequestAdapter(), + responded: { + onSuccess(response) { + // response is automatically inferred as AxiosResponse type + return response.data; + }, + onError(err: AxiosError) { + // err type is any by default, you can cast it to AxiosError + //... + } + } +}); +``` diff --git a/versioned_docs/version-2.x/tutorial/08-request-adapter/04-alova-adapter-taro.md b/versioned_docs/version-2.x/tutorial/08-request-adapter/04-alova-adapter-taro.md index 20fec51a7..da94af45f 100644 --- a/versioned_docs/version-2.x/tutorial/08-request-adapter/04-alova-adapter-taro.md +++ b/versioned_docs/version-2.x/tutorial/08-request-adapter/04-alova-adapter-taro.md @@ -1,436 +1,443 @@ ---- -title: Taro Adapter -sidebar_position: 40 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -:::info Tips - -This plugin only supports the taro application of react 16.8+, vue3 version. - -::: - -## Install - - - - -```bash -npm install @alova/adapter-taro --save -``` - - - - -```bash -yarn add @alova/adapter-taro -``` - - - - -:::warning React-Native Application - -If you are develop a React-Native app with Taro, please ensure `metro >= 0.76.0` and enable `resolver.unstable_enablePackageExports` in the `metro.config.js`. - -[about unstable_enablePackageExports of metro](https://facebook.github.io/metro/docs/configuration/#unstable_enablepackageexports-experimental) - -::: - -:::warning Dependency precompilation issues - -[Dependency precompilation function](https://docs.taro.zone/blog/2022/05/19/Taro-3.5-beta#2-%E4%BE%9D%E8%B5%96%E9%A2%84%E7%BC%96%E8%AF%91) has been added in Taro v3.5 beta, and is enabled by default in development mode when you are using the `alova` library and `@alova/scene-react(vue)` may cause the error `` [alova]can not call useHooks until set the `statesHook` at alova instance. ``. This is caused by the prebundle feature repeatedly packaging two different `alova` packages. , turning off the prebundle function at this time can solve this problem. - -```js -// config/dev.ts -export default { - // ... - compiler: { - type: 'webpack5', - prebundle: { - // Close prebundle - enable: false - } - } -} satisfies UserConfigExport - -``` - -Thanks to [LBinin’s issue](https://github.com/alovajs/scene/issues/63). - -This problem has been committed to the Taro team [issue](https://github.com/NervJS/taro/issues/15728) and we look forward to solving this issue. - -::: - -## Usage - -### create alova - -Calling **AdapterTaro** will return _Request Adapter_, _Storage Adapter_, and _ReactHook_, so you no longer need to set these three items, and the usage is exactly the same. - - - - -```javascript -import { createAlova } from 'alova'; -import AdapterTaro from '@alova/adapter-taro'; - -const alovaInst = createAlova({ - baseURL: 'https://api.alovajs.org', - ...AdapterTaro() -}); -``` - - - - - -```javascript -import { createAlova } from 'alova'; -import AdapterTaroVue from '@alova/adapter-taro/vue'; - -const alovaInst = createAlova({ - baseURL: 'https://api.alovajs.org', - ...AdapterTaroVue() -}); -``` - - - - -### Request - -The usage method of the request is exactly the same as that used in the web environment. Already fully compatible with `Taro.request`, you can specify [all configuration items](https://taro-docs.jd.com/docs/apis/network/request/) supported by `Taro.request` in the _config_ of method instance creation - - - - -```jsx -const list = () => - alovaInst.Get('/list', { - // The set parameters will be passed to Taro.request - enableHttp2: true - }); - -const App = () => { - const { loading, data } = useRequest(list); - - return ( - { loading ? Loading... : null } - The requested data is: { JSON.stringify(data) } - ) -}; -``` - - - - -```html - - Loading... - The requested data is: {{ data }} - - - -``` - - - - -### Upload - -When `requestType: 'upload'` is set in the _config_ of the method instance, it means to upload the file, the request adapter will call `Taro.uploadFile`, and the uploaded file data is placed in the data of the method instance, you need to specify in the data `name` and `filePath`, these two parameters will be passed to `Taro.uploadFile`, at the same time, you can also specify other parameters in data, and the request adapter will pass them to `formData `in parameters. - -Similarly, it is fully compatible with `Taro.uploadFile`, you can specify [all configuration items](https://taro-docs.jd.com/docs/apis/network/upload/uploadFile) supported by `Taro.uploadFile`, if there are additional parameters to be set, please specify them in _config_ of the method instance. - - - - -```jsx -const uploadFile = (name, filePath, formData) => - alovaInst.Post( - '/uploadImg', - { - name, - filePath, - - // Additional data will be passed into formData of uni.uploadFile - ...formData - }, - { - // Set the request method to upload, and the adapter will call uni.uploadFile - requestType: 'upload', - - // Start upload progress - enableUpload: true - } - ); - -const App = () => { - const { loading, data, uploading, send } = useRequest(uploadFile, { - immediate: false - }); - - const handleImageChoose = () => { - Taro.chooseImage({ - success: chooseImageRes => { - const tempFilePaths = chooseImageRes.tempFilePaths; - send('fileName', tempFilePaths[0], { - extra1: 'a', - extra2: 'b' - }); - } - }); - }; - - return ( - { loading ? Uploading... : null } - Upload progress: { uploading.loaded }/{ uploading.total } - - {/* ... */} - ) -} -``` - - - - -```html - - Uploading... - Upload progress: {{ uploading.loaded }}/{{ uploading.total }} - - - - - -``` - - - - -### download - -When `requestType: 'download'` is set in the _config_ of the method instance, it means to download the file, and the request adapter will call `Taro.downloadFile`. - -Similarly, it is fully compatible with `Taro.downloadFile`, you can specify [all configuration items](https://taro-docs.jd.com/docs/apis/network/download/downloadFile) supported by `Taro.downloadFile`, if there are additional parameters to be set, please specify them in _config_ of the method instance. - - - - -```jsx -const downloadFile = filePath => - alovaInst.Get('/bigImage.jpg', { - // Set the request method to download, and the adapter will call uni.downloadFile - requestType: 'download', - filePath, - - // Start download progress - enableDownload: true - }); - -const App = () => { - const { loading, data, downloading, send } = useRequest(downloadFile, { - immediate: false - }); - const handleImageDownload = () => { - send('file_save_path'); - }; - - return ( - { loading ? Downloading... : null } - Download progress: { downloading.loaded }/{ downloading.total } - - {/* ... */} - ); -} -``` - - - - -```html - - Downloading... - Download progress: {{ downloading.loaded }}/{{ downloading.total }} - - - - - -``` - - - - -## Mock request adapter compatible - -When using Taro to develop applications, we may still need to use mock requests, but by default, the response data of [Mock Request Adapter (@alova/mock)](/tutorial/request-adapter/alova-mock) is a `Response` instance, That is, it is compatible with the `GlobalFetch` request adapter by default. When used in the Taro environment, we need to make the response data of the simulated request adapter compatible with the Taro adapter, so you need to use the **@alova/adapter-taro** package exported `taroMockResponse` as response adapter. - -```javascript -import { defineMock, createAlovaMockAdapter } from '@alova/mock'; -import AdapterTaro, { taroRequestAdapter, taroMockResponse } from '@alova/adapter-taro'; - -const mocks = defineMock({ - //... -}); - -// mock data request adapter -export default createAlovaMockAdapter([mocks], { - // After specifying the taro request adapter, requests that do not match the simulated interface will use this adapter to send requests - httpAdapter: taroRequestAdapter, - - // Simulate the response adapter, after specifying, the response data will be converted to a taro-compatible data format - onMockResponse: taroMockResponse -}); - -export const alovaInst = createAlova({ - baseURL: 'https://api.alovajs.org', - timeout: 5000, - ...AdapterTaro({ - // Control whether to use the simulated request adapter through environment variables - mockRequest: process.env.NODE_ENV === 'development' ? mockAdapter : undefined - }) - //... -}); -``` - -## Typescript - -The taro request adapter provides complete type adaptation, and the type of method configuration and response data will exactly match the type of taro. - -### method configuration - -When creating a method instance, in addition to the general configuration items in the method, you can also use the configuration items in `Taro.request`, `Taro.uploadFile` and `Taro.downloadFile`, we have removed the type and method Items that conflict with the common configuration of the instance. - -```typescript -/** - * Taro.request requests additional parameters - */ -export type TaroRequestConfig = Omit< - Taro.request.Option, - 'url' | 'data' | 'header' | 'method' | 'timeout' | 'success' | 'fail' | 'complete' ->; - -/** - * Taro.uploadFile additional parameter - */ -export type TaroUploadConfig = Omit< - Taro.uploadFile.Option, - 'url' | 'filePath' | 'name' | 'header' | 'formData' | 'timeout' | 'success' | 'fail' | 'complete' ->; - -/** - * Taro.downloadFile additional parameters - */ -export type TaroDownloadConfig = Omit< - Taro.downloadFile.Option, - 'url' | 'header' | 'timeout' | 'success' | 'fail' | 'complete' ->; - -/** - * Merged request configuration parameters - */ -export type TaroConfig = { - /** - * Request type, upload means upload, download means download, not filling means normal request - */ - requestType?: 'upload' | 'download'; -} & TaroRequestConfig & - TaroUploadConfig & - TaroDownloadConfig; -``` - -### Response data - -Because the taro request adapter is compatible with `Taro.request`, `Taro.uploadFile` and `Taro.downloadFile`, but their response value types are slightly different, so the response data type is as follows: - -```typescript -type TaroResponse = - // The response type of Taro.request - | Taro.request.SuccessCallbackResult - - // The response type of Taro.uploadFile - | Taro.uploadFile.SuccessCallbackResult - - // The response type of Taro.downloadFile - | Taro.downloadFile.FileSuccessCallbackResult; -``` - -In actual use, we usually need to process the response data globally. It is recommended to judge the returned data separately. A simple example is as follows: - -```typescript -const alovaInst = createAlova({ - baseURL: 'https://api.alovajs.org', - ...AdapterTaro(), - responded(response) { - const { statusCode, data } = response as Taro.request.SuccessCallbackResult; - if (statusCode >= 400) { - throw new Error('request error'); - } - return data || null; - } -}); -``` +--- +title: Taro Adapter +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info Tips + +This plugin only supports the taro application of react 16.8+, vue3 version. + +::: + +## Install + + + + +```bash +npm install @alova/adapter-taro --save +``` + + + + +```bash +yarn add @alova/adapter-taro +``` + + + + +:::warning React-Native Application + +If you are develop a React-Native app with Taro, please ensure `metro >= 0.76.0` and enable `resolver.unstable_enablePackageExports` in the `metro.config.js`. + +[about unstable_enablePackageExports of metro](https://facebook.github.io/metro/docs/configuration/#unstable_enablepackageexports-experimental) + +::: + +:::warning Dependency precompilation issues + +[Dependency precompilation function](https://docs.taro.zone/blog/2022/05/19/Taro-3.5-beta#2-%E4%BE%9D%E8%B5%96%E9%A2%84%E7%BC%96%E8%AF%91) has been added in Taro v3.5 beta, and is enabled by default in development mode when you are using the `alova` library and `@alova/scene-react(vue)` may cause the error `` [alova]can not call useHooks until set the `statesHook` at alova instance. ``. This is caused by the prebundle feature repeatedly packaging two different `alova` packages. , turning off the prebundle function at this time can solve this problem. + +```js +// config/dev.ts +export default { + // ... + compiler: { + type: 'webpack5', + prebundle: { + // Close prebundle + enable: false + } + } +} satisfies UserConfigExport + +``` + +Thanks to [LBinin’s issue](https://github.com/alovajs/scene/issues/63). + +This problem has been committed to the Taro team [issue](https://github.com/NervJS/taro/issues/15728) and we look forward to solving this issue. + +::: + +## Usage + +### create alova + +Calling **AdapterTaro** will return _Request Adapter_, _Storage Adapter_, and _ReactHook_, so you no longer need to set these three items, and the usage is exactly the same. + + + + +```javascript +import { createAlova } from 'alova'; +import AdapterTaro from '@alova/adapter-taro'; + +const alovaInst = createAlova({ + baseURL: 'https://api.alovajs.org', + ...AdapterTaro() +}); +``` + + + + + +```javascript +import { createAlova } from 'alova'; +import AdapterTaroVue from '@alova/adapter-taro/vue'; + +const alovaInst = createAlova({ + baseURL: 'https://api.alovajs.org', + ...AdapterTaroVue() +}); +``` + + + + +### Request + +The usage method of the request is exactly the same as that used in the web environment. Already fully compatible with `Taro.request`, you can specify [all configuration items](https://taro-docs.jd.com/docs/apis/network/request/) supported by `Taro.request` in the _config_ of method instance creation + + + + +```jsx +const list = () => + alovaInst.Get('/list', { + // The set parameters will be passed to Taro.request + enableHttp2: true + }); + +const App = () => { + const { loading, data } = useRequest(list); + + return ( + { loading ? Loading... : null } + The requested data is: { JSON.stringify(data) } + ) +}; +``` + + + + +```html + + Loading... + The requested data is: {{ data }} + + + +``` + + + + +### Upload + +When `requestType: 'upload'` is set in the _config_ of the method instance, it means to upload the file, the request adapter will call `Taro.uploadFile`, and the uploaded file data is placed in the data of the method instance, you need to specify in the data `name` and `filePath`, these two parameters will be passed to `Taro.uploadFile`, at the same time, you can also specify other parameters in data, and the request adapter will pass them to `formData `in parameters. + +Similarly, it is fully compatible with `Taro.uploadFile`, you can specify [all configuration items](https://taro-docs.jd.com/docs/apis/network/upload/uploadFile) supported by `Taro.uploadFile`, if there are additional parameters to be set, please specify them in _config_ of the method instance. + + + + +```jsx +const uploadFile = (name, filePath, formData) => + alovaInst.Post( + '/uploadImg', + { + name, + filePath, + + // Additional data will be passed into formData of uni.uploadFile + ...formData + }, + { + // Set the request method to upload, and the adapter will call uni.uploadFile + requestType: 'upload', + + // Start upload progress + enableUpload: true + } + ); + +const App = () => { + const { loading, data, uploading, send } = useRequest(uploadFile, { + immediate: false + }); + + const handleImageChoose = () => { + Taro.chooseImage({ + success: chooseImageRes => { + const tempFilePaths = chooseImageRes.tempFilePaths; + send('fileName', tempFilePaths[0], { + extra1: 'a', + extra2: 'b' + }); + } + }); + }; + + return ( + { loading ? Uploading... : null } + Upload progress: { uploading.loaded }/{ uploading.total } + + {/* ... */} + ) +} +``` + + + + +```html + + Uploading... + Upload progress: {{ uploading.loaded }}/{{ uploading.total }} + + + + + +``` + + + + +### download + +When `requestType: 'download'` is set in the _config_ of the method instance, it means to download the file, and the request adapter will call `Taro.downloadFile`. + +Similarly, it is fully compatible with `Taro.downloadFile`, you can specify [all configuration items](https://taro-docs.jd.com/docs/apis/network/download/downloadFile) supported by `Taro.downloadFile`, if there are additional parameters to be set, please specify them in _config_ of the method instance. + + + + +```jsx +const downloadFile = filePath => + alovaInst.Get('/bigImage.jpg', { + // Set the request method to download, and the adapter will call uni.downloadFile + requestType: 'download', + filePath, + + // Start download progress + enableDownload: true + }); + +const App = () => { + const { loading, data, downloading, send } = useRequest(downloadFile, { + immediate: false + }); + const handleImageDownload = () => { + send('file_save_path'); + }; + + return ( + { loading ? Downloading... : null } + Download progress: { downloading.loaded }/{ downloading.total } + + {/* ... */} + ); +} +``` + + + + +```html + + Downloading... + Download progress: {{ downloading.loaded }}/{{ downloading.total }} + + + + + +``` + + + + +## Mock request adapter compatible + +When using Taro to develop applications, we may still need to use mock requests, but by default, the response data of [Mock Request Adapter (@alova/mock)](/tutorial/request-adapter/alova-mock) is a `Response` instance, That is, it is compatible with the `GlobalFetch` request adapter by default. When used in the Taro environment, we need to make the response data of the simulated request adapter compatible with the Taro adapter, so you need to use the **@alova/adapter-taro** package exported `taroMockResponse` as response adapter. + +```javascript +import { defineMock, createAlovaMockAdapter } from '@alova/mock'; +import AdapterTaro, { taroRequestAdapter, taroMockResponse } from '@alova/adapter-taro'; + +const mocks = defineMock({ + //... +}); + +// mock data request adapter +export default createAlovaMockAdapter([mocks], { + // After specifying the taro request adapter, requests that do not match the simulated interface will use this adapter to send requests + httpAdapter: taroRequestAdapter, + + // Simulate the response adapter, after specifying, the response data will be converted to a taro-compatible data format + onMockResponse: taroMockResponse +}); + +export const alovaInst = createAlova({ + baseURL: 'https://api.alovajs.org', + timeout: 5000, + ...AdapterTaro({ + // Control whether to use the simulated request adapter through environment variables + mockRequest: process.env.NODE_ENV === 'development' ? mockAdapter : undefined + }) + //... +}); +``` + +## Typescript + +The taro request adapter provides complete type adaptation, and the type of method configuration and response data will exactly match the type of taro. + +### method configuration + +When creating a method instance, in addition to the general configuration items in the method, you can also use the configuration items in `Taro.request`, `Taro.uploadFile` and `Taro.downloadFile`, we have removed the type and method Items that conflict with the common configuration of the instance. + +```typescript +/** + * Taro.request requests additional parameters + */ +export type TaroRequestConfig = Omit< + Taro.request.Option, + 'url' | 'data' | 'header' | 'method' | 'timeout' | 'success' | 'fail' | 'complete' +>; + +/** + * Taro.uploadFile additional parameter + */ +export type TaroUploadConfig = Omit< + Taro.uploadFile.Option, + | 'url' + | 'filePath' + | 'name' + | 'header' + | 'formData' + | 'timeout' + | 'success' + | 'fail' + | 'complete' +>; + +/** + * Taro.downloadFile additional parameters + */ +export type TaroDownloadConfig = Omit< + Taro.downloadFile.Option, + 'url' | 'header' | 'timeout' | 'success' | 'fail' | 'complete' +>; + +/** + * Merged request configuration parameters + */ +export type TaroConfig = { + /** + * Request type, upload means upload, download means download, not filling means normal request + */ + requestType?: 'upload' | 'download'; +} & TaroRequestConfig & + TaroUploadConfig & + TaroDownloadConfig; +``` + +### Response data + +Because the taro request adapter is compatible with `Taro.request`, `Taro.uploadFile` and `Taro.downloadFile`, but their response value types are slightly different, so the response data type is as follows: + +```typescript +type TaroResponse = + // The response type of Taro.request + | Taro.request.SuccessCallbackResult + + // The response type of Taro.uploadFile + | Taro.uploadFile.SuccessCallbackResult + + // The response type of Taro.downloadFile + | Taro.downloadFile.FileSuccessCallbackResult; +``` + +In actual use, we usually need to process the response data globally. It is recommended to judge the returned data separately. A simple example is as follows: + +```typescript +const alovaInst = createAlova({ + baseURL: 'https://api.alovajs.org', + ...AdapterTaro(), + responded(response) { + const { statusCode, data } = response as Taro.request.SuccessCallbackResult; + if (statusCode >= 400) { + throw new Error('request error'); + } + return data || null; + } +}); +``` diff --git a/versioned_docs/version-2.x/tutorial/08-request-adapter/05-alova-adapter-uniapp.md b/versioned_docs/version-2.x/tutorial/08-request-adapter/05-alova-adapter-uniapp.md index 1466a4d15..890c44aea 100644 --- a/versioned_docs/version-2.x/tutorial/08-request-adapter/05-alova-adapter-uniapp.md +++ b/versioned_docs/version-2.x/tutorial/08-request-adapter/05-alova-adapter-uniapp.md @@ -1,279 +1,278 @@ ---- -title: Uniapp Adapter -sidebar_position: 50 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -:::info Tips - -This plugin only supports vue3 version of uniapp application. - -::: - -## Install - - - - -```bash -npm install @alova/adapter-uniapp --save -``` - - - - -```bash -yarn add @alova/adapter-uniapp -``` - - - - -## Usage - -### create alova - -Calling **AdapterUniapp** will return _Request Adapter_, _Storage Adapter_, and _VueHook_, so you no longer need to set these three items, and the usage is exactly the same. - -```javascript -import { createAlova } from 'alova'; -import AdapterUniapp from '@alova/adapter-uniapp'; - -const alovaInst = createAlova({ - baseURL: 'https://api.alovajs.org', - ...AdapterUniapp() -}); -``` - -### Request - -The usage method of the request is exactly the same as that used in the web environment. Already fully compatible with `uni.request`, you can specify [all configuration items] supported by `uni.request` in _config_ of method instance creation (https://uniapp.dcloud.net.cn/api/request/ request.html) - -```html - - Loading... - The requested data is: {{ data }} - - - -``` - -When using `useRequest/useWatcher` to send a request immediately, it will be executed asynchronously in the `onLoad` hook, so you can access the options data in `methodHandler` as follows: - -```javascript -import { onLoad } from '@dcloudio/uni-app'; - -let options = {}; -onLoad(opt => { - options = opt; -}); -const { loading, data } = useRequest(() => getDetail(options.id)); -``` - -### Upload - -When `requestType: 'upload'` is set in the _config_ of the method instance, it means to upload the file, the request adapter will call `uni.uploadFile`, and the uploaded file data is placed in the data of the method instance, you need to specify in the data `name`, `filePath or files`, and `file` (if necessary), these 4 parameters will be passed to `uni.uploadFile`, at the same time, you can also specify other parameters besides these 4 parameters in data , the request adapter will pass them into the `formData` parameter. - -Similarly, it is fully compatible with `uni.uploadFile`, you can specify [all configuration items] supported by `uni.uploadFile` in _config_ of method instance creation (https://uniapp.dcloud.net.cn/api /request/network-file.html#uploadfile), if there are additional parameters to be set, please specify them in _config_ of the method instance. - -```html - - Uploading... - Upload progress: {{ uploading.loaded }}/{{ uploading.total }} - - - - - -``` - -### download - -When `requestType: 'download'` is set in the _config_ of the method instance, it means to download the file, and the request adapter will call `uni.downloadFile`. - -Similarly, it is fully compatible with `uni.downloadFile`, you can specify [all configuration items] supported by `uni.downloadFile` in _config_ of method instance creation (https://uniapp.dcloud.net.cn/api /request/network-file.html#downloadfile), if there are additional parameters to be set, please specify them in _config_ of the method instance. - -```html - - Downloading... - Download progress: {{ downloading.loaded }}/{{ downloading.total }} - - - - - -``` - -## Mock request adapter compatible - -When using uniapp to develop applications, we may still need to use simulated requests, but by default, the response data of [mock adapter (@alova/mock)](/tutorial/request-adapter/alova-mock) is a `Response` instance, That is, it is compatible with the `GlobalFetch` request adapter by default. When used in the uniapp environment, we need to make the response data of the simulated request adapter compatible with the uniapp adapter, so you need to use the **@alova/adapter-uniapp** package exported `uniappMockResponse` as response adapter. - -```javascript -import { defineMock, createAlovaMockAdapter } from '@alova/mock'; -import AdapterUniapp, { uniappRequestAdapter, uniappMockResponse } from '@alova/adapter-uniapp'; - -const mocks = defineMock({ - //... -}); - -// mock data request adapter -export default createAlovaMockAdapter([mocks], { - // After specifying the uniapp request adapter, requests that do not match the simulated interface will use this adapter to send requests - httpAdapter: uniappRequestAdapter, - - // Simulate the response adapter, after specifying, the response data will be converted to a uniapp-compatible data format - onMockResponse: uniappMockResponse -}); - -export const alovaInst = createAlova({ - baseURL: 'https://api.alovajs.org', - timeout: 5000, - ...AdapterUniapp({ - // Control whether to use the simulated request adapter through environment variables - mockRequest: process.env.NODE_ENV === 'development' ? mockAdapter : undefined - }) - //... -}); -``` - -## Typescript - -uniapp request adapter provides complete type adaptation, and the type of method configuration and response data will exactly match the type of uniapp. - -### method configuration - -When creating a method instance, in addition to the general configuration items in the method, you can also use the configuration items in `uniapp.request`, `uniapp.uploadFile` and `uniapp.downloadFile`, we have removed and method from the type Items that conflict with the common configuration of the instance. - -```typescript -/** - * uni.request requests additional parameters - */ -export type UniappRequestConfig = Omit< - UniNamespace.RequestOptions, - 'url' | 'data' | 'header' | 'method' | 'timeout' | 'success' | 'fail' | 'complete' ->; - -/** - * uni.uploadFile additional parameters - */ -export type UniappUploadConfig = Omit< - UniNamespace.UploadFileOption, - 'url' | 'name' | 'header' | 'formData' | 'timeout' | 'success' | 'fail' | 'complete' ->; - -/** - * uni.downloadFile additional parameters - */ -export type UniappDownloadConfig = Omit< - UniNamespace.DownloadFileOption, - 'url' | 'header' | 'timeout' | 'success' | 'fail' | 'complete' ->; - -/** - * Merged request configuration parameters - */ -export type UniappConfig = { - /** - * Request type, upload means upload, download means download, not filling means normal request - */ - requestType?: 'upload' | 'download'; -} & UniappRequestConfig & - UniappUploadConfig & - UniappDownloadConfig; -``` - -### Response data - -Because the uniapp request adapter is compatible with `uniapp.request`, `uniapp.uploadFile` and `uniapp.downloadFile`, but their response value types are slightly different, so the response data type is as follows: - -```typescript -type UniappResponse = - // The response type of uni.request - | UniNamespace.RequestSuccessCallbackResult - - // The response type of uni.uploadFile - | UniNamespace.UploadFileSuccessCallbackResult - - // The response type of uni.downloadFile - | UniNamespace.DownloadSuccessData; -``` - -In actual use, we usually need to process the response data globally. It is recommended to judge the returned data separately. A simple example is as follows: - -```typescript -const alovaInst = createAlova( - baseURL: 'https://api.alovajs.org', - ...AdapterUniapp(), - responded(response) { - const { statusCode, data } = response as UniNamespace.RequestSuccessCallbackResult; - if (statusCode >= 400) { - throw new Error('request error'); - } - return data || null; - } -}); -``` +--- +title: Uniapp Adapter +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info Tips + +This plugin only supports vue3 version of uniapp application. + +::: + +## Install + + + + +```bash +npm install @alova/adapter-uniapp --save +``` + + + + +```bash +yarn add @alova/adapter-uniapp +``` + + + + +## Usage + +### create alova + +Calling **AdapterUniapp** will return _Request Adapter_, _Storage Adapter_, and _VueHook_, so you no longer need to set these three items, and the usage is exactly the same. + +```javascript +import { createAlova } from 'alova'; +import AdapterUniapp from '@alova/adapter-uniapp'; + +const alovaInst = createAlova({ + baseURL: 'https://api.alovajs.org', + ...AdapterUniapp() +}); +``` + +### Request + +The usage method of the request is exactly the same as that used in the web environment. Already fully compatible with `uni.request`, you can specify [all configuration items] supported by `uni.request` in _config_ of method instance creation (https://uniapp.dcloud.net.cn/api/request/ request.html) + +```html + + Loading... + The requested data is: {{ data }} + + + +``` + +When using `useRequest/useWatcher` to send a request immediately, it will be executed asynchronously in the `onLoad` hook, so you can access the options data in `methodHandler` as follows: + +```javascript +import { onLoad } from '@dcloudio/uni-app'; + +let options = {}; +onLoad(opt => { + options = opt; +}); +const { loading, data } = useRequest(() => getDetail(options.id)); +``` + +### Upload + +When `requestType: 'upload'` is set in the _config_ of the method instance, it means to upload the file, the request adapter will call `uni.uploadFile`, and the uploaded file data is placed in the data of the method instance, you need to specify in the data `name`, `filePath or files`, and `file` (if necessary), these 4 parameters will be passed to `uni.uploadFile`, at the same time, you can also specify other parameters besides these 4 parameters in data , the request adapter will pass them into the `formData` parameter. + +Similarly, it is fully compatible with `uni.uploadFile`, you can specify [all configuration items] supported by `uni.uploadFile` in _config_ of method instance creation (https://uniapp.dcloud.net.cn/api /request/network-file.html#uploadfile), if there are additional parameters to be set, please specify them in _config_ of the method instance. + +```html + + Uploading... + Upload progress: {{ uploading.loaded }}/{{ uploading.total }} + + + + + +``` + +### download + +When `requestType: 'download'` is set in the _config_ of the method instance, it means to download the file, and the request adapter will call `uni.downloadFile`. + +Similarly, it is fully compatible with `uni.downloadFile`, you can specify [all configuration items] supported by `uni.downloadFile` in _config_ of method instance creation (https://uniapp.dcloud.net.cn/api /request/network-file.html#downloadfile), if there are additional parameters to be set, please specify them in _config_ of the method instance. + +```html + + Downloading... + Download progress: {{ downloading.loaded }}/{{ downloading.total }} + + + + + +``` + +## Mock request adapter compatible + +When using uniapp to develop applications, we may still need to use simulated requests, but by default, the response data of [mock adapter (@alova/mock)](/tutorial/request-adapter/alova-mock) is a `Response` instance, That is, it is compatible with the `GlobalFetch` request adapter by default. When used in the uniapp environment, we need to make the response data of the simulated request adapter compatible with the uniapp adapter, so you need to use the **@alova/adapter-uniapp** package exported `uniappMockResponse` as response adapter. + +```javascript +import { defineMock, createAlovaMockAdapter } from '@alova/mock'; +import AdapterUniapp, { uniappRequestAdapter, uniappMockResponse } from '@alova/adapter-uniapp'; + +const mocks = defineMock({ + //... +}); + +// mock data request adapter +export default createAlovaMockAdapter([mocks], { + // After specifying the uniapp request adapter, requests that do not match the simulated interface will use this adapter to send requests + httpAdapter: uniappRequestAdapter, + + // Simulate the response adapter, after specifying, the response data will be converted to a uniapp-compatible data format + onMockResponse: uniappMockResponse +}); + +export const alovaInst = createAlova({ + baseURL: 'https://api.alovajs.org', + timeout: 5000, + ...AdapterUniapp({ + // Control whether to use the simulated request adapter through environment variables + mockRequest: process.env.NODE_ENV === 'development' ? mockAdapter : undefined + }) + //... +}); +``` + +## Typescript + +uniapp request adapter provides complete type adaptation, and the type of method configuration and response data will exactly match the type of uniapp. + +### method configuration + +When creating a method instance, in addition to the general configuration items in the method, you can also use the configuration items in `uniapp.request`, `uniapp.uploadFile` and `uniapp.downloadFile`, we have removed and method from the type Items that conflict with the common configuration of the instance. + +```typescript +/** + * uni.request requests additional parameters + */ +export type UniappRequestConfig = Omit< + UniNamespace.RequestOptions, + 'url' | 'data' | 'header' | 'method' | 'timeout' | 'success' | 'fail' | 'complete' +>; + +/** + * uni.uploadFile additional parameters + */ +export type UniappUploadConfig = Omit< + UniNamespace.UploadFileOption, + 'url' | 'name' | 'header' | 'formData' | 'timeout' | 'success' | 'fail' | 'complete' +>; + +/** + * uni.downloadFile additional parameters + */ +export type UniappDownloadConfig = Omit< + UniNamespace.DownloadFileOption, + 'url' | 'header' | 'timeout' | 'success' | 'fail' | 'complete' +>; + +/** + * Merged request configuration parameters + */ +export type UniappConfig = { + /** + * Request type, upload means upload, download means download, not filling means normal request + */ + requestType?: 'upload' | 'download'; +} & UniappRequestConfig & + UniappUploadConfig & + UniappDownloadConfig; +``` + +### Response data + +Because the uniapp request adapter is compatible with `uniapp.request`, `uniapp.uploadFile` and `uniapp.downloadFile`, but their response value types are slightly different, so the response data type is as follows: + +```typescript +type UniappResponse = + // The response type of uni.request + | UniNamespace.RequestSuccessCallbackResult + + // The response type of uni.uploadFile + | UniNamespace.UploadFileSuccessCallbackResult + + // The response type of uni.downloadFile + | UniNamespace.DownloadSuccessData; +``` + +In actual use, we usually need to process the response data globally. It is recommended to judge the returned data separately. A simple example is as follows: + +```typescript +const alovaInst = createAlova( + baseURL: 'https://api.alovajs.org', + ...AdapterUniapp(), + responded(response) { + const { statusCode, data } = response as UniNamespace.RequestSuccessCallbackResult; + if (statusCode >= 400) { + throw new Error('request error'); + } + return data || null; + } +}); +``` diff --git a/versioned_docs/version-2.x/tutorial/09-framework/01-vue-options.md b/versioned_docs/version-2.x/tutorial/09-framework/01-vue-options.md index 4f2a5ddd9..7f28b451c 100644 --- a/versioned_docs/version-2.x/tutorial/09-framework/01-vue-options.md +++ b/versioned_docs/version-2.x/tutorial/09-framework/01-vue-options.md @@ -1,6 +1,5 @@ --- title: vue2/3 options -sidebar_position: 10 --- import Tabs from '@theme/Tabs'; diff --git a/versioned_docs/version-2.x/tutorial/09-framework/02-solid.md b/versioned_docs/version-2.x/tutorial/09-framework/02-solid.md index 6f7e0e099..eb5fb12f2 100644 --- a/versioned_docs/version-2.x/tutorial/09-framework/02-solid.md +++ b/versioned_docs/version-2.x/tutorial/09-framework/02-solid.md @@ -1,6 +1,5 @@ --- title: solid -sidebar_position: 20 --- Comming soon... diff --git a/versioned_docs/version-2.x/tutorial/09-framework/03-angular.md b/versioned_docs/version-2.x/tutorial/09-framework/03-angular.md index 245469211..dfc05e123 100644 --- a/versioned_docs/version-2.x/tutorial/09-framework/03-angular.md +++ b/versioned_docs/version-2.x/tutorial/09-framework/03-angular.md @@ -1,6 +1,5 @@ --- title: angular -sidebar_position: 30 --- Comming soon... diff --git a/versioned_docs/version-2.x/tutorial/09-framework/04-native-mp.md b/versioned_docs/version-2.x/tutorial/09-framework/04-native-mp.md index a55050983..6d5e8bc1c 100644 --- a/versioned_docs/version-2.x/tutorial/09-framework/04-native-mp.md +++ b/versioned_docs/version-2.x/tutorial/09-framework/04-native-mp.md @@ -1,6 +1,5 @@ --- title: Native mini program(China🇨🇳) -sidebar_position: 40 --- Comming soon... diff --git a/versioned_docs/version-2.x/tutorial/09-framework/05-preact.md b/versioned_docs/version-2.x/tutorial/09-framework/05-preact.md index c70ee8364..a996552a5 100644 --- a/versioned_docs/version-2.x/tutorial/09-framework/05-preact.md +++ b/versioned_docs/version-2.x/tutorial/09-framework/05-preact.md @@ -1,6 +1,5 @@ --- title: preact -sidebar_position: 50 --- Comming soon... diff --git a/versioned_docs/version-2.x/tutorial/09-framework/06-qwik.md b/versioned_docs/version-2.x/tutorial/09-framework/06-qwik.md index 3d2cd2c94..00c0f06e3 100644 --- a/versioned_docs/version-2.x/tutorial/09-framework/06-qwik.md +++ b/versioned_docs/version-2.x/tutorial/09-framework/06-qwik.md @@ -1,6 +1,5 @@ --- title: qwik -sidebar_position: 60 --- Comming soon... diff --git a/versioned_docs/version-2.x/tutorial/09-framework/07-lit.md b/versioned_docs/version-2.x/tutorial/09-framework/07-lit.md index 8d3500f34..f3b5a8b92 100644 --- a/versioned_docs/version-2.x/tutorial/09-framework/07-lit.md +++ b/versioned_docs/version-2.x/tutorial/09-framework/07-lit.md @@ -1,6 +1,5 @@ --- title: lit -sidebar_position: 70 --- Comming soon... diff --git a/versioned_docs/version-2.x/tutorial/09-framework/08-stencil.md b/versioned_docs/version-2.x/tutorial/09-framework/08-stencil.md index e651aacfd..cb9b09671 100644 --- a/versioned_docs/version-2.x/tutorial/09-framework/08-stencil.md +++ b/versioned_docs/version-2.x/tutorial/09-framework/08-stencil.md @@ -1,6 +1,5 @@ --- title: stencil -sidebar_position: 80 --- Comming soon... diff --git a/versioned_docs/version-2.x/tutorial/10-custom/01-overview.md b/versioned_docs/version-2.x/tutorial/10-custom/01-overview.md index 0c66df6ee..e1e58a969 100644 --- a/versioned_docs/version-2.x/tutorial/10-custom/01-overview.md +++ b/versioned_docs/version-2.x/tutorial/10-custom/01-overview.md @@ -1,26 +1,25 @@ ---- -title: Overview -sidebar_position: 10 ---- - -Alova has high scalability. In addition to providing common features such as core caching mechanism, request sharing mechanism, and state management, it also provides various customization functions and middleware mechanisms, which can adapt to different js environments and customize requests. Strategy. - -## Adapter - -In order to meet the running requirements of js in different environments, you can customize the request adapter, storage adapter, and even the state adapter of the UI framework, which will be introduced in detail in the next chapters. Some adapter examples are listed below. - -- [fetch adapter](https://github.com/alovajs/alova/blob/main/src/predefine/GlobalFetch.ts) -- [localStorage storage adapter](https://github.com/alovajs/alova/blob/main/src/predefine/globalLocalStorage.ts) -- [vue states hook](https://github.com/alovajs/alova/blob/main/src/predefine/VueHook.ts) - -You can also combine multiple types of adapters into a collection, for example [Uniapp Adapter](/tutorial/request-adapter/alova-adapter-uniapp). - -## Write request strategy - -alova's request strategy is separate from the alova core library, so that developers can also take advantage of alova's high scalability to write their own request strategies. Usually, a custom request strategy is based on the combination of `useRequest`, `useWatcher` and `useFetcher`, and writing [middleware](/tutorial/advanced/middleware), cache manipulation functions for them to control their The request method, so as to realize the request strategy of various effects. - -The request strategies in **@alova/scene** are well represented, and it is strongly recommended that you refer to the source code for inspiration. - -- [Paging request strategy source code](https://github.com/alovajs/scene/blob/main/src/hooks/pagination/usePagination.js) -- [Captcha strategy source code](https://github.com/alovajs/scene/blob/main/src/hooks/useCaptcha.ts) -- [Form submission strategy source code](https://github.com/alovajs/scene/blob/main/src/hooks/useForm.ts) +--- +title: Overview +--- + +Alova has high scalability. In addition to providing common features such as core caching mechanism, request sharing mechanism, and state management, it also provides various customization functions and middleware mechanisms, which can adapt to different js environments and customize requests. Strategy. + +## Adapter + +In order to meet the running requirements of js in different environments, you can customize the request adapter, storage adapter, and even the state adapter of the UI framework, which will be introduced in detail in the next chapters. Some adapter examples are listed below. + +- [fetch adapter](https://github.com/alovajs/alova/blob/main/src/predefine/GlobalFetch.ts) +- [localStorage storage adapter](https://github.com/alovajs/alova/blob/main/src/predefine/globalLocalStorage.ts) +- [vue states hook](https://github.com/alovajs/alova/blob/main/src/predefine/VueHook.ts) + +You can also combine multiple types of adapters into a collection, for example [Uniapp Adapter](/tutorial/request-adapter/alova-adapter-uniapp). + +## Write request strategy + +alova's request strategy is separate from the alova core library, so that developers can also take advantage of alova's high scalability to write their own request strategies. Usually, a custom request strategy is based on the combination of `useRequest`, `useWatcher` and `useFetcher`, and writing [middleware](/tutorial/advanced/middleware), cache manipulation functions for them to control their The request method, so as to realize the request strategy of various effects. + +The request strategies in **@alova/scene** are well represented, and it is strongly recommended that you refer to the source code for inspiration. + +- [Paging request strategy source code](https://github.com/alovajs/scene/blob/main/src/hooks/pagination/usePagination.js) +- [Captcha strategy source code](https://github.com/alovajs/scene/blob/main/src/hooks/useCaptcha.ts) +- [Form submission strategy source code](https://github.com/alovajs/scene/blob/main/src/hooks/useForm.ts) diff --git a/versioned_docs/version-2.x/tutorial/10-custom/02-custom-http-adapter.md b/versioned_docs/version-2.x/tutorial/10-custom/02-custom-http-adapter.md index ad6a9729a..3f64236b6 100644 --- a/versioned_docs/version-2.x/tutorial/10-custom/02-custom-http-adapter.md +++ b/versioned_docs/version-2.x/tutorial/10-custom/02-custom-http-adapter.md @@ -1,193 +1,192 @@ ---- -title: Custom Request Adapter -sidebar_position: 20 ---- - -Remember how to create an Alova instance? - -```javascript -const alovaInstance = createAlova({ - //... - requestAdapter: GlobalFetch() -}); -``` - -`requestAdapter` is a request adapter. Internal request sending and receiving will depend on the request adapter. `GlobalFetch` manages requests through fetch api. In most cases, we can use it. However, when `alova` When running in an environment where fetch api is not available (such as app, applet), it is necessary to replace a request adapter that supports the current environment. - -So how should you customize a request adapter? Very simple, it is actually a function, which is called every time a request is made, and returns an object, which contains such things as `url`, `method`, `data`, `headers`, `timeout`, etc. Request related data sets. Although there are many fields, we only need to access the data we need. - -## Request adapter structure - -The request adapter will receive request-related parameters and the currently requesting method instance, and return a set of response-related functions. - -```javascript -function CustomRequestAdapter(requestElements, methodInstance) { - // send request... - return { - async response() { - // ...return the response data - }, - async headers() { - // Asynchronous function that returns response headers - }, - abort() { - // Abort request, this function will be triggered when abort is called externally - }, - onDownload(updateDownloadProgress) { - // Download progress information, internally call updateDownloadProgress continuously to update the download progress - }, - onUpload(updateUploadProgress) { - // Upload progress information, internally call updateUploadProgress continuously to update the upload progress - } - }; -} -``` - -### Request parameter details - -**requestElements** - -Relevant elements of the send request, including the following data. - -```typescript -interface RequestElements { - // request url, the get parameter is already included - readonly url: string; - - // Request type, such as GET, POST, PUT, etc. - readonly type: MethodType; - - // Request header information, object - readonly headers: Arg; - - // request body information - readonly data?: RequestBody; -} -``` - -**methodInstance** - -The method instance of the current request - -### Return parameter details - -**response (required)** - -An asynchronous function, the function returns the response value, which will be passed to the global response interceptor responded; - -**headers (required)** - -An asynchronous function, the response header object returned by the function will be passed to the transformData conversion hook function of the Method instance; - -**abort (required)** - -A common function, which is used for aborting request. All aborting requests will eventually call this function to execute; - -**onDownload (optional)** - -An ordinary function that receives a callback function that updates the download progress, and customizes the frequency of the progress update within this function, in this example simulating an update every 100 milliseconds. The `updateDownloadProgress` callback function receives two parameters, the first parameter is the total size, and the second parameter is the downloaded size; - -**onUpload (optional)** - -An ordinary function that receives a callback function that updates the upload progress, and customizes the frequency of the progress update within this function, in this example simulating an update every 100 milliseconds. The `updateUploadProgress` callback function receives two parameters, the first parameter is the total size, and the second parameter is the uploaded size; - -## XMLHttpRequest request adapter example - -The following is an example of an adapter that sends requests through XMLHttpRequest, mainly to demonstrate how to write the adapter, the code is incomplete and cannot be run. - -```javascript -function XMLHttpRequestAdapter(requestElements, methodInstance) { - // Deconstruct the data that needs to be used - const { url, type, data, headers } = config; - - // send request - const xhr = new XMLHttpRequest(); - xhr.open(type, url); - for (const key in headers) { - xhr.setRequestHeader(key, headers[key]); - } - const responsePromise = new Promise((resolve, reject) => { - xhr.addEventListener('load', event => { - // process response data - resolve(/* ... */); - }); - xhr.addEventListener('error', event => { - // Handle request errors - reject(/* ... */); - }); - }); - - xhr.send(JSON.stringify(data)); - - return { - // Asynchronous function that returns response data - response: () => responsePromise, - - // Asynchronous function that returns response headers - headers: () => responsePromise.then(() => xhr.getAllResponseHeaders()), - abort: () => { - xhr.abort(); - }, - - // Download progress information, internally call updateDownloadProgress continuously to update the download progress - onDownload: updateDownloadProgress => { - xhr.addEventListener('progress', event => { - // data receiving progress - updateDownloadProgress(event.total, event.loaded); - }); - }, - - // Upload progress information, internally call updateUploadProgress continuously to update the upload progress - onUpload: updateUploadProgress => { - xhr.upload.onprogress = event => { - updateUploadProgress(event.total, event.loaded); - }; - } - }; -} -``` - -:::note - -More complete request adapter details can be found in [GlobalFetch source code](https://github.com/alovajs/alova/blob/main/src/predefine/GlobalFetch.ts) to understand. - -::: - -## Request adapter type - -The types of the global `beforeRequest`, `responded` interceptors, and the configuration object when the `Method` instance is created will be automatically inferred based on the type provided by the request adapter. The following are the types of GlobalFetch. - -```javascript -import type { RequestElements, Method, ProgressUpdater } from 'alova'; - -export type GlobalFetch = () => ( - elements: RequestElements, - method: Method -) => { - response: () => Promise, - headers: () => Promise, - onDownload: (handler: ProgressUpdater) => void, - abort: () => void -}; -``` - -Three types of values ​​of `RC`, `RE` and `RH` are specified in this type, so the type given by the request adapter will be automatically inferred in the global interceptor, method instance configuration and other places. - -They are expressed as: - -- **RC**: Abbreviation of _RequestConfig_, request configuration object type -- **RH**: Abbreviation of _ResponseHeader_, response header object type -- **RE**: Abbreviation of _Response_, response type - -If you are using **GlobalFetch**, their types will be inferred as: - -- **RC**: fetch api's request configuration object `RequestInit`; -- **RH**: Response header object `Headers`; -- **RE**: response object `Response`; - -In order to facilitate the definition of the request adapter type, alova also provides an adapter type. You only need to pass in the `RC/RE/RH` generic parameters as needed. - -```typescript -import type { AlovaRequestAdapter } from 'alova'; -type CustomRequestAdpater = AlovaRequestAdapter; -``` +--- +title: Custom Request Adapter +--- + +Remember how to create an Alova instance? + +```javascript +const alovaInstance = createAlova({ + //... + requestAdapter: GlobalFetch() +}); +``` + +`requestAdapter` is a request adapter. Internal request sending and receiving will depend on the request adapter. `GlobalFetch` manages requests through fetch api. In most cases, we can use it. However, when `alova` When running in an environment where fetch api is not available (such as app, applet), it is necessary to replace a request adapter that supports the current environment. + +So how should you customize a request adapter? Very simple, it is actually a function, which is called every time a request is made, and returns an object, which contains such things as `url`, `method`, `data`, `headers`, `timeout`, etc. Request related data sets. Although there are many fields, we only need to access the data we need. + +## Request adapter structure + +The request adapter will receive request-related parameters and the currently requesting method instance, and return a set of response-related functions. + +```javascript +function CustomRequestAdapter(requestElements, methodInstance) { + // send request... + return { + async response() { + // ...return the response data + }, + async headers() { + // Asynchronous function that returns response headers + }, + abort() { + // Abort request, this function will be triggered when abort is called externally + }, + onDownload(updateDownloadProgress) { + // Download progress information, internally call updateDownloadProgress continuously to update the download progress + }, + onUpload(updateUploadProgress) { + // Upload progress information, internally call updateUploadProgress continuously to update the upload progress + } + }; +} +``` + +### Request parameter details + +**requestElements** + +Relevant elements of the send request, including the following data. + +```typescript +interface RequestElements { + // request url, the get parameter is already included + readonly url: string; + + // Request type, such as GET, POST, PUT, etc. + readonly type: MethodType; + + // Request header information, object + readonly headers: Arg; + + // request body information + readonly data?: RequestBody; +} +``` + +**methodInstance** + +The method instance of the current request + +### Return parameter details + +**response (required)** + +An asynchronous function, the function returns the response value, which will be passed to the global response interceptor responded; + +**headers (required)** + +An asynchronous function, the response header object returned by the function will be passed to the transformData conversion hook function of the Method instance; + +**abort (required)** + +A common function, which is used for aborting request. All aborting requests will eventually call this function to execute; + +**onDownload (optional)** + +An ordinary function that receives a callback function that updates the download progress, and customizes the frequency of the progress update within this function, in this example simulating an update every 100 milliseconds. The `updateDownloadProgress` callback function receives two parameters, the first parameter is the total size, and the second parameter is the downloaded size; + +**onUpload (optional)** + +An ordinary function that receives a callback function that updates the upload progress, and customizes the frequency of the progress update within this function, in this example simulating an update every 100 milliseconds. The `updateUploadProgress` callback function receives two parameters, the first parameter is the total size, and the second parameter is the uploaded size; + +## XMLHttpRequest request adapter example + +The following is an example of an adapter that sends requests through XMLHttpRequest, mainly to demonstrate how to write the adapter, the code is incomplete and cannot be run. + +```javascript +function XMLHttpRequestAdapter(requestElements, methodInstance) { + // Deconstruct the data that needs to be used + const { url, type, data, headers } = config; + + // send request + const xhr = new XMLHttpRequest(); + xhr.open(type, url); + for (const key in headers) { + xhr.setRequestHeader(key, headers[key]); + } + const responsePromise = new Promise((resolve, reject) => { + xhr.addEventListener('load', event => { + // process response data + resolve(/* ... */); + }); + xhr.addEventListener('error', event => { + // Handle request errors + reject(/* ... */); + }); + }); + + xhr.send(JSON.stringify(data)); + + return { + // Asynchronous function that returns response data + response: () => responsePromise, + + // Asynchronous function that returns response headers + headers: () => responsePromise.then(() => xhr.getAllResponseHeaders()), + abort: () => { + xhr.abort(); + }, + + // Download progress information, internally call updateDownloadProgress continuously to update the download progress + onDownload: updateDownloadProgress => { + xhr.addEventListener('progress', event => { + // data receiving progress + updateDownloadProgress(event.total, event.loaded); + }); + }, + + // Upload progress information, internally call updateUploadProgress continuously to update the upload progress + onUpload: updateUploadProgress => { + xhr.upload.onprogress = event => { + updateUploadProgress(event.total, event.loaded); + }; + } + }; +} +``` + +:::note + +More complete request adapter details can be found in [GlobalFetch source code](https://github.com/alovajs/alova/blob/main/src/predefine/GlobalFetch.ts) to understand. + +::: + +## Request adapter type + +The types of the global `beforeRequest`, `responded` interceptors, and the configuration object when the `Method` instance is created will be automatically inferred based on the type provided by the request adapter. The following are the types of GlobalFetch. + +```javascript +import type { RequestElements, Method, ProgressUpdater } from 'alova'; + +export type GlobalFetch = () => ( + elements: RequestElements, + method: Method +) => { + response: () => Promise, + headers: () => Promise, + onDownload: (handler: ProgressUpdater) => void, + abort: () => void +}; +``` + +Three types of values ​​of `RC`, `RE` and `RH` are specified in this type, so the type given by the request adapter will be automatically inferred in the global interceptor, method instance configuration and other places. + +They are expressed as: + +- **RC**: Abbreviation of _RequestConfig_, request configuration object type +- **RH**: Abbreviation of _ResponseHeader_, response header object type +- **RE**: Abbreviation of _Response_, response type + +If you are using **GlobalFetch**, their types will be inferred as: + +- **RC**: fetch api's request configuration object `RequestInit`; +- **RH**: Response header object `Headers`; +- **RE**: response object `Response`; + +In order to facilitate the definition of the request adapter type, alova also provides an adapter type. You only need to pass in the `RC/RE/RH` generic parameters as needed. + +```typescript +import type { AlovaRequestAdapter } from 'alova'; +type CustomRequestAdpater = AlovaRequestAdapter; +``` diff --git a/versioned_docs/version-2.x/tutorial/10-custom/03-custom-storage-adapter.md b/versioned_docs/version-2.x/tutorial/10-custom/03-custom-storage-adapter.md index 2cd749f8f..7633add11 100644 --- a/versioned_docs/version-2.x/tutorial/10-custom/03-custom-storage-adapter.md +++ b/versioned_docs/version-2.x/tutorial/10-custom/03-custom-storage-adapter.md @@ -1,48 +1,47 @@ ---- -title: Custom Storage Adapter -sidebar_position: 30 ---- - -Alova involves multiple functions that require data persistence, such as persistent cache, silent submission, and offline submission. **By default, alova will use `localStorage` to store persistent data**, but considering the non-browser environment, it also supports customization. - -Customizing the storage adapter is also very simple, you only need to specify the functions for saving data, getting data, and removing data, roughly like this. - -```javascript -const customStorageAdapter = { - set(key, value) { - // Save the data, value is structured data, which can be converted to a string by calling JSON.stringify - }, - get(key) { - // get data, return structured data, which can be converted by calling JSON.parse - }, - remove(key) { - // remove data - } -}; -``` - -Then pass in this adapter when creating an `alova` instance. - -```javascript -const alovaInstance = createAlova({ - //... - storageAdapter: customStorageAdapter -}); -``` - -## SessionStorage storage adapter example - -```javascript -const sessionStorageAdapter = { - set(key, value) { - sessionStorage.setItem(key, JSON.stringify(value)); - }, - get(key) { - const data = sessionStorage.getItem(key); - return data ? JSON.parse(data) : data; - }, - remove(key) { - sessionStorage.removeItem(key); - } -}; -``` +--- +title: Custom Storage Adapter +--- + +Alova involves multiple functions that require data persistence, such as persistent cache, silent submission, and offline submission. **By default, alova will use `localStorage` to store persistent data**, but considering the non-browser environment, it also supports customization. + +Customizing the storage adapter is also very simple, you only need to specify the functions for saving data, getting data, and removing data, roughly like this. + +```javascript +const customStorageAdapter = { + set(key, value) { + // Save the data, value is structured data, which can be converted to a string by calling JSON.stringify + }, + get(key) { + // get data, return structured data, which can be converted by calling JSON.parse + }, + remove(key) { + // remove data + } +}; +``` + +Then pass in this adapter when creating an `alova` instance. + +```javascript +const alovaInstance = createAlova({ + //... + storageAdapter: customStorageAdapter +}); +``` + +## SessionStorage storage adapter example + +```javascript +const sessionStorageAdapter = { + set(key, value) { + sessionStorage.setItem(key, JSON.stringify(value)); + }, + get(key) { + const data = sessionStorage.getItem(key); + return data ? JSON.parse(data) : data; + }, + remove(key) { + sessionStorage.removeItem(key); + } +}; +``` diff --git a/versioned_docs/version-2.x/tutorial/10-custom/04-custom-stateshook.md b/versioned_docs/version-2.x/tutorial/10-custom/04-custom-stateshook.md index e2b686346..67e5af1a4 100644 --- a/versioned_docs/version-2.x/tutorial/10-custom/04-custom-stateshook.md +++ b/versioned_docs/version-2.x/tutorial/10-custom/04-custom-stateshook.md @@ -1,101 +1,100 @@ ---- -title: Custom States Hook -sidebar_position: 50 ---- - -Remember how to create an Alova instance? - -```javascript -const alovaInstance = createAlova({ - //... - statesHook: ReactHook -}); -``` - -`statesHook` will decide which MVVM library state to return when requesting, alova currently provides **VueHook, ReactHook, svelteHook**. - -In most cases, you should not use this function, but if you need to adapt to more MVVM libraries that alova does not support, you need to customize `statesHook`. - -`statesHook` is an ordinary object that contains specific functions, but these basically do not involve algorithms, let's see how **VueHook** is written. - -## statesHook structure - -statesHook is represented by an object, the following is an example of **VueHook**. - -```javascript -import { ref, watch, onUnmounted } from 'vue'; - -const VueHook = { - // state creation function - create: rawData => ref(data), - - // state export function - export: state => state, - - // dehydration function - dehydrate: state => state.value, - - // Reactive state update function - update: (newVal, states) => { - Object.keys(newVal).forEach(key => { - states[key].value = newVal[key]; - }); - }, - - // request send control function - effectRequest({ handler, removeStates, saveStates, immediate, frontStates, watchingStates }) { - // Remove the corresponding state when the component is uninstalled - onUnmounted(removeStates); - - // When calling useRequest and useFetcher, watchingStates is undefined - if (!watchingStates) { - handler(); - return; - } - - // When calling useWatcher, watchingStates is an array of states that need to be monitored - // When immediate is true, it means that the request needs to be sent immediately - watch(watchingStates, handler, { immediate }); - } -}; -``` - -## Custom statesHook function description - -> All of the following 5 functions must be specified. - -**create** - -Responsive state creation function, `loading`, `error`, `data`, `downloading`, `uploading`, etc. are all created by calling this function, such as the vue3 project will be created as a ref value; - -**export** - -State export function, this function receives the responsive state created by the create function, and exports the final state for developers to use, where the state exported by `VueHook` is the original state; - -**dehydrate** - -Dehydration function, which means converting the responsive state into normal data, is the opposite operation to create, in `updateState`; - -**update** - -Responsive status update function, the status update maintained internally by `alova` is done through this function. This function receives two parameters, the first parameter is the new data object, and the second parameter is the map collection of the original responsive state, here you can write a fixed cycle to update `states`; - -**effectRequest** - -Request sending control function, it will execute this function immediately when `useRequest`, `useWatcher`, `useFetcher` are called, we need to complete three things in this function: - -1. When the current component is uninstalled, call the removeStates function to remove the responsive state involved in the current component to avoid memory overflow; -2. When calling useWatcher, bind the state monitor, and call the sendRequest function when the state changes. You can use whether `states` is an array to judge whether `useWatcher` is called. At the same time, the `immediate` parameter is used to judge whether `useWatcher` is called Whether the request needs to be sent immediately; -3. When calling `useRequest` and `useFetcher`, call sendRequest to send a request, at this time `states` is `undefined`; - -:::warning Caution - -If the library involved in statesHook is like `react`, the use hook of `alova` will be called every time it is re-rendered, then the `saveStates` function needs to be triggered every time it is re-rendered in `effectRequest`, this is because `react `Every re-render refreshes its state references, so we need to re-save them again. - -::: - -[ReactHook source code click here to view](https://github.com/alovajs/alova/blob/main/src/predefine/ReactHook.ts) - -## statesHook type - -If you want it to support typescript when customizing statesHook, you can [click here to view](/tutorial/combine-framework/typescript) +--- +title: Custom States Hook +--- + +Remember how to create an Alova instance? + +```javascript +const alovaInstance = createAlova({ + //... + statesHook: ReactHook +}); +``` + +`statesHook` will decide which MVVM library state to return when requesting, alova currently provides **VueHook, ReactHook, svelteHook**. + +In most cases, you should not use this function, but if you need to adapt to more MVVM libraries that alova does not support, you need to customize `statesHook`. + +`statesHook` is an ordinary object that contains specific functions, but these basically do not involve algorithms, let's see how **VueHook** is written. + +## statesHook structure + +statesHook is represented by an object, the following is an example of **VueHook**. + +```javascript +import { ref, watch, onUnmounted } from 'vue'; + +const VueHook = { + // state creation function + create: rawData => ref(data), + + // state export function + export: state => state, + + // dehydration function + dehydrate: state => state.value, + + // Reactive state update function + update: (newVal, states) => { + Object.keys(newVal).forEach(key => { + states[key].value = newVal[key]; + }); + }, + + // request send control function + effectRequest({ handler, removeStates, saveStates, immediate, frontStates, watchingStates }) { + // Remove the corresponding state when the component is uninstalled + onUnmounted(removeStates); + + // When calling useRequest and useFetcher, watchingStates is undefined + if (!watchingStates) { + handler(); + return; + } + + // When calling useWatcher, watchingStates is an array of states that need to be monitored + // When immediate is true, it means that the request needs to be sent immediately + watch(watchingStates, handler, { immediate }); + } +}; +``` + +## Custom statesHook function description + +> All of the following 5 functions must be specified. + +**create** + +Responsive state creation function, `loading`, `error`, `data`, `downloading`, `uploading`, etc. are all created by calling this function, such as the vue3 project will be created as a ref value; + +**export** + +State export function, this function receives the responsive state created by the create function, and exports the final state for developers to use, where the state exported by `VueHook` is the original state; + +**dehydrate** + +Dehydration function, which means converting the responsive state into normal data, is the opposite operation to create, in `updateState`; + +**update** + +Responsive status update function, the status update maintained internally by `alova` is done through this function. This function receives two parameters, the first parameter is the new data object, and the second parameter is the map collection of the original responsive state, here you can write a fixed cycle to update `states`; + +**effectRequest** + +Request sending control function, it will execute this function immediately when `useRequest`, `useWatcher`, `useFetcher` are called, we need to complete three things in this function: + +1. When the current component is uninstalled, call the removeStates function to remove the responsive state involved in the current component to avoid memory overflow; +2. When calling useWatcher, bind the state monitor, and call the sendRequest function when the state changes. You can use whether `states` is an array to judge whether `useWatcher` is called. At the same time, the `immediate` parameter is used to judge whether `useWatcher` is called Whether the request needs to be sent immediately; +3. When calling `useRequest` and `useFetcher`, call sendRequest to send a request, at this time `states` is `undefined`; + +:::warning Caution + +If the library involved in statesHook is like `react`, the use hook of `alova` will be called every time it is re-rendered, then the `saveStates` function needs to be triggered every time it is re-rendered in `effectRequest`, this is because `react `Every re-render refreshes its state references, so we need to re-save them again. + +::: + +[ReactHook source code click here to view](https://github.com/alovajs/alova/blob/main/src/predefine/ReactHook.ts) + +## statesHook type + +If you want it to support typescript when customizing statesHook, you can [click here to view](/tutorial/combine-framework/typescript) diff --git a/versioned_docs/version-2.x/tutorial/11-others/01-RSM.md b/versioned_docs/version-2.x/tutorial/11-others/01-RSM.md index 854517793..63820c100 100644 --- a/versioned_docs/version-2.x/tutorial/11-others/01-RSM.md +++ b/versioned_docs/version-2.x/tutorial/11-others/01-RSM.md @@ -1,61 +1,60 @@ ---- -title: Request Scene Model (RSM) -sidebar_position: 10 ---- - -## What is the request scene model - -The request scenario model is based on the perspective of the client. It describes the abstract model of the client from triggering the request intent to receiving the request result. It consists of four stages: request timing, request behavior, request event, and response management. For example, when making a request, you often need to think about the following questions, - -1. When the request is made; -2. Whether to display the request status; -3. Do you need to retry the request on failure; -4. How to process the response data; -5. Do you need to encrypt the request parameters; -6. Whether to cache the frequently used response data; -7. How to operate data across pages; -8. How to process requests in a weak or disconnected network environment; -9. ... - -`fetch` or `axios` are often more focused on how to interact with the server, but we always need to deal with the above problems by ourselves. These functions that are beneficial to application performance and stability will always allow programmers to write low-maintenance sexual code. The request scene model is to abstract all links from preparing the request to processing the response data, so as to cover the model of the entire CS interaction life cycle from the perspective of the front end. `alova` is a library that requests scene models, it is a supplement to request libraries such as `axios`, not a substitute. - -> CS interaction: generally refers to data interaction between all client types and servers - -## Request scene model - -![RSM](/img/rsm-en.png) - -## request timing - -Describe when a request needs to be made, implemented with `useHook` in `alova`. - -- Initialize display data, such as just entering a certain interface or sub-interface; -- Human-computer interaction triggers CS interaction, and the request needs to be changed again, such as page turning, filtering, sorting, fuzzy search, etc.; -- Send requests in an anti-shake manner, avoid view data flickering, and reduce server pressure -- Preloading data, such as preloading the content of the next page in a page, predicting that the user clicks a button to pre-fetch data; -- To operate server data, it is necessary to issue a request for addition, deletion, modification and query, such as submitting data, deleting data, etc.; -- Synchronize server status, such as polling requests in scenarios where data changes rapidly, and re-pull data after operating a certain data; - -## Request Behavior - -Describes how to process the request, implemented as a Method abstraction in `alova`. - -- Placeholder request, when requesting, display loading, skeleton diagram, or real data used last time; -- Cache high-frequency responses, multiple execution requests will use fresh data; -- Multi-request serial and parallel; -- The retry mechanism of important interfaces reduces the probability of request failure caused by network instability; -- Submit silently. When you only care about submitting data, directly respond to the success event after submitting the request, and the background guarantees that the request is successful; -- Offline submission, the submitted data will be temporarily stored locally when offline, and then submitted after the network connection; - -## request event - -Indicates sending a request with request parameters and getting a response. `alova` can work with any request library or native solution such as `axios`, `fetch`, `XMLHttpRequest`. - -## Response management - -`alova` makes the response data stateful and manages it in a unified manner, refreshes the view data and operates the cache at the request level, avoids operations at the component level, and is more elegant and unified. - -- Remove the cached response data, which will be pulled from the server when the request is made again; -- Update the cached response data, which can update the response data at any location, which is very helpful for updating data across pages; -- Refresh the response data, which can re-refresh the response data at any position, and is also very helpful for updating data across pages; -- Customize the cache setting. When requesting batch data, you can manually set the cache for the batch data one by one, so as to meet the cache hit of subsequent single data; +--- +title: Request Scene Model (RSM) +--- + +## What is the request scene model + +The request scenario model is based on the perspective of the client. It describes the abstract model of the client from triggering the request intent to receiving the request result. It consists of four stages: request timing, request behavior, request event, and response management. For example, when making a request, you often need to think about the following questions, + +1. When the request is made; +2. Whether to display the request status; +3. Do you need to retry the request on failure; +4. How to process the response data; +5. Do you need to encrypt the request parameters; +6. Whether to cache the frequently used response data; +7. How to operate data across pages; +8. How to process requests in a weak or disconnected network environment; +9. ... + +`fetch` or `axios` are often more focused on how to interact with the server, but we always need to deal with the above problems by ourselves. These functions that are beneficial to application performance and stability will always allow programmers to write low-maintenance sexual code. The request scene model is to abstract all links from preparing the request to processing the response data, so as to cover the model of the entire CS interaction life cycle from the perspective of the front end. `alova` is a library that requests scene models, it is a supplement to request libraries such as `axios`, not a substitute. + +> CS interaction: generally refers to data interaction between all client types and servers + +## Request scene model + +![RSM](/img/rsm-en.png) + +## request timing + +Describe when a request needs to be made, implemented with `useHook` in `alova`. + +- Initialize display data, such as just entering a certain interface or sub-interface; +- Human-computer interaction triggers CS interaction, and the request needs to be changed again, such as page turning, filtering, sorting, fuzzy search, etc.; +- Send requests in an anti-shake manner, avoid view data flickering, and reduce server pressure +- Preloading data, such as preloading the content of the next page in a page, predicting that the user clicks a button to pre-fetch data; +- To operate server data, it is necessary to issue a request for addition, deletion, modification and query, such as submitting data, deleting data, etc.; +- Synchronize server status, such as polling requests in scenarios where data changes rapidly, and re-pull data after operating a certain data; + +## Request Behavior + +Describes how to process the request, implemented as a Method abstraction in `alova`. + +- Placeholder request, when requesting, display loading, skeleton diagram, or real data used last time; +- Cache high-frequency responses, multiple execution requests will use fresh data; +- Multi-request serial and parallel; +- The retry mechanism of important interfaces reduces the probability of request failure caused by network instability; +- Submit silently. When you only care about submitting data, directly respond to the success event after submitting the request, and the background guarantees that the request is successful; +- Offline submission, the submitted data will be temporarily stored locally when offline, and then submitted after the network connection; + +## request event + +Indicates sending a request with request parameters and getting a response. `alova` can work with any request library or native solution such as `axios`, `fetch`, `XMLHttpRequest`. + +## Response management + +`alova` makes the response data stateful and manages it in a unified manner, refreshes the view data and operates the cache at the request level, avoids operations at the component level, and is more elegant and unified. + +- Remove the cached response data, which will be pulled from the server when the request is made again; +- Update the cached response data, which can update the response data at any location, which is very helpful for updating data across pages; +- Refresh the response data, which can re-refresh the response data at any position, and is also very helpful for updating data across pages; +- Customize the cache setting. When requesting batch data, you can manually set the cache for the batch data one by one, so as to meet the cache hit of subsequent single data; diff --git a/versioned_docs/version-2.x/tutorial/11-others/02-comparison.md b/versioned_docs/version-2.x/tutorial/11-others/02-comparison.md index c7099fc71..1c4557231 100644 --- a/versioned_docs/version-2.x/tutorial/11-others/02-comparison.md +++ b/versioned_docs/version-2.x/tutorial/11-others/02-comparison.md @@ -1,93 +1,92 @@ ---- -title: Compare with other libraries -sidebar_position: 20 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -## Compare with axios - -axios provides a very simple and easy-to-use HTTP request function based on promise. It only requires a simple line of code to send and receive requests, and can run in the browser and nodejs environment. It is a very excellent request js library. - -But axios focuses on sending requests and receiving responses, which means that if you need to write more code by yourself to actively optimize the request function, and alova is like a weapon of axios, combining axios with alova can get more powerful request capabilities. The following are the request management capabilities added by alova to axios. - -### alova provides automated request status management for axios - -When you only use axios, you usually need to maintain request-related status by yourself. You can get automated request status management capabilities by using alova's use hook. - - - - -```javascript -// vue3 example -const loading = ref(false); -const data = ref({}); -const error = ref(null); -const request = async () => { - try { - loading.value = true; - data.value = await axios.get('/xxx'); - } catch (e) { - error.value = e; - } - loading.value = false; -}; -mounted(request); -``` - - - - -```javascript -// Use axios as alova's request adapter -const { loading, data, error } = useRequest(alova.Get('/xxx')); -``` - - - - -### alova provides high-performance request strategies out of the box - -alova provides you with [multiple high-performance request strategy modules](/tutorial/strategy). You can use different modules according to different request scenarios, which axios does not have. - -### alova provides response data cache for axios - -alova provides 3 caching modes to meet different caching scenarios, namely memory mode, cache occupying mode, and recovery mode. They are component-independent and can hit the cache as long as the request address and parameters are the same, unless you turn it off. Response data caching can greatly improve request fluency and reduce server pressure. - -### alova provides request sharing function for axios - -Request sharing will reuse the same request when sending multiple identical requests at the same time. It can also improve application fluency and reduce server pressure. - -### alova provides data pre-fetching for axios - -Requesting the data to be used in advance can also greatly improve application fluency. - -### alova can manage request states - -You can use alova across any component hierarchy to access stateful data in other components, which allows you to reduce some of the trouble of cross-component communication. - -## Compared with react-query and swr - -react-query is a powerful asynchronous state management, and swr is a React Hooks library for data requests. Their common feature is the use of use hooks to send and manage requests, and data caching functions. For them, alova has the following differences at. - -### alova has different goals - -In fact, alova's use hook also refers to the design of react-query and swr, but alova chooses the direction of the request strategy library. You can use different request strategy modules in different request scenarios, allowing you to write less code. At the same time, more efficient Client-Server data interaction can also be achieved. - -### Method proxy design - -Both react-query and swr use `axios` or `fetch api` directly in use hook to send requests, while alova uses the `Method` proxy design mode. This design has the following 3 benefits: - -1. Unified usage without different usage depending on the platform or UI framework. -2. Request libraries such as `axios` and `fetch api` are decoupled from each API in the form of request adapters, which allows alova to provide a unified development experience and perfect coding migration. -3. Each `Method` instance represents an API, you can aggregate the request parameters and request behavior parameters of the same API into the same `Method` instance without spreading them to different files, which is more suitable for managing a large number of APIs. -4. alova realizes automatic management of response data cache by serializing request parameters on the `Method` instance. You do not need to specify the cache key, and both react-query and swr need to customize the `queryKey` to manage the cache. - -### High flexibility - -Alova achieves high flexibility through various adapters and middleware. It can not only run in any js environment, but also support users to customize request modules in different scenarios. - -### Lightweight - -alova is very lightweight, and its size is only 30%+ of react-query and axios. Similar in size to swr, but provides richer functionality. +--- +title: Compare with other libraries +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## Compare with axios + +axios provides a very simple and easy-to-use HTTP request function based on promise. It only requires a simple line of code to send and receive requests, and can run in the browser and nodejs environment. It is a very excellent request js library. + +But axios focuses on sending requests and receiving responses, which means that if you need to write more code by yourself to actively optimize the request function, and alova is like a weapon of axios, combining axios with alova can get more powerful request capabilities. The following are the request management capabilities added by alova to axios. + +### alova provides automated request status management for axios + +When you only use axios, you usually need to maintain request-related status by yourself. You can get automated request status management capabilities by using alova's use hook. + + + + +```javascript +// vue3 example +const loading = ref(false); +const data = ref({}); +const error = ref(null); +const request = async () => { + try { + loading.value = true; + data.value = await axios.get('/xxx'); + } catch (e) { + error.value = e; + } + loading.value = false; +}; +mounted(request); +``` + + + + +```javascript +// Use axios as alova's request adapter +const { loading, data, error } = useRequest(alova.Get('/xxx')); +``` + + + + +### alova provides high-performance request strategies out of the box + +alova provides you with [multiple high-performance request strategy modules](/tutorial/strategy). You can use different modules according to different request scenarios, which axios does not have. + +### alova provides response data cache for axios + +alova provides 3 caching modes to meet different caching scenarios, namely memory mode, cache occupying mode, and recovery mode. They are component-independent and can hit the cache as long as the request address and parameters are the same, unless you turn it off. Response data caching can greatly improve request fluency and reduce server pressure. + +### alova provides request sharing function for axios + +Request sharing will reuse the same request when sending multiple identical requests at the same time. It can also improve application fluency and reduce server pressure. + +### alova provides data pre-fetching for axios + +Requesting the data to be used in advance can also greatly improve application fluency. + +### alova can manage request states + +You can use alova across any component hierarchy to access stateful data in other components, which allows you to reduce some of the trouble of cross-component communication. + +## Compared with react-query and swr + +react-query is a powerful asynchronous state management, and swr is a React Hooks library for data requests. Their common feature is the use of use hooks to send and manage requests, and data caching functions. For them, alova has the following differences at. + +### alova has different goals + +In fact, alova's use hook also refers to the design of react-query and swr, but alova chooses the direction of the request strategy library. You can use different request strategy modules in different request scenarios, allowing you to write less code. At the same time, more efficient Client-Server data interaction can also be achieved. + +### Method proxy design + +Both react-query and swr use `axios` or `fetch api` directly in use hook to send requests, while alova uses the `Method` proxy design mode. This design has the following 3 benefits: + +1. Unified usage without different usage depending on the platform or UI framework. +2. Request libraries such as `axios` and `fetch api` are decoupled from each API in the form of request adapters, which allows alova to provide a unified development experience and perfect coding migration. +3. Each `Method` instance represents an API, you can aggregate the request parameters and request behavior parameters of the same API into the same `Method` instance without spreading them to different files, which is more suitable for managing a large number of APIs. +4. alova realizes automatic management of response data cache by serializing request parameters on the `Method` instance. You do not need to specify the cache key, and both react-query and swr need to customize the `queryKey` to manage the cache. + +### High flexibility + +Alova achieves high flexibility through various adapters and middleware. It can not only run in any js environment, but also support users to customize request modules in different scenarios. + +### Lightweight + +alova is very lightweight, and its size is only 30%+ of react-query and axios. Similar in size to swr, but provides richer functionality. diff --git a/versioned_docs/version-2.x/tutorial/11-others/03-Q&A.md b/versioned_docs/version-2.x/tutorial/11-others/03-Q&A.md index 2b0f56323..379f15d32 100644 --- a/versioned_docs/version-2.x/tutorial/11-others/03-Q&A.md +++ b/versioned_docs/version-2.x/tutorial/11-others/03-Q&A.md @@ -1,24 +1,23 @@ ---- -title: Question & Answer -sidebar_position: 30 ---- - -## Why to create alova? - -Data requests have always been an indispensable and important part of applications. Since the birth of XMLHttpRequest, request schemes have emerged in endlessly. Client-side data interaction exploration has always focused on the simplicity of requests, such as `$.ajax`, `axios`, `fetch api` and Request tools such as `react-query`, the coding form continues to evolve from callback functions, Promise, to usehook. These js libraries have done a good job in making requests simple, but they only provide common functions, which means For different request scenarios such as sharing requests, paging requests, form submissions, uploading and downloading files, etc., developers still need to write complex codes themselves, which reduces development efficiency and performance cannot be guaranteed. As user experience becomes more and more important, In this era, application fluency has become more and more important. - -Additionally, the collaboration between client and server is also separated. Front-end engineers need to consult API documents and manually write API functions, and any changes of APIs need to actively notify front-end engineers, which will make your product more uncontrollable. - -**We think there is a simpler solution is that based on your request scenarios such as pagination, form submission, breakpoint resumption, etc., select the corresponding useHook, which will help you manage data and control when requests should be sent**. This allows developers to achieve more efficient Client-Server data interaction while writing little code. - -Additionally, alova has very flexible expansion capabilities to implement request strategies in different scenarios. You can also customize your own request scenarios. This part is in [Custom Chapter](/category/custom). - -In order to cover more request scenarios, we also abstracted the request scenarios into [Request scene model(RSM)](/tutorial/others/RSM), which explains alova's request strategy scheme well. In the future, alova will continue to carry forward our exploration of request strategies. - -## Alternative to the request libraries? - -alova is a request strategy library, which was originally created to provide specific request strategy solutions for different request scenarios, so as to achieve a smooth request experience more concisely and elegantly, such as `$.ajax`, `axios` and `fetch- api`, etc. provide good support for request sending and response receiving, they are an essential part of the [RSM](/tutorial/others/RSM) process (request events), alova still needs to dependent on them to make requests, so we can Think of alova as an weaponry of the request library, making the request library more powerful. - -## Why binding UI framework? - -Decoupling a js library means using it in more scenarios. For example, axios can be used in nodejs, but it also means that developers need to write more template code, such as using useHooks to encapsulate axios. However, alova abandons more usage scenarios brought about by decoupling, and positions the scope of use in conjunction with the UI framework to use alova in the most streamlined way. This is for the benefit of developers and is prevalent in a UI framework. Sometimes, deep binding can provide developers with direct-use functions and improve the developer's experience without requiring too much template code. +--- +title: Question & Answer +--- + +## Why to create alova? + +Data requests have always been an indispensable and important part of applications. Since the birth of XMLHttpRequest, request schemes have emerged in endlessly. Client-side data interaction exploration has always focused on the simplicity of requests, such as `$.ajax`, `axios`, `fetch api` and Request tools such as `react-query`, the coding form continues to evolve from callback functions, Promise, to usehook. These js libraries have done a good job in making requests simple, but they only provide common functions, which means For different request scenarios such as sharing requests, paging requests, form submissions, uploading and downloading files, etc., developers still need to write complex codes themselves, which reduces development efficiency and performance cannot be guaranteed. As user experience becomes more and more important, In this era, application fluency has become more and more important. + +Additionally, the collaboration between client and server is also separated. Front-end engineers need to consult API documents and manually write API functions, and any changes of APIs need to actively notify front-end engineers, which will make your product more uncontrollable. + +**We think there is a simpler solution is that based on your request scenarios such as pagination, form submission, breakpoint resumption, etc., select the corresponding useHook, which will help you manage data and control when requests should be sent**. This allows developers to achieve more efficient Client-Server data interaction while writing little code. + +Additionally, alova has very flexible expansion capabilities to implement request strategies in different scenarios. You can also customize your own request scenarios. This part is in [Custom Chapter](/category/custom). + +In order to cover more request scenarios, we also abstracted the request scenarios into [Request scene model(RSM)](/tutorial/others/RSM), which explains alova's request strategy scheme well. In the future, alova will continue to carry forward our exploration of request strategies. + +## Alternative to the request libraries? + +alova is a request strategy library, which was originally created to provide specific request strategy solutions for different request scenarios, so as to achieve a smooth request experience more concisely and elegantly, such as `$.ajax`, `axios` and `fetch- api`, etc. provide good support for request sending and response receiving, they are an essential part of the [RSM](/tutorial/others/RSM) process (request events), alova still needs to dependent on them to make requests, so we can Think of alova as an weaponry of the request library, making the request library more powerful. + +## Why binding UI framework? + +Decoupling a js library means using it in more scenarios. For example, axios can be used in nodejs, but it also means that developers need to write more template code, such as using useHooks to encapsulate axios. However, alova abandons more usage scenarios brought about by decoupling, and positions the Scope of usage in conjunction with the UI framework to use alova in the most streamlined way. This is for the benefit of developers and is prevalent in a UI framework. Sometimes, deep binding can provide developers with direct-use functions and improve the developer's experience without requiring too much template code. diff --git a/versioned_docs/version-2.x/tutorial/11-others/04-future.md b/versioned_docs/version-2.x/tutorial/11-others/04-future.md index d889d2646..83cc31696 100644 --- a/versioned_docs/version-2.x/tutorial/11-others/04-future.md +++ b/versioned_docs/version-2.x/tutorial/11-others/04-future.md @@ -1,36 +1,35 @@ ---- -title: Future of alova -sidebar_position: 40 ---- - -alovajs is positioned as a lightweight request strategy library. It currently provides good support in terms of request functions and request strategies, but the future of alovajs does not stop there. - -## More request strategies - -This is the direction that remains unchanged, and we will continue to explore efficient and easy-to-use request strategies based on common businesses. - -## More UI framework support - -Although alovajs is a request tool based on UI framework, its flexible design allows us to use it in various UI frameworks. It will eventually be compatible with the following UI frameworks and js environments: - -- Functional style framework, such as `react/react-native/vue-composntion/svelte/solid/preact/qwik`. -- SSR frameworks such as `next/nuxt/sveltekit`. -- class style framework, such as `angular/lit/stencil`. -- options style framework, such as `vue-options/native applet (China🇨🇳)`. -- Multi-end adaptation framework, such as `Uniapp/Taro` (China 🇨🇳). - -Please check [Go to UI Framework](/category/framework) for details. - -## Automatic management and maintenance of APIs - -In the future, alovajs is also committed to solving front-end API problems and further simplifying the workflow of front-end development. This is the next development direction of alovajs: **automatic management and maintenance of APIs**, which specifically includes the following three points. - -1. Automatically generate a request function with complete ts type and complete description. Whether it is a js project or a ts project, the call does not need to be introduced, making it as convenient for developers as directly calling `location.reload`, and the request function can be directly seen A complete description and request parameter type hints, which can be automatically generated by openAPI. - -2. Since the automatically generated request function has a complete description and type hint, develop a vscode plug-in to quickly retrieve the API you need to use through keywords, and you no longer need to consult the API documentation. - -3. Solve the problem of front-end and back-end collaboration fault. Any changes in the interface are known to the front-end and can be notified when starting the project. If changes are found when building the project, an error will be thrown to stop the build. If it is a ts project, it will also be compiled. When an error is thrown, you can also view the change record through the vscode plugin. - -## Development checklist - -[Click here to view all the development checklist](/contributing/become-core-member/#list-of-participating-repositories) +--- +title: Future of alova +--- + +alovajs is positioned as a lightweight request strategy library. It currently provides good support in terms of request functions and request strategies, but the future of alovajs does not stop there. + +## More request strategies + +This is the direction that remains unchanged, and we will continue to explore efficient and easy-to-use request strategies based on common businesses. + +## More UI framework support + +Although alovajs is a request tool based on UI framework, its flexible design allows us to use it in various UI frameworks. It will eventually be compatible with the following UI frameworks and js environments: + +- Functional style framework, such as `react/react-native/vue-composntion/svelte/solid/preact/qwik`. +- SSR frameworks such as `next/nuxt/sveltekit`. +- class style framework, such as `angular/lit/stencil`. +- options style framework, such as `vue-options/native applet (China🇨🇳)`. +- Multi-end adaptation framework, such as `Uniapp/Taro` (China 🇨🇳). + +Please check [Go to UI Framework](/category/framework) for details. + +## Automatic management and maintenance of APIs + +In the future, alovajs is also committed to solving front-end API problems and further simplifying the workflow of front-end development. This is the next development direction of alovajs: **automatic management and maintenance of APIs**, which specifically includes the following three points. + +1. Automatically generate a request function with complete ts type and complete description. Whether it is a js project or a ts project, the call does not need to be introduced, making it as convenient for developers as directly calling `location.reload`, and the request function can be directly seen A complete description and request parameter type hints, which can be automatically generated by openAPI. + +2. Since the automatically generated request function has a complete description and type hint, develop a vscode plug-in to quickly retrieve the API you need to use through keywords, and you no longer need to consult the API documentation. + +3. Solve the problem of front-end and back-end collaboration fault. Any changes in the interface are known to the front-end and can be notified when starting the project. If changes are found when building the project, an error will be thrown to stop the build. If it is a ts project, it will also be compiled. When an error is thrown, you can also view the change record through the vscode plugin. + +## Development checklist + +[Click here to view all the development checklist](/contributing/become-core-member/#list-of-participating-repositories) diff --git a/versioned_docs/version-2.x/tutorial/11-others/05-react-native.md b/versioned_docs/version-2.x/tutorial/11-others/05-react-native.md index dbd0d47df..f010e5586 100644 --- a/versioned_docs/version-2.x/tutorial/11-others/05-react-native.md +++ b/versioned_docs/version-2.x/tutorial/11-others/05-react-native.md @@ -1,15 +1,14 @@ ---- -title: React-Native app development -sidebar_position: 50 ---- - -You can develop a React-Native app with alova, and you can also use request adapter `GlobalFetch` directly to handle request event. - -But...there are some cautions: - -## metro version - -In the alova, `exports` is used to define multiple items in `package.json`, so it's necessary to ensure 2 points below: - -1. version of metro must >= 0.76.0 -2. enable `resolver.unstable_enablePackageExports` in `metro.config.js`. [Click here for detail](https://facebook.github.io/metro/docs/configuration/#unstable_enablepackageexports-experimental) +--- +title: React-Native app development +--- + +You can develop a React-Native app with alova, and you can also use request adapter `GlobalFetch` directly to handle request event. + +But...there are some cautions: + +## metro version + +In the alova, `exports` is used to define multiple items in `package.json`, so it's necessary to ensure 2 points below: + +1. version of metro must >= 0.76.0 +2. enable `resolver.unstable_enablePackageExports` in `metro.config.js`. [Click here for detail](https://facebook.github.io/metro/docs/configuration/#unstable_enablepackageexports-experimental) diff --git a/versioned_docs/version-2.x/tutorial/11-others/06-use-in-static.md b/versioned_docs/version-2.x/tutorial/11-others/06-use-in-static.md index d47608206..f31117293 100644 --- a/versioned_docs/version-2.x/tutorial/11-others/06-use-in-static.md +++ b/versioned_docs/version-2.x/tutorial/11-others/06-use-in-static.md @@ -1,6 +1,5 @@ --- title: Use in static html -sidebar_position: 60 --- import Tabs from '@theme/Tabs'; @@ -36,7 +35,9 @@ You can also use alova via importing from CDN. Vue.createApp({ setup() { - return alova.useRequest(alovaInstance.Get('https://jsonplaceholder.typicode.com/todos/1')); + return alova.useRequest( + alovaInstance.Get('https://jsonplaceholder.typicode.com/todos/1') + ); } }).mount('#app'); @@ -124,7 +125,9 @@ svelte 依赖于编译工具,不能通过 CDN 直接使用,详情见 [svelte el: '#app', mixins: AlovaVueOptions.mapAlovaHook(function () { return { - todo: alova.useRequest(alovaInstance.Get('https://jsonplaceholder.typicode.com/todos/1')) + todo: alova.useRequest( + alovaInstance.Get('https://jsonplaceholder.typicode.com/todos/1') + ) }; }), data() { diff --git a/versioned_docs/version-2.x/tutorial/11-others/07-hide-recommend-tips.md b/versioned_docs/version-2.x/tutorial/11-others/07-hide-recommend-tips.md index 75aad3a62..cee9e2594 100644 --- a/versioned_docs/version-2.x/tutorial/11-others/07-hide-recommend-tips.md +++ b/versioned_docs/version-2.x/tutorial/11-others/07-hide-recommend-tips.md @@ -1,46 +1,45 @@ ---- -title: Hide recommend Tips -sidebar_position: 70 ---- - -:::info version required - -v2.7.0- - -::: - -Alova can cooperate with the extension library to obtain a better development experience. In order to allow more developers to obtain a better development experience, the extension of alova will be recommended in the console when using it. - -![tips](/img/alova-tips.png) - -These prompt codes will be automatically removed when building the production environment package. If you want to hide them in the development environment, you can do the following: - -## Vite - -Set environment variable **VITE_ALOVA_TIPS=0** in `.env.development` file - -```bash title=.env.development -VITE_ALOVA_TIPS=0 -``` - -:::warning Warning -If info still exists. you can try to remove the deps cache of vite, which at the dir `node_modules/.vite/deps`. -::: - -## Webpack - -### Vue - -Set environment variable **VUE_APP_ALOVA_TIPS=0** in `.env.development` file - -```bash title=.env.development -VUE_APP_ALOVA_TIPS=0 -``` - -### React - -Set environment variable **REACT_APP_ALOVA_TIPS=0** in `.env.development` file - -```bash title=.env.development -REACT_APP_ALOVA_TIPS=0 -``` +--- +title: Hide recommend Tips +--- + +:::info version required + +v2.7.0- + +::: + +Alova can cooperate with the extension library to obtain a better development experience. In order to allow more developers to obtain a better development experience, the extension of alova will be recommended in the console when using it. + +![tips](/img/alova-tips.png) + +These prompt codes will be automatically removed when building the production environment package. If you want to hide them in the development environment, you can do the following: + +## Vite + +Set environment variable **VITE_ALOVA_TIPS=0** in `.env.development` file + +```bash title=.env.development +VITE_ALOVA_TIPS=0 +``` + +:::warning Warning +If info still exists. you can try to remove the deps cache of vite, which at the dir `node_modules/.vite/deps`. +::: + +## Webpack + +### Vue + +Set environment variable **VUE_APP_ALOVA_TIPS=0** in `.env.development` file + +```bash title=.env.development +VUE_APP_ALOVA_TIPS=0 +``` + +### React + +Set environment variable **REACT_APP_ALOVA_TIPS=0** in `.env.development` file + +```bash title=.env.development +REACT_APP_ALOVA_TIPS=0 +``` diff --git a/versioned_sidebars/version-2.x-sidebars.json b/versioned_sidebars/version-2.x-sidebars.json index cf7852543..9631e0457 100644 --- a/versioned_sidebars/version-2.x-sidebars.json +++ b/versioned_sidebars/version-2.x-sidebars.json @@ -1,17 +1,17 @@ { - "tutorialSidebar": [ + "tutorial": [ { "type": "autogenerated", "dirName": "tutorial" } ], - "apiSidebar": [ + "api": [ { "type": "autogenerated", "dirName": "api" } ], - "contributingSidebar": [ + "contributing": [ { "type": "autogenerated", "dirName": "contributing" From 64313e9d5cf49361cd56ea28a1eef54c518acd7c Mon Sep 17 00:00:00 2001 From: JOU Amjs Date: Wed, 19 Jun 2024 23:41:49 +0800 Subject: [PATCH 06/11] docs: corrent link, translate announcement --- docs/about/03-qa.md | 2 +- docusaurus.config.ts | 2 +- i18n/zh-CN/code.json | 16 ++ .../version-2.x/about/{03-Q&A.md => 03-qa.md} | 0 .../docusaurus-theme-classic/navbar.json | 12 ++ src/pages/_indexComponent/index.module.css | 196 +++++++++--------- src/theme/AnnouncementBar/Content/index.js | 8 +- .../Content/styles.module.scss | 1 + .../version-2.x/about/{03-Q&A.md => 03-qa.md} | 0 9 files changed, 134 insertions(+), 103 deletions(-) rename i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/about/{03-Q&A.md => 03-qa.md} (100%) rename versioned_docs/version-2.x/about/{03-Q&A.md => 03-qa.md} (100%) diff --git a/docs/about/03-qa.md b/docs/about/03-qa.md index 700caa69f..1cd6b4cbc 100644 --- a/docs/about/03-qa.md +++ b/docs/about/03-qa.md @@ -1,5 +1,5 @@ --- -title: Question & Answer +title: Question and Answer --- import Tabs from '@theme/Tabs'; diff --git a/docusaurus.config.ts b/docusaurus.config.ts index 6973b0fa3..e104a0450 100644 --- a/docusaurus.config.ts +++ b/docusaurus.config.ts @@ -123,7 +123,7 @@ const config: Config = { to: 'about/comparison' }, { - label: 'QA', + label: 'Q&A', to: 'about/qa' } ] diff --git a/i18n/zh-CN/code.json b/i18n/zh-CN/code.json index 25e47b667..9ffec08ae 100644 --- a/i18n/zh-CN/code.json +++ b/i18n/zh-CN/code.json @@ -664,8 +664,24 @@ }, "announcement.content": { "message": "如果你也喜欢alova,请为它点个star吧!", + "description": "description of announcement content" + }, + "announcement.title": { + "message": "alova v3.0.0-beta已发布", "description": "description of announcement title" }, + "announcement.desc": { + "message": "更简单,更强大,支持客户端和服务端", + "description": "description of announcement description" + }, + "announcement.btn1": { + "message": "新特性", + "description": "description of announcement btn1" + }, + "announcement.btn2": { + "message": "v3.0指南", + "description": "description of announcement btn2" + }, "example.open in new tab": { "message": "遇到问题?点此在新页面中打开示例", "description": "description of example open in new tab" diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/about/03-Q&A.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/about/03-qa.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/about/03-Q&A.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/about/03-qa.md diff --git a/i18n/zh-CN/docusaurus-theme-classic/navbar.json b/i18n/zh-CN/docusaurus-theme-classic/navbar.json index c4b20a56f..805ba1a89 100644 --- a/i18n/zh-CN/docusaurus-theme-classic/navbar.json +++ b/i18n/zh-CN/docusaurus-theme-classic/navbar.json @@ -62,5 +62,17 @@ "item.label.GitHub": { "message": "GitHub", "description": "Navbar item with label GitHub" + }, + "item.label.Request Scene Model": { + "message": "请求场景模型", + "description": "Navbar item with label Request Scene Model" + }, + "item.label.Comparison": { + "message": "请求库对比", + "description": "Navbar item with label Comparison" + }, + "item.label.Q&A": { + "message": "提问&回答", + "description": "Navbar item with label Q&A" } } diff --git a/src/pages/_indexComponent/index.module.css b/src/pages/_indexComponent/index.module.css index 6ff77738a..13b7ebeac 100644 --- a/src/pages/_indexComponent/index.module.css +++ b/src/pages/_indexComponent/index.module.css @@ -1,97 +1,99 @@ -/** - * CSS files with the .module.css suffix will be treated as CSS modules - * and scoped locally. - */ - -.heroBanner { - padding: 4rem 0; - position: relative; - overflow: hidden; -} - -.heroContent { - display: flex; - flex-direction: column; - align-items: center; - width: 50%; - margin: 0 auto; - text-align: center; - --ifm-button-padding-horizontal: 1rem; -} -.heroContent > h1 { - font-size: 4rem; -} -.heroContent > p { - font-size: 1.2rem; -} - -.heroContent code { - padding-left: 3rem; - padding-right: 3rem; -} - -.buttons { - display: flex; - align-items: center; -} - -.btn { - margin-right: 10px; -} -.btn:last-child { - margin-right: 0; -} - -.logo { - width: 200px; -} - -.relationContent { - background: var(--ifm-color-emphasis-100); - border-radius: 0.5rem; - padding: 3rem 3rem; - color: var(--ifm-color-emphasis-700); - width: 56%; - margin: 0 auto; -} -[data-theme='dark'] .relationContent { - background: var(--ifm-color-emphasis-200); -} - -.quota { - font-size: 2rem; - line-height: 2rem; -} - -@media screen and (max-width: 996px) { - .heroBanner { - padding: 1rem; - } - .heroContent { - width: 90%; - } - .heroContent > h1 { - font-size: 3.4rem; - } - .logo { - display: none; - } - .relation { - width: 90%; - } - .codeExample { - display: none; - } - .relationContent { - padding: 1rem 2rem; - width: auto; - } -} - -.announcement { - margin-top: 2rem; - background: var(--ifm-color-emphasis-100); - color: var(--ifm-color-primary); - padding: 0.4rem 2rem; - border-radius: 10rem; -} +/** + * CSS files with the .module.css suffix will be treated as CSS modules + * and scoped locally. + */ + +.heroBanner { + padding: 4rem 0; + position: relative; + overflow: hidden; +} + +.heroContent { + display: flex; + flex-direction: column; + align-items: center; + width: 50%; + margin: 0 auto; + text-align: center; + --ifm-button-padding-horizontal: 1rem; +} +.heroContent > h1 { + font-size: 4rem; +} +.heroContent > p { + font-size: 1.2rem; +} + +.heroContent code { + padding-left: 3rem; + padding-right: 3rem; +} + +.buttons { + display: flex; + align-items: center; +} + +.btn { + margin-right: 10px; +} +.btn:last-child { + margin-right: 0; +} + +.logo { + width: 200px; +} + +.relationContent { + background: var(--ifm-color-emphasis-100); + border-radius: 0.5rem; + padding: 3rem 3rem; + color: var(--ifm-color-emphasis-700); + width: 56%; + margin: 0 auto; +} +[data-theme='dark'] .relationContent { + background: var(--ifm-color-emphasis-200); +} + +.quota { + font-size: 2rem; + line-height: 2rem; +} + +@media screen and (max-width: 996px) { + .heroBanner { + padding: 1rem; + } + .heroContent { + width: 90%; + } + .heroContent > h1 { + font-size: 3.4rem; + } + .logo { + display: none; + } + .relation { + width: 90%; + } + .codeExample { + display: none; + } + .relationContent { + padding: 1rem 2rem; + width: auto; + } +} + +.announcement { + margin-top: 2rem; + background: var(--ifm-color-emphasis-100); + color: var(--ifm-color-primary); + padding: 0.4rem 2rem; + border-radius: 10rem; + font-weight: 800; + font-size: 0.9rem; +} diff --git a/src/theme/AnnouncementBar/Content/index.js b/src/theme/AnnouncementBar/Content/index.js index b9af84a81..11d096924 100644 --- a/src/theme/AnnouncementBar/Content/index.js +++ b/src/theme/AnnouncementBar/Content/index.js @@ -14,10 +14,10 @@ export default function AnnouncementBarContent(props) { {...props} className={clsx(styles.content, props.className, 'flex-col align-center')}> - alova v3.0.0-beta is HERE! + alova v3.0.0-beta is HERE! - + More simple, more powerful, support both client & server @@ -25,12 +25,12 @@ export default function AnnouncementBarContent(props) { - What's New + What's New - v3.0 Tutorial + v3.0 Tutorial diff --git a/src/theme/AnnouncementBar/Content/styles.module.scss b/src/theme/AnnouncementBar/Content/styles.module.scss index a3430dcc6..87fb80c67 100644 --- a/src/theme/AnnouncementBar/Content/styles.module.scss +++ b/src/theme/AnnouncementBar/Content/styles.module.scss @@ -17,6 +17,7 @@ margin: 0.5rem; cursor: pointer; transition: all 0.3s; + background: rgba($color: #000000, $alpha: 0.3); &:hover { color: var(--ifm-color-primary); diff --git a/versioned_docs/version-2.x/about/03-Q&A.md b/versioned_docs/version-2.x/about/03-qa.md similarity index 100% rename from versioned_docs/version-2.x/about/03-Q&A.md rename to versioned_docs/version-2.x/about/03-qa.md From 0f429fe6ad0f6b462cd3d171c804602af9ef6690 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=83=A1=E9=95=87?= Date: Thu, 20 Jun 2024 18:35:20 +0800 Subject: [PATCH 07/11] docs: update links and content for v3.0 release notes and tutorials --- docs/release-notes-v3.md | 177 +++++++++++++++++ .../09-extension-integration.md | 40 +++- .../current/release-notes-v3.md | 183 ++++++++++++++++++ .../09-extension-integration.md | 40 +++- src/theme/AnnouncementBar/Content/index.js | 2 +- 5 files changed, 439 insertions(+), 3 deletions(-) create mode 100644 docs/release-notes-v3.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/release-notes-v3.md diff --git a/docs/release-notes-v3.md b/docs/release-notes-v3.md new file mode 100644 index 000000000..4cd275bff --- /dev/null +++ b/docs/release-notes-v3.md @@ -0,0 +1,177 @@ +--- +title: 'alova v3.0 Release Notes' +--- + +## 整体升级目标 + +Alova@3.0旨在进一步实现“Run in any JS environment”的目标,通过重构和优化,使其在服务端和更多的 JS 环境中使用更加友好。 + +## 重新设计 + +### 重新设计结构 + +- **fetch 适配器导出路径改为 alova/fetch** + + ```javascript + const adapterFetch = require('alova/fetch'); + const alova = createAlova({ + requestAdapter: adapterFetch() + }); + ``` + +- **JS 包结构调整** + + - 新增 alova/client 和 alova/server 区分客户端 hook 和服务端 hook。 + - 核心 hooks 放在客户端 hook 中,统一在 alova 包中导出。 + + ```javascript + import vueHook from 'alova/vue'; + import reactHook from 'alova/react'; + import { useRequest, useWatcher, usePagination } from 'alova/client'; + const alova = createAlova({ + statesHook: vueHook, + requestAdapter: fetchAdapter() + }); + const { data, loading, error } = useRequest(alova.Get('/api/user')); + ``` + + ```javascript + const { useRateLimit, sendCaptcha } = require('alova/server'); + const fetch = require('alova/fetch'); + const alova = createAlova({ requestAdapter: fetch() }); + const requestCaptcha = mobile => alova.Get('/api/captcha', { params: { mobile } }); + try { + await sendCaptcha(requestCaptcha, { key: mobile, countdown: 60 }); + } catch (error) { + throw new Error('发送失败'); + } + ``` + +### 其他重构点 + +- **useForm 不再支持直接传入 id 返回缓存的 hookReturns** +- **updateStateEffect 不再支持 method 匹配器方式使用** +- **SilentMethod 函数改为返回 promise 的异步函数** +- **accessAction 新增 silent 参数** + +## 废弃项 + +### useWatcher 的 sendable + +- **废弃 useWatcher 的 sendable,通过 middleware 判断** + + ```typescript + // alova@2.x + let sendable = false; + useWatcher(() => method, [xxx], { sendable: () => sendable }); + + // alova@3.x + let sendable = false; + useWatcher(() => method, [xxx], { + async middleware(_, next) { + if (sendable) { + return next(); + } + } + }); + ``` + +### mapWatcher + +- **废弃 mapWatcher** + + ```typescript + // alova@2.x + export default { + mixins: mapAlovaHook(function () { + return { testRequest: useRequest(this.method) }; + }), + watch: mapWatcher({ + 'testRequest.data'() { + /* ... */ + } + }) + }; + + // alova@3.x + export default { + watch: { + 'testRequest.data'() { + /* ... */ + } + } + }; + ``` + +### localCache 更改为 cacheFor + +- **localCache 更改为 cacheFor** + +### 其他废弃项 + +- **updateState 的 onMatch 钩子** +- **matchSnapshotMethod** +- **getMethodKey** +- **method.**key**简化为 method.key** +- **method.transformData 简化为 method.transform** + +## 其他修改项 + +### 使用更简单 + +- **去掉更多设置参数** + +### 支持自定义 alova 实例 id + +- **多服务器场景下,不同 alova 实例缓存隔离** + ```typescript + const alova = createAlova({ + id: 'custom-id', + requestAdapter: fetchAdapter() + }); + ``` + +### 支持依赖收集 + +- **性能优化,减少多余的视图渲染** + +### middleware 优化 + +- **使用 proxyState 访问和修改状态** + ```typescript + middleware({ proxyStates, args }, { update }) => { + const loadingValue = proxyStates.loading.v; + proxyStates.loading.v = true; + } + ``` + +### 事件装饰 + +- **废弃 decorateSuccess/decorateError 等事件装饰** + ```typescript + import { decorateEvent } from '@alova/shared/createEventManager'; + const exposure = useRequest(/* ... */); + exposure.onSuccess = decorateEvent(exposure.onSuccess, (handler, event) => { + event.extraAttribute = { + /* ... */ + }; + const res = handler(event, 1, 2, 3); + return res; + }); + exposure.onSuccess((event, extra1, extra2, extra3) => { + return 100; + }); + ``` + +### 限制 method 快照数量 + +```javascript +import { globalConfig } from 'alova'; +globalConfig({ + methodSnapshots: 1000 // 设置最大数量,默认为1000 +}); +``` + +### 移除废弃的 responsed + +- **统一使用 responded** diff --git a/docs/tutorial/02-getting-started/09-extension-integration.md b/docs/tutorial/02-getting-started/09-extension-integration.md index b091b02f2..f0f02351b 100644 --- a/docs/tutorial/02-getting-started/09-extension-integration.md +++ b/docs/tutorial/02-getting-started/09-extension-integration.md @@ -8,7 +8,13 @@ Integrating Alova's editor extension can make it more powerful. 2. Embed api documents in the code to experience the effect of checking and using APIs. 3. Update api regularly and actively notify front-end development, no longer relying on server-side developers to notify. -Install VS Code extension + + +:::info + +The extension will be released soon... + +::: > Automatically generate support for swagger-v2 and openapi-v3 specifications. @@ -90,3 +96,35 @@ module.exports = { */ }; ``` + +## Usage + +The generated API code is accessed through the global `Apis` by default. You can enjoy the smart prompts provided by the editor to quickly preview the API information, allowing you to use the API while checking. + +API parameters will be specified through parameters such as `params/pathParams/data/headers`, and you can also specify the config parameters of the method instance. + +```js +Apis.user.changeProfile({ + // (optional)query parameters + params: { + id: 12 + }, + // (optional)path parameters + pathParams: { + id2: 20 + }, + // (optional)body parameters + data: { + name: 'alova', + age: 18 + }, + // (optional)header parameters + headers: { + 'Content-Type': 'application/json' + }, + + // config configuration items supported by other methods + cacheFor: 100 * 1000, + transform: response => response.detail +}); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/release-notes-v3.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/release-notes-v3.md new file mode 100644 index 000000000..f9c5f7024 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/release-notes-v3.md @@ -0,0 +1,183 @@ +--- +title: 'alova v3.0 Release Notes' +--- + +alova@3.0 的发布日志,目前处于 beta 阶段,后续可能有所改动,请谨慎使用。 + +## 整体升级目标 + +alova@3.0旨在进一步实现“Run in any JS environment”的目标,让使用更简单,通过重构和优化,使其在服务端和更多的 JS 环境中使用更加友好。 + +## 重新设计 + +### 重新设计结构 + +- **fetch 适配器导出路径改为 alova/fetch** + + ```javascript + const adapterFetch = require('alova/fetch'); + const alova = createAlova({ + requestAdapter: adapterFetch() + }); + ``` + +- **JS 包结构调整** + + - 新增 alova/client 和 alova/server 区分客户端 hook 和服务端 hook。 + - 核心 hooks 放在客户端 hook 中,统一在 alova 包中导出。 + + ```javascript + import vueHook from 'alova/vue'; + import reactHook from 'alova/react'; + import { useRequest, useWatcher, usePagination } from 'alova/client'; + const alova = createAlova({ + statesHook: vueHook, + requestAdapter: fetchAdapter() + }); + const { data, loading, error } = useRequest(alova.Get('/api/user')); + ``` + + ```javascript + const { useRateLimit, sendCaptcha } = require('alova/server'); + const fetch = require('alova/fetch'); + const alova = createAlova({ requestAdapter: fetch() }); + const requestCaptcha = mobile => alova.Get('/api/captcha', { params: { mobile } }); + try { + await sendCaptcha(requestCaptcha, { key: mobile, countdown: 60 }); + } catch (error) { + throw new Error('发送失败'); + } + ``` + +### 其他重构点 + +- **useForm 不再支持直接传入 id 返回缓存的 hookReturns** +- **updateStateEffect 不再支持 method 匹配器方式使用** +- **SilentMethod 函数改为返回 promise 的异步函数** +- **accessAction 新增 silent 参数** + +## 废弃项 + +### useWatcher 的 sendable + +- **废弃 useWatcher 的 sendable,通过 middleware 判断** + + ```typescript + // alova@2.x + let sendable = false; + useWatcher(() => method, [xxx], { sendable: () => sendable }); + + // alova@3.x + let sendable = false; + useWatcher(() => method, [xxx], { + async middleware(_, next) { + if (sendable) { + return next(); + } + } + }); + ``` + +### mapWatcher + +- **废弃 mapWatcher** + + ```typescript + // alova@2.x + export default { + mixins: mapAlovaHook(function () { + return { testRequest: useRequest(this.method) }; + }), + watch: mapWatcher({ + 'testRequest.data'() { + /* ... */ + } + }) + }; + + // alova@3.x + export default { + watch: { + 'testRequest.data'() { + /* ... */ + } + } + }; + ``` + +### localCache 更改为 cacheFor + +- **localCache 更改为 cacheFor** + +### 其他废弃项 + +- **updateState 的 onMatch 钩子** +- **matchSnapshotMethod** +- **getMethodKey** +- **method.**key**简化为 method.key** +- **method.transformData 简化为 method.transform** + +## 其他修改项 + +### 使用更简单 + +- **去掉更多设置参数** + +### 支持自定义 alova 实例 id + +- **多服务器场景下,不同 alova 实例缓存隔离** + ```typescript + const alova = createAlova({ + id: 'custom-id', + requestAdapter: fetchAdapter() + }); + ``` + +### 支持依赖收集 + +- **性能优化,减少多余的视图渲染** + +### middleware 优化 + +- **使用 proxyState 访问和修改状态** + ```typescript + middleware({ proxyStates, args }, { update }) => { + const loadingValue = proxyStates.loading.v; + proxyStates.loading.v = true; + } + ``` + +### 事件装饰 + +- **废弃 decorateSuccess/decorateError 等事件装饰** + ```typescript + import { decorateEvent } from '@alova/shared/createEventManager'; + const exposure = useRequest(/* ... */); + exposure.onSuccess = decorateEvent(exposure.onSuccess, (handler, event) => { + event.extraAttribute = { + /* ... */ + }; + const res = handler(event, 1, 2, 3); + return res; + }); + exposure.onSuccess((event, extra1, extra2, extra3) => { + return 100; + }); + ``` + +### 限制 method 快照数量 + +```javascript +import { globalConfig } from 'alova'; +globalConfig({ + methodSnapshots: 1000 // 设置最大数量,默认为1000 +}); +``` + +### 移除废弃的 responsed + +- **统一使用 responded** + +```md + +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/09-extension-integration.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/09-extension-integration.md index a1194a24b..7537e20b4 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/09-extension-integration.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/09-extension-integration.md @@ -8,7 +8,13 @@ title: 编辑器扩展集成 2. 将 api 文档嵌入代码中,带你体验边查边用 API 的效果。 3. 定时更新 api 并主动通知前端开发,不再依赖服务端开发人员通知。 -安装 VS Code 扩展 + + +:::info + +插件将在近期发布,敬请期待... + +::: > 自动生成支持 swagger-v2 和 openapi-v3 规范。 @@ -89,3 +95,35 @@ module.exports = { } ``` + +## 用法 + +生成的 API 代码默认通过全局的`Apis`访问,你可以享受编辑器为你带来的智能提示来快速预览 API 信息,让你可以边查边使用 API。 + +API 参数将通过`params/pathParams/data/headers`等参数来指定,同时你还可以指定 method 实例的 config 参数。 + +```js +Apis.user.changeProfile({ + // (可选)query参数 + params: { + id: 12 + }, + // (可选)path参数 + pathParams: { + id2: 20 + }, + // (可选)body参数 + data: { + name: 'alova', + age: 18 + }, + // (可选)header参数 + headers: { + 'Content-Type': 'application/json' + }, + + // 其他method支持的config配置项 + cacheFor: 100 * 1000, + transform: response => response.detail +}); +``` diff --git a/src/theme/AnnouncementBar/Content/index.js b/src/theme/AnnouncementBar/Content/index.js index 11d096924..4d1c0dd38 100644 --- a/src/theme/AnnouncementBar/Content/index.js +++ b/src/theme/AnnouncementBar/Content/index.js @@ -24,7 +24,7 @@ export default function AnnouncementBarContent(props) {
+ to="/next/release-notes-v3"> What's New Date: Thu, 20 Jun 2024 22:30:17 +0800 Subject: [PATCH 08/11] docs: update release-notes for alova@3.0 --- docs/release-notes-v3.md | 709 ++++++++++++++---- .../current/release-notes-v3.md | 688 +++++++++++++---- 2 files changed, 1124 insertions(+), 273 deletions(-) diff --git a/docs/release-notes-v3.md b/docs/release-notes-v3.md index 4cd275bff..a9b62b4fc 100644 --- a/docs/release-notes-v3.md +++ b/docs/release-notes-v3.md @@ -2,176 +2,607 @@ title: 'alova v3.0 Release Notes' --- -## 整体升级目标 - -Alova@3.0旨在进一步实现“Run in any JS environment”的目标,通过重构和优化,使其在服务端和更多的 JS 环境中使用更加友好。 - -## 重新设计 - -### 重新设计结构 - -- **fetch 适配器导出路径改为 alova/fetch** - - ```javascript - const adapterFetch = require('alova/fetch'); - const alova = createAlova({ - requestAdapter: adapterFetch() - }); - ``` - -- **JS 包结构调整** - - - 新增 alova/client 和 alova/server 区分客户端 hook 和服务端 hook。 - - 核心 hooks 放在客户端 hook 中,统一在 alova 包中导出。 - - ```javascript - import vueHook from 'alova/vue'; - import reactHook from 'alova/react'; - import { useRequest, useWatcher, usePagination } from 'alova/client'; - const alova = createAlova({ - statesHook: vueHook, - requestAdapter: fetchAdapter() - }); - const { data, loading, error } = useRequest(alova.Get('/api/user')); - ``` - - ```javascript - const { useRateLimit, sendCaptcha } = require('alova/server'); - const fetch = require('alova/fetch'); - const alova = createAlova({ requestAdapter: fetch() }); - const requestCaptcha = mobile => alova.Get('/api/captcha', { params: { mobile } }); - try { - await sendCaptcha(requestCaptcha, { key: mobile, countdown: 60 }); - } catch (error) { - throw new Error('发送失败'); +:::warning beta reminder + +The release log of alova@3.0 is currently in the beta stage and may be changed later. Please use it with caution. + +::: + +## Overall upgrade goal + +alova@3.0 aims to further achieve the goal of "Run in any JS environment" and make it easier to use. We have fully redesigned and refactored the code in 3.0 to make it more user-friendly on the server and in more JS environments. + +## Redesign alova + +### Redesign the structure + +1. Change the fetch adapter export path to `alova/fetch/` + +```javascript +const adapterFetch = require('alova/fetch'); +const alova = createAlova({ + requestAdapter: adapterFetch() +}); +``` + +2. JS package structure adjustment + +- Merge the original `@alova/scene-vue`, `@alova/scene-react`, `@alova/scene-svelte` into a package `alova/client` using cross-UI framework technology. + +- Move `useRequest/useWatcher/useFetcher` to `alova/client`. + +- Add `alova/server` to export the server-side scene strategy module. + +Now you can directly use `alova/client` and `alova/server` as long as you install `alova`. + +```javascript +import vueHook from 'alova/vue'; +import reactHook from 'alova/react'; +import svelteHook from 'alova/svelte'; +import vueDemiHook from 'alova/vue-demi'; +import { useRequest, useWatcher, usePagination } from 'alova/client'; +import adapterFetch from 'alova/fetch'; + +const alova = createAlova({ + statesHook: vueHook, + requestAdapter: adapterFetch() +}); +const { data, loading, error } = useRequest(alova.Get('/api/user')); +``` + +server hooks usage example + +```javascript +const { useRateLimit, sendCaptcha } = require('alova/server'); +import adapterFetch from 'alova/fetch'; +const alova = createAlova({ + requestAdapter: adapterFetch() +}); +const requestCaptcha = mobile => alova.Get('/api/captcha', { params: { mobile } }); +try { + await sendCaptcha(requestCaptcha, { key: mobile, countdown: 60 }); +} catch (error) { + throw new Error('Send failed'); +} +``` + +### Redesign cache mode + +In order to make alova shine on the server side and simplify alova's API, we considered the following cache application scenarios +Application scenarios + +1. High access frequency and low latency requirements, such as hot news and product details, can further reduce network overhead and maintain faster response when the network is unstable. +2. Reduce the pressure on downstream servers. For example, for services with peak access periods, the upper-level cache can effectively reduce the pressure on the backend database and microservices. +3. Integrate data merging and processing of multiple downstream servers. Multiple serial requests may lead to longer response time and may consume performance due to complex data conversion. The converted data can be cached. +4. API rate limit and billing. Weather forecast service API updates weather information every hour, geographic location data API, etc. + +As well as most asynchronous cache processing mechanisms, the following redesigns have been made: + +#### 1. Remove the placeholder mode + +Placeholder is only valid on the client and with useHook. In 3.0, initialData is added to support functions to achieve the same effect, as follows: + +```javascript +const { onSuccess } = useRequest(Getter, { + initialData() { + // Set the last response data + const storedData = localStorage.getItem('placeholder-data'); + return JSON.parse(storedData || '{}'); + + // Also use alova's storage adapter + // return alovaInst.l2cache.get('placeholder-data'); } - ``` +}); +onSuccess(({ data, method }) => { + // Save response data + localStorage.setItem('placeholder-data', JSON.stringify(data)); -### 其他重构点 + // Also use alova's storage adapter + alovaInst.l2cache.set('placeholder-data', data); +}); +``` -- **useForm 不再支持直接传入 id 返回缓存的 hookReturns** -- **updateStateEffect 不再支持 method 匹配器方式使用** -- **SilentMethod 函数改为返回 promise 的异步函数** -- **accessAction 新增 silent 参数** +At this point, there are only two cache modes, the definition and usage remain unchanged, but the memory mode supports custom adapters and asynchronous operations, as follows: + +- **memory mode**: supports custom memory adapters and asynchronous operations, for example, it can be customized as LRUCache in the server scenario, and can also manage processes across processes, or manage caches anywhere else you want. + +- **restore mode**: response data will be cached in both memory and storage. When requesting, the memory cache will be searched first. If not found, it will be searched in storage. After a hit, the cache will be restored to the memory cache. If both miss, a request will be initiated. + +#### 2. Memory cache and store cache support asynchronous operations + +We can use memory as the first-level cache and storage cache as the second-level cache. + +> Note: In terms of the use of a single cache, there is no difference between memory mode and storage mode. Both support custom adapters and asynchronous operations. You can also customize the cache to be placed anywhere, but the implementation mechanism is that the first-level cache takes precedence over the second-level cache. In restore mode, you can implement the mechanism of the second-level cache restoring the first-level cache. + +Scenario recommendation: Memory mode uses local memory (including cross-process sharing in the cluster), and storage mode is placed in a persistent cache such as a file or Redis. + +```js +const { LRUCache } = require('lru-cache'); +const { createClient } = require('redis'); + +const client = createClient(/*...*/); +const alovaInst = createAlova({ + // ... + // l1 is the highest priority cache, that is, the original memory cache + l1Cache: new LRUCache(), + // l2 is a lower memory cache, that is, the original persistent cache + l2Cache: { + set: async (key, [data, expireTs]) => { + await client.set(key, data, { + EX: Number((expireTs - Date.now()) / 1000) + }); + }, + get: async key => client.get(key), + remove: async key => client.set(key, '', 0) + } +}); +``` -## 废弃项 +An example of handling cache avalanche, solved by adding random expiration time -### useWatcher 的 sendable +```javascript +alova.Get('/xxx', { + cacheFor: { + expire: 100000 * Math.floor(Math.random() * 1000), + mode: 'restore' + } +}); +``` -- **废弃 useWatcher 的 sendable,通过 middleware 判断** +#### 3. Cache operation function optimization + +1. In order to adapt to asynchronous storage adapters, cache operation functions `setCache`, `queryCache`, `invalidateCache` are changed to asynchronous functions. +2. Add separate control for level 1 and level 2 cache. +3. `setCache/queryCache/invalidateCache` no longer supports method matcher parameters. For details, see the method snapshot matcher modification section below. + +```js +await setCache(methodInstance, data, { + /** + * Cache policy. + * - l1: only set l1 cache. + * - l2: only set l2 cache. + * - all: set l1 cache and set l2 cache (method cache mode needs to be "restore"). + * @default all + */ + policy: 'all' +}); - ```typescript - // alova@2.x - let sendable = false; - useWatcher(() => method, [xxx], { sendable: () => sendable }); +/** + * Cache policy. + * - l1: only query l1 cache. + * - l2: only query l2 cache. + * - all: query l1 cache first, and query l2 cache if l1 cache is not found (method cache mode needs to be "restore"). + * @default all + */ +await queryCache(methodInstance, { + policy: 'all' +}); +await invalidateCache([methodInstance1, methodInstance2 /*...*/]); +``` - // alova@3.x - let sendable = false; - useWatcher(() => method, [xxx], { - async middleware(_, next) { - if (sendable) { - return next(); +#### 4. Set different cache expiration times + +In high access frequency and low latency requirements, we often need to cache high-frequency access data in the local server for a short time, and set a cache with a longer expiration time in the remote cache server. At this time, we can do this: + +```js +// Global settings +const alovaInst = createAlova({ + // ... + cacheFor: { + GET: { + mode: 'restore', + expire: ({ method, mode }) => { + if (method.meta.setDiffExpire) { + // Set a 5-minute cache in l1 cache and a 1-day cache in l2 cache + return mode === 'memory' ? 5 * 60 : 24 * 60 * 60; + } } } - }); - ``` - -### mapWatcher - -- **废弃 mapWatcher** - - ```typescript - // alova@2.x - export default { - mixins: mapAlovaHook(function () { - return { testRequest: useRequest(this.method) }; - }), - watch: mapWatcher({ - 'testRequest.data'() { - /* ... */ - } - }) - }; - - // alova@3.x - export default { - watch: { - 'testRequest.data'() { - /* ... */ - } + } +}); + +// Set for a single request +alovaInst.Get('/user/profile', { + // ... + cacheFor: { + mode: 'restore', + expire: ({ method, mode }) => { + // Set 5 minutes cache in l1 cache, 1 day cache in l2 cache + return mode === 'memory' ? 5 * 60 : 24 * 60 * 60; } - }; - ``` + } +}); +``` -### localCache 更改为 cacheFor +### method snapshot matcher modification -- **localCache 更改为 cacheFor** +The original method matcher can be used in `setCache`, `queryCache`, `invalidateCache`ache`, `useFetcher.fetch`, `updateState`, but the number of method instances they require is different. In addition, there is an ambiguity problem. Only method instances that have been requested are saved in the matcher container (memory). However, when the page is refreshed, when the persistent cache is obtained or invalidated, for example, `queryCache('method-name')`or`invalidateCache('method-name')` may be invalid because the method instance cannot be found in the snapshot, causing ambiguity. -### 其他废弃项 +Therefore, in alova@3, the method matcher is externalized, and the above five functions are changed to only support the passing of method instances. Users can clearly know whether the method instance snapshot has been found, which is more unified in usage and does not cause ambiguity. The code design is as follows: -- **updateState 的 onMatch 钩子** -- **matchSnapshotMethod** -- **getMethodKey** -- **method.**key**简化为 method.key** -- **method.transformData 简化为 method.transform** +```js +// alova@2 writing +invalidateCache('method-name'); +const data = queryCache('method-name2'); -## 其他修改项 +// alova@3 writing +const methodSnapshots = alovaInst.snapshots.match('method-name'); // Match multiple +invalidateCache(methodSnapshots); +const oneSnapshot = alovaInst.snapshots.match('method-name2', true); // Match only one +const data = await queryCache(oneSnapshot); +``` -### 使用更简单 +### Rewrite snapshot matching and automatic invalidation cache algorithm -- **去掉更多设置参数** +In 2.x, snapshot matching and automatic invalidation cache algorithms both search for the target method by traversing method snapshots. On the server side or long-running clients, the matching efficiency may be reduced due to too many snapshots, which will cause performance loss. In 3.x, the search steps will be reduced to improve the search efficiency. +Export new function: -### 支持自定义 alova 实例 id +```js +declare function hitCacheBySource(sourceMethod: Method): Promise; +await hitCacheBySource(alova.Get('/api/profile')); +``` -- **多服务器场景下,不同 alova 实例缓存隔离** - ```typescript - const alova = createAlova({ - id: 'custom-id', - requestAdapter: fetchAdapter() - }); - ``` +## Deprecated items -### 支持依赖收集 +### Deprecated useWatcher's sendable -- **性能优化,减少多余的视图渲染** +Deprecated useWatcher's sendable, judged by middleware. -### middleware 优化 +```typescript +// alova@2.x +let sendable = false; +useWatcher(() => method, [xxx], { sendable: () => sendable }); -- **使用 proxyState 访问和修改状态** - ```typescript - middleware({ proxyStates, args }, { update }) => { - const loadingValue = proxyStates.loading.v; - proxyStates.loading.v = true; +// alova@3.x +let sendable = false; +useWatcher(() => method, [xxx], { + async middleware(_, next) { + if (sendable) { + return next(); + } } - ``` +}); +``` + +### Deprecate `enableDownload/enableUpload` of method + +These two parameters are no longer needed in method. They will be changed to automatically determine whether to enable or not. Getter will be used to determine whether uploading and downloading are used externally. + +### Remove deprecated responsed + +It is recommended to use the `responded` field uniformly in createAlova. -### 事件装饰 +```js +createAlova({ + responded() { + // ... + } +}); +``` + +### Deprecated error log control parameter errorLogger + +In 2.x, an error message will be printed in the console whenever there is a request error. When the error is not caught at the request, the console will display two identical error messages, which is not very friendly. + +In 3.x, if onError is bound or error is used, no error will be thrown. Otherwise, an error will be thrown to avoid displaying two error messages. + +### Deprecated `onMatch` hook of `updateState` + +Because updateState no longer supports method matchers, the `onMatch` hook is no longer useful. + +### Deprecated `matchSnapshotMethod` + +In alova@3.0, `alova.snapshots.match` is used to obtain the instance snapshot under the corresponding alova. + +```js +alova.snapshots.match('method-name'); +``` + +### Deprecated `getMethodKey` + +alova@3.x no longer exports `getMethodKey`, you can import the function that calculates the method key from the following methods. + +```js +import { key } from '@alova/shared/function'; + +const methodKey = key(methodInstance); + +// or +const currentKey = methodInstance.generateKey(); +``` + +### Deprecated middleware items + +1. Deprecated `context.update` function. +2. Deprecate event decorations such as `decorateSuccess/decorateError/decorateComplete`. Now you can decorate events like this: + +```js +import { decorateEvent } from '@alova/shared/createEventManager'; + +const exposure = useRequest(/* ... */); +exposure.onSuccess = decorateEvent(exposure.onSuccess, (handler, event) => { + event.extraAttribute = { + /* ... */ + }; // Mount extra attributes for event + const res = handler(event, 1, 2, 3); // Pass extra parameters to event callback and get return value + // ... +}); -- **废弃 decorateSuccess/decorateError 等事件装饰** - ```typescript - import { decorateEvent } from '@alova/shared/createEventManager'; - const exposure = useRequest(/* ... */); - exposure.onSuccess = decorateEvent(exposure.onSuccess, (handler, event) => { - event.extraAttribute = { - /* ... */ +exposure.onSuccess((event, extra1, extra2, extra3) => { + event.extraAttribute; // Extra attributes + extra1; // 1 + extra2; // 2 + extra3; // 3 + return 100; +}); +``` + +### @alova/vue-options deprecates `mapWatcher` + +Listening to the return status of useHook no longer requires the use of the auxiliary function `mapWatcher`, so it is deprecated. + +```js +// @alova/vue-options@1.x +export default { + mixins: mapAlovaHook(function () { + return { + testRequest: useRequest(this.method) }; - const res = handler(event, 1, 2, 3); - return res; - }); - exposure.onSuccess((event, extra1, extra2, extra3) => { - return 100; - }); - ``` + }), + + watch: mapWatcher({ + 'testRequest.data'() { + // ... + } + }) +} + +// @alova/vue-options@2.0 +export default { + // ... + watch: { + 'testRequest.data'() { + // ... + } + } +} +``` -### 限制 method 快照数量 +## @alova/scene-\* optimization -```javascript -import { globalConfig } from 'alova'; -globalConfig({ - methodSnapshots: 1000 // 设置最大数量,默认为1000 +We have optimized some strategies of `@alova/scene-*` and moved them to `alova/client`. + +### usePagination + +#### 1. Set abortLast to true by default + +When continuously turning pages or querying data, only the content of the last operation will be displayed, which is more in line with the experience. + +```js +// 2.x requires explicit settings +usePagination({ + // ... + abortLast: true }); + +// 3.x does not require +usePagination({ + // ... +}); +``` + +#### Optimize the use under react + +Under react, the page and pageSize export items are no longer a reactState, and now update is used uniformly to change the state. + +```js +// 2.x +const { + page: [page, setPage], + pageSize: [pageSize, setPageSize] + /*...*/ +} = usePagination(/*...*/); +const handleAddPage = () => { + setPage(page + 1); +}; +const handleSetPageSize = newPageSize => { + setPageSize(newPageSize); +}; + +// 3.x +const { page, pageSize, update /*...*/ } = usePagination(/*...*/); +const handleAddPage = () => { + update({ page: page + 1 }); +}; +const handleSetPageSize = newPageSize => { + update({ pageSize: newPageSize }); +}; ``` -### 移除废弃的 responsed +### useForm + +#### 1. No longer supports directly passing in id to return cached hookReturns + +Due to the original scene The package is directly divided into three packages: `@alova/scene-react`, `@alova/scene-vue`, and `@alova/scene-svelte`. The export status of each package is fixed to the status type of the corresponding UI framework. After the revision, it is unified into a client library. When submitting a multi-step form, it is also necessary to provide a method instance or methodHandler to automatically infer which UI framework's status type should be exported, but the function remains unchanged. -- **统一使用 responded** +```js +// In 2.x, states are the state types of the corresponding UI framework +const states = useForm('form-id'); + +// In 3.0, a method instance or methodHandler must also be provided +const { states, update } = useForm(formData => methodHandler(formData), { + // ... + id: 'form-id' +}); +``` + +### useSQRequest + +#### 1. `updateStateEffect` no longer supports the use of method matchers + +Since updateState no longer supports the use of method matchers, `updateStateEffect` is modified synchronously, and the `onMatch` callback function is no longer supported. + +#### 2. Modification of `SilentMethod` member method usage + +Since l2Cache can be set as an asynchronous function, `SilentMethod.replace/remove/save` and `filterSilentMethods/getSilentMethod` are changed from synchronous functions to asynchronous functions that return promises. + +```js +import { filterSilentMethods, getSilentMethod } from 'alova/client'; + +const silentMethods = await filterSilentMethods('method-name', 'queue-name'); +const silentMethod = await getSilentMethod('method-name', 'queue-name'); + +await silentMethod.replace(newSilentMethod); +await silentMethod.remove(); +await silentMethod.save(); +``` + +### accessAction + +Added silent parameter, controlled by silent parameter, no error is thrown when delegation is not matched + +```js +// Error is thrown when method-name is not matched +accessAction('method-name', ({ send }) => { + send(); +}); + +// No error is thrown at this time +accessAction( + 'method-name', + ({ send }) => { + send(); + }, + true +); +``` + +### All useHook's force function is changed to AlovaEvent object + +```js +// @2.x +useRequest(Getter, { + force(arg1, arg2) { + // ... + } +}); + +// 3.x +useRequest(Getter, { + force(event) { + // Get args + const arg1 = event.sendArgs[0]; + const arg2 = event.sendArgs[1]; + + // Get method + const method = event.method; + } +}); +``` + +## Field name modification + +### `method.key` is simplified to `method.key` + +```js +// 2.x +method.__key__ = 'custom-key'; + +// 3.x +method.key = 'custom-key'; +``` + +### `method.transformData` is simplified to `method.transform` + +```js +// 2.x +alova.Get('/api/profile', { + transformData(data) { + return data.detail; + } +}); + +// 3.x +alova.Get('/api/profile', { + transform(data) { + return data.detail; + } +}); +``` + +### `method.localCache` changed to `method.cacheFor` + +```js +// 2.x +alova.Get('/api/profile', { + localCache: 1000 * 60 * 60 +}); + +// 3.x +alova.Get('/api/profile', { + cacheFor: 1000 * 60 * 60 +}); +``` + +### @alova/adapter-uniapp export name change + +Since alova's `storageAdapter` was renamed to `l2Cache`, the export item `uniappStorageAdapter` of `@alova/adapter-uniapp` was changed to `uniappL2CacheAdapter`. + +## Other optimizations + +### Support dependency collection + +This is a performance optimization. All useHooks in alova@3.x will not update unaccessed states to reduce redundant view rendering caused by internal state updates. + +A simple example is as follows. When `loading` is not used, `useRequest` will not update the `loading` state to reduce additional view rendering. + +```js +const App = () => { + const { data } = useRequest(method1); + return data ?
{data.name}
: null; +}; + +export default App; +``` + +### middleware optimization + +In alova@2.x, accessing states data in middleware is not flexible enough because you get a framework-related state collection, which is not conducive to writing general middleware. Therefore, the following changes are made in 3.x: + +1. Use the framework-independent proxyState to access and modify the state so that the middleware can be used in any UI framework. + +```js +middleware({ proxyStates, args }, ({ update }) => { + // Access state + const loadingValue = proxyStates.loading.v; + + // Modify state + proxyStates.loading.v = true; +}); +``` + +2. sendArgs and fetchArgs are changed to args. + +### All event binding functions return their own objects + +In order to optimize the user experience and reduce renaming, all event binding functions return the export object of useHook to obtain the effect of chain call binding events. + +```js +// 2.x +const { onSuccess, loading, data } = useRequest(method); +onSuccess(event => { + // ... +}); +const { onSuccess: onSuccess2, error } = useRequest(method2); +onSuccess2(event => { + // ... +}); + +// 3.x +const { loading, data } = useRequest(method).onSuccess(event => { + // ... +}); +const { error } = useRequest(method2).onSuccess(event => { + // ... +}); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/release-notes-v3.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/release-notes-v3.md index f9c5f7024..a3a3eb8c5 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/release-notes-v3.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/release-notes-v3.md @@ -2,182 +2,602 @@ title: 'alova v3.0 Release Notes' --- +:::warning beta 提醒 + alova@3.0 的发布日志,目前处于 beta 阶段,后续可能有所改动,请谨慎使用。 +::: + ## 整体升级目标 -alova@3.0旨在进一步实现“Run in any JS environment”的目标,让使用更简单,通过重构和优化,使其在服务端和更多的 JS 环境中使用更加友好。 +alova@3.0旨在进一步实现“Run in any JS environment”的目标,让使用更简单,我们在 3.0 中进行了全量的重新设计和代码重构,使其在服务端和更多的 JS 环境中使用更加友好。 -## 重新设计 +## 重新设计 alova ### 重新设计结构 -- **fetch 适配器导出路径改为 alova/fetch** - - ```javascript - const adapterFetch = require('alova/fetch'); - const alova = createAlova({ - requestAdapter: adapterFetch() - }); - ``` - -- **JS 包结构调整** - - - 新增 alova/client 和 alova/server 区分客户端 hook 和服务端 hook。 - - 核心 hooks 放在客户端 hook 中,统一在 alova 包中导出。 - - ```javascript - import vueHook from 'alova/vue'; - import reactHook from 'alova/react'; - import { useRequest, useWatcher, usePagination } from 'alova/client'; - const alova = createAlova({ - statesHook: vueHook, - requestAdapter: fetchAdapter() - }); - const { data, loading, error } = useRequest(alova.Get('/api/user')); - ``` - - ```javascript - const { useRateLimit, sendCaptcha } = require('alova/server'); - const fetch = require('alova/fetch'); - const alova = createAlova({ requestAdapter: fetch() }); - const requestCaptcha = mobile => alova.Get('/api/captcha', { params: { mobile } }); - try { - await sendCaptcha(requestCaptcha, { key: mobile, countdown: 60 }); - } catch (error) { - throw new Error('发送失败'); +1. fetch 适配器导出路径改为 `alova/fetch/` + +```javascript +const adapterFetch = require('alova/fetch'); +const alova = createAlova({ + requestAdapter: adapterFetch() +}); +``` + +2. JS 包结构调整 + +- 将原`@alova/scene-vue`、`@alova/scene-react`、`@alova/scene-svelte`使用跨 UI 框架技术合并为一个包`alova/client`。 +- 将`useRequest/useWatcher/useFetcher`移至`alova/client`。 +- 新增 `alova/server` 导出服务端场景策略模块。 + +现在只要安装`alova`就可以直接使用`alova/client`和`alova/server`。 + +```javascript +import vueHook from 'alova/vue'; +import reactHook from 'alova/react'; +import svelteHook from 'alova/svelte'; +import vueDemiHook from 'alova/vue-demi'; +import { useRequest, useWatcher, usePagination } from 'alova/client'; +import adapterFetch from 'alova/fetch'; + +const alova = createAlova({ + statesHook: vueHook, + requestAdapter: adapterFetch() +}); +const { data, loading, error } = useRequest(alova.Get('/api/user')); +``` + +server hooks 使用示例 + +```javascript +const { useRateLimit, sendCaptcha } = require('alova/server'); +import adapterFetch from 'alova/fetch'; +const alova = createAlova({ + requestAdapter: adapterFetch() +}); +const requestCaptcha = mobile => alova.Get('/api/captcha', { params: { mobile } }); +try { + await sendCaptcha(requestCaptcha, { key: mobile, countdown: 60 }); +} catch (error) { + throw new Error('发送失败'); +} +``` + +### 重新设计缓存模式 + +为了让 alova 在服务端也能大放异彩,同时简化 alova 的 api,我们考虑了以下的缓存应用场景 +应用场景 + +1. 高访问频率和低延迟需求,例如热门新闻、商品详情,可以进一步减少网络开销,在网络不稳定时也保持更快的响应。 +2. 减轻下游服务器压力,例如有访问高峰期的服务,上层缓存可以有效减少对后端数据库和微服务的压力。 +3. 整合多个下游服务器的数据合并和处理,多个串行请求可能导致更长的响应时间,也可能因复杂的数据转换消耗性能,可将转换后的数据进行缓存。 +4. API 速率限制和计费,天气预报服务 API 每小时更新一次天气信息,地理位置数据 API 等。 + +以及大多数的异步缓存处理机制,现做出了如下的重新设计: + +#### 1. 去掉 placeholder 模式 + +placeholder 只在客户端且搭配 useHook 才有效,3.0 中添加 initialData 对函数的支持,实现相同效果,如下: + +```javascript +const { onSuccess } = useRequest(Getter, { + initialData() { + // 设置上一次的响应数据 + const storedData = localStorage.getItem('placeholder-data'); + return JSON.parse(storedData || '{}'); + + // 也使用alova的存储适配器 + // return alovaInst.l2cache.get('placeholder-data'); } - ``` +}); +onSuccess(({ data, method }) => { + // 保存响应数据 + localStorage.setItem('placeholder-data', JSON.stringify(data)); + + // 也使用alova的存储适配器 + alovaInst.l2cache.set('placeholder-data', data); +}); +``` -### 其他重构点 +此时,只存在两种缓存模式,定义和使用方式都不变,但对 memory 模式支持自定义适配器,并且支持异步操作,具体如下: -- **useForm 不再支持直接传入 id 返回缓存的 hookReturns** -- **updateStateEffect 不再支持 method 匹配器方式使用** -- **SilentMethod 函数改为返回 promise 的异步函数** -- **accessAction 新增 silent 参数** +- **memory 模式**:支持自定义 memory 适配器并支持异步操作,例如在服务端场景可以自定义为 LRUCache,同时还可以跨进程管理进程,或者你想要的其他任何地方管理缓存。 +- **restore 模式**:响应数据将同时缓存到 memory 和 storage 中,请求时将优先查找 memory 缓存,未找到时再在 storage 中的寻找,命中后将缓存恢复到 memory 缓存,如果都未命中则会发起请求。 -## 废弃项 +#### 2. memory 缓存和 store 缓存支持异步操作 + +我们可以将内存作为一级缓存,storage 缓存作为二级缓存。 -### useWatcher 的 sendable +> 说明:从单个缓存的使用上来说,memory 模式和 storage 模式没有区别,都支持自定义适配器和异步操作,你也可以自定义将缓存放在任何地方,但实现机制上一级缓存优先于二级缓存,在 restore 模式下,你可以实现二级缓存恢复一级缓存的机制。 -- **废弃 useWatcher 的 sendable,通过 middleware 判断** +场景推荐:memory 模式使用本地内存(集群中包含跨进程共享),storage 模式放到持久化缓存中例如文件中或 Redis 中。 - ```typescript - // alova@2.x - let sendable = false; - useWatcher(() => method, [xxx], { sendable: () => sendable }); +```js +const { LRUCache } = require('lru-cache'); +const { createClient } = require('redis'); - // alova@3.x - let sendable = false; - useWatcher(() => method, [xxx], { - async middleware(_, next) { - if (sendable) { - return next(); +const client = createClient(/*...*/); +const alovaInst = createAlova({ + // ... + // l1为优先级最高的缓存,即原来的内存缓存 + l1Cache: new LRUCache(), + // l2为较低的内存缓存,即原来的持久化缓存 + l2Cache: { + set: async (key, [data, expireTs]) => { + await client.set(key, data, { + EX: Number((expireTs - Date.now()) / 1000) + }); + }, + get: async key => client.get(key), + remove: async key => client.set(key, '', 0) + } +}); +``` + +一个处理缓存雪崩的示例,通过添加随机过期时间解决 + +```javascript +alova.Get('/xxx', { + cacheFor: { + expire: 100000 * Math.floor(Math.random() * 1000), + mode: 'restore' + } +}); +``` + +#### 3. 缓存操作函数优化 + +1. 为了适应异步的存储适配器,缓存操作函数 `setCache`、`queryCache`、`invalidateCache` 改为异步函数。 +2. 增加单独对 level 1 和 level 2 缓存的控制。 +3. `setCache/queryCache/invalidateCache`不再支持 method 匹配器参数,具体下面的见 method 快照匹配器修改部分 + +```js +await setCache(methodInstance, data, { + /** + * 缓存策略。 + * - l1:只设置l1缓存。 + * - l2:只设置l2缓存。 + * - all:设置l1缓存和设置l2缓存(方法缓存模式需要为“restore”)。 + * @default all + */ + policy: 'all' +}); + +/** + * 缓存策略。 + * - l1:仅查询l1缓存。 + * - l2:仅查询l2缓存。 + * - all: 首先查询l1缓存,如果找不到l1缓存则查询l2缓存(方法缓存模式需要为“restore”)。 + * @default all + */ +await queryCache(methodInstance, { + policy: 'all' +}); +await invalidateCache([methodInstance1, methodInstance2 /*...*/]); +``` + +#### 4. 设置不同的缓存过期时间 + +在高访问频率和低延迟需求中,我们时常需要在服务器本地中短时缓存高频访问数据,同时在远程缓存服务器设置更长过期时间的缓存,此时可以这样做: + +```js +// 全局设置 +const alovaInst = createAlova({ + // ... + cacheFor: { + GET: { + mode: 'restore', + expire: ({ method, mode }) => { + if (method.meta.setDiffExpire) { + // 在l1缓存设置5分钟缓存,在l2缓存设置1天缓存 + return mode === 'memory' ? 5 * 60 : 24 * 60 * 60; + } } } - }); - ``` - -### mapWatcher - -- **废弃 mapWatcher** - - ```typescript - // alova@2.x - export default { - mixins: mapAlovaHook(function () { - return { testRequest: useRequest(this.method) }; - }), - watch: mapWatcher({ - 'testRequest.data'() { - /* ... */ - } - }) - }; - - // alova@3.x - export default { - watch: { - 'testRequest.data'() { - /* ... */ - } + } +}); + +// 对单个请求设置 +alovaInst.Get('/user/profile', { + // ... + cacheFor: { + mode: 'restore', + expire: ({ method, mode }) => { + // 在l1缓存设置5分钟缓存,在l2缓存设置1天缓存 + return mode === 'memory' ? 5 * 60 : 24 * 60 * 60; } - }; - ``` + } +}); +``` -### localCache 更改为 cacheFor +### method 快照匹配器修改 -- **localCache 更改为 cacheFor** +原 method 匹配器可以在 `setCache`、`queryCache`、`invalidateCache`、`useFetcher.fetch`、`updateState` 五个函数中使用,但它们需要的 method 实例数量各不相同,此外,还存在歧义问题,已发送过请求的 method 实例才会保存在匹配器容器中(内存),但当刷新页面后,再获取或失效持久化的缓存时,例如 `queryCache('method-name')`或 `invalidateCache('method-name')`可能因为在快照中找不到 method 实例而无效,造成歧义。 -### 其他废弃项 +因此在 alova@3 中将 method 匹配器外部化,并且以上五个函数改为只支持传入 method 实例,用户可以清楚地知道是否有查找到 method 实例快照,更统一了使用方式,也不造成歧义。代码设计如下: -- **updateState 的 onMatch 钩子** -- **matchSnapshotMethod** -- **getMethodKey** -- **method.**key**简化为 method.key** -- **method.transformData 简化为 method.transform** +```js +// alova@2写法 +invalidateCache('method-name'); +const data = queryCache('method-name2'); -## 其他修改项 +// alova@3写法 +const methodSnapshots = alovaInst.snapshots.match('method-name'); // 匹配多个 +invalidateCache(methodSnapshots); +const oneSnapshot = alovaInst.snapshots.match('method-name2', true); // 只匹配一个 +const data = await queryCache(oneSnapshot); +``` -### 使用更简单 +### 重写快照匹配和自动失效缓存算法 -- **去掉更多设置参数** +在 2.x 中,快照匹配和自动失效缓存算法都是通过遍历 method 快照的方式来查找目标 method 的,在服务端或长期运行的客户端中可能因快照过多而匹配效率降低,损耗性能,在 3.x 中将减少查找步骤,实现查找效率的提高。 +导出新函数: -### 支持自定义 alova 实例 id +```js +declare function hitCacheBySource(sourceMethod: Method): Promise; +await hitCacheBySource(alova.Get('/api/profile')); +``` -- **多服务器场景下,不同 alova 实例缓存隔离** - ```typescript - const alova = createAlova({ - id: 'custom-id', - requestAdapter: fetchAdapter() - }); - ``` +## 废弃项 -### 支持依赖收集 +### 废弃 useWatcher 的 sendable -- **性能优化,减少多余的视图渲染** +废弃 useWatcher 的 sendable,通过 middleware 判断。 -### middleware 优化 +```typescript +// alova@2.x +let sendable = false; +useWatcher(() => method, [xxx], { sendable: () => sendable }); -- **使用 proxyState 访问和修改状态** - ```typescript - middleware({ proxyStates, args }, { update }) => { - const loadingValue = proxyStates.loading.v; - proxyStates.loading.v = true; +// alova@3.x +let sendable = false; +useWatcher(() => method, [xxx], { + async middleware(_, next) { + if (sendable) { + return next(); + } } - ``` +}); +``` + +### 废弃 method 的 `enableDownload/enableUpload` + +在 method 中不再需要这两个参数,将改为自动判断是否开启,通过 getter 判断外部是否使用了 uploading 和 downloading。 + +### 移除废弃的 responsed + +在 createAlova 中推荐统一使用`responded`字段。 + +```js +createAlova({ + responded() { + // ... + } +}); +``` + +### 废弃错误日志控制参数 errorLogger + +在 2.x 中,只要请求错误都将在控制台打印一条错误信息,当请求处未捕获错误时,控制台将会显示两条相同的错误信息,这不太友好。 +在 3.x 中修改为,如果绑定了 onError,或者使用了 error 则不再抛出错误,否则抛出错误,避免显示两条错误信息。 + +### 废弃 `updateState` 的 `onMatch` 钩子 + +因为 updateState 不再支持 method 匹配器,因此 `onMatch` 钩子不再有用。 + +### 废弃`matchSnapshotMethod` + +在alova@3.0 中通过 `alova.snapshots.match` 获取对应 alova 下的实例快照。 + +```js +alova.snapshots.match('method-name'); +``` + +### 废弃`getMethodKey` + +alova@3.x不再导出`getMethodKey`,可以从以下方式中导入计算 method key 的函数。 + +```js +import { key } from '@alova/shared/function'; +const methodKey = key(methodInstance); + +// 或者 +const currentKey = methodInstance.generateKey(); +``` + +### middleware 废弃项 + +1. 废弃了 `context.update` 函数。 +2. 废弃`decorateSuccess/decorateError/decorateComplete`等事件装饰,现在可以这样装饰事件: + +```js +import { decorateEvent } from '@alova/shared/createEventManager'; + +const exposure = useRequest(/* ... */); +exposure.onSuccess = decorateEvent(exposure.onSuccess, (handler, event) => { + event.extraAttribute = { + /* ... */ + }; // 为event挂载额外的属性 + const res = handler(event, 1, 2, 3); // 为事件回调传递额外参数,并获取返回值 + // ... +}); + +exposure.onSuccess((event, extra1, extra2, extra3) => { + event.extraAttribute; // 额外的属性 + extra1; // 1 + extra2; // 2 + extra3; // 3 + return 100; +}); +``` + +### @alova/vue-options 废弃 `mapWatcher` -### 事件装饰 +监听 useHook 的返回状态不再需要使用辅助函数`mapWatcher`,因此废弃。 -- **废弃 decorateSuccess/decorateError 等事件装饰** - ```typescript - import { decorateEvent } from '@alova/shared/createEventManager'; - const exposure = useRequest(/* ... */); - exposure.onSuccess = decorateEvent(exposure.onSuccess, (handler, event) => { - event.extraAttribute = { - /* ... */ +```js +// @alova/vue-options@1.x +export default { + mixins: mapAlovaHook(function () { + return { + testRequest: useRequest(this.method) }; - const res = handler(event, 1, 2, 3); - return res; - }); - exposure.onSuccess((event, extra1, extra2, extra3) => { - return 100; - }); - ``` + }), + + watch: mapWatcher({ + 'testRequest.data'() { + // ... + } + }) +} + +// @alova/vue-options@2.0 +export default { + // ... + watch: { + 'testRequest.data'() { + // ... + } + } +} +``` -### 限制 method 快照数量 +## @alova/scene-\*优化 -```javascript -import { globalConfig } from 'alova'; -globalConfig({ - methodSnapshots: 1000 // 设置最大数量,默认为1000 +我们对`@alova/scene-*`做了部分策略的优化,并将它们移动到`alova/client`中。 + +### usePagination + +#### 1. 默认设 abortLast 为 true + +当连续翻页或查询数据时,只会展示最后一次操作的内容,更加符合体验。 + +```js +// 2.x需要显式设置 +usePagination({ + // ... + abortLast: true +}); + +// 3.x不需要 +usePagination({ + // ... }); ``` -### 移除废弃的 responsed +#### 优化在 react 下的使用 + +在 react 下,page、pageSize 导出项不再是一个 reactState,现在统一使用 update 来更改状态。 + +```js +// 2.x +const { + page: [page, setPage], + pageSize: [pageSize, setPageSize] + /*...*/ +} = usePagination(/*...*/); +const handleAddPage = () => { + setPage(page + 1); +}; +const handleSetPageSize = newPageSize => { + setPageSize(newPageSize); +}; + +// 3.x +const { page, pageSize, update /*...*/ } = usePagination(/*...*/); +const handleAddPage = () => { + update({ page: page + 1 }); +}; +const handleSetPageSize = newPageSize => { + update({ pageSize: newPageSize }); +}; +``` + +### useForm + +#### 1. 不再支持直接传入 id 返回缓存的 hookReturns + +由于原来的 scene 包直接划分为了`@alova/scene-react`、`@alova/scene-vue`、`@alova/scene-svelte`三个包,每个包的导出状态固定为对应 UI 框架的状态类型,改版后统一为了一个 client 库,在多步骤表单提交时也需要提供 method 实例或 methodHandler 来自动推断应该导出哪个 UI 框架的状态类型,但功能保持不变。 + +```js +// 2.x中states为对应的UI框架的状态类型 +const staates = useForm('form-id'); + +// 3.x中必须也提供method实例或methodHandler +const { states, update } = useForm(formData => methodHandler(formData), { + // ... + id: 'form-id' +}); +``` + +### useSQRequest + +#### 1. `updateStateEffect` 不再支持 method 匹配器方式使用 + +由于 updateState 中不再支持 method 匹配器用法,`updateStateEffect`同步修改,同时也不再支持`onMatch`回调函数。 + +#### 2. `SilentMethod`成员方法使用修改 + +由于 l2Cache 可设置为异步函数的原因,`SilentMethod.replace/remove/save`以及 `filterSilentMethods/getSilentMethod` 由同步函数改为返回 promise 的异步函数。 -- **统一使用 responded** +```js +import { filterSilentMethods, getSilentMethod } from 'alova/client'; -```md +const silentMethods = await filterSilentMethods('method-name', 'queue-name'); +const silentMethod = await getSilentMethod('method-name', 'queue-name'); + +await silentMethod.replace(newSilentMethod); +await silentMethod.remove(); +await silentMethod.save(); +``` + +### accessAction + +新增 silent 参数,通过 silent 参数控制,当未匹配到 delegation 时不抛出错误 + +```js +// 未匹配到method-name时会抛出错误 +accessAction('method-name', ({ send }) => { + send(); +}); + +// 此时不会抛出错误 +accessAction( + 'method-name', + ({ send }) => { + send(); + }, + true +); +``` + +### 所有 useHook 的 force 函数更改为 AlovaEvent 对象 + +```js +// @2.x +useRequest(Getter, { + force(arg1, arg2) { + // ... + } +}); + +// 3.x +useRequest(Getter, { + force(event) { + // 获取 args + const arg1 = event.sendArgs[0]; + const arg2 = event.sendArgs[1]; + + // 获取method + const method = event.method; + } +}); +``` +## 字段修改 + +### `method.key` 简化为 `method.key` + +```js +// 2.x +method.__key__ = 'custom-key'; + +// 3.x +method.key = 'custom-key'; +``` + +### `method.transformData` 简化为 `method.transform` + +```js +// 2.x +alova.Get('/api/profile', { + transformData(data) { + return data.detail; + } +}); + +// 3.x +alova.Get('/api/profile', { + transform(data) { + return data.detail; + } +}); +``` + +### `method.localCache` 更改为 `method.cacheFor` + +```js +// 2.x +alova.Get('/api/profile', { + localCache: 1000 * 60 * 60 +}); + +// 3.x +alova.Get('/api/profile', { + cacheFor: 1000 * 60 * 60 +}); +``` + +### @alova/adapter-uniapp 导出名称修改 + +由于 alova 的`storageAdapter`改名为`l2Cache`,因此`@alova/adapter-uniapp`的导出项`uniappStorageAdapter`更改为`uniappL2CacheAdapter`。 + +## 其他优化项 + +### 支持依赖收集 + +这是一个性能优化项,在alova@3.x中的所有 useHook 将不会更新未访问过的状态,以减少内部更新状态导致多余的视图渲染。 + +一个简单的示例如下,当未使用到`loading`时,`useRequest`的内部不会更新`loading`状态以减少额外的视图渲染。 + +```js +const App = () => { + const { data } = useRequest(method1); + return data ?
{data.name}
: null; +}; + +export default App; +``` + +### middleware 优化 + +在alova@2.x,在 middleware 中访问 states 数据显得不够灵活,因为你获得的是框架相关的状态集合,这不利于编写通用的 middleware,因此在 3.x 中做出以下修改: + +1. 改用框架无关的 proxyState 来访问和修改状态,让 middleware 可以在任意的 UI 框架下使用。 + +```js +middleware({ proxyStates, args }, ({ update }) => { + // 访问状态 + const loadingValue = proxyStates.loading.v; + + // 修改状态 + proxyStates.loading.v = true; +}); +``` + +2. sendArgs 和 fetchArgs 更改为 args。 + +### 所有的事件绑定函数返回自身对象 + +为了优化使用体验,减少重命名,所有的事件绑定函数返回 useHook 的导出对象以获得链式调用绑定事件的效果。 + +```js +// 2.x +const { onSuccess, loading, data } = useRequest(method); +onSuccess(event => { + // ... +}); +const { onSuccess: onSuccess2, error } = useRequest(method2); +onSuccess2(event => { + // ... +}); + +// 3.x +const { loading, data } = useRequest(method).onSuccess(event => { + // ... +}); +const { error } = useRequest(method2).onSuccess(event => { + // ... +}); ``` From df5ae5429f14e41412e06401cb0e8f5df23aad7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=83=A1=E9=95=87?= Date: Fri, 21 Jun 2024 14:28:23 +0800 Subject: [PATCH 09/11] docs: update links and content for v3.0 release notes and tutorials - Correct minor typos and update documentation to reflect changes in the latest version. - Ensure all links are pointing to the correct resources and that the content is up-to-date with the new features and fixes. --- .github/workflows/deploy-docs.yml | 116 +-- .prettierignore | 3 + README.md | 1 + babel.config.js | 6 +- codesandbox/00-create-alova/react.js | 18 +- codesandbox/00-create-alova/svelte.js | 18 +- codesandbox/00-create-alova/vueComposition.js | 18 +- codesandbox/00-create-alova/vueOptions.js | 18 +- .../02-first-request/get.js | 24 +- .../02-first-request/post.js | 32 +- .../react-useRequest.en.jsx | 30 +- .../react-useRequest.zh.jsx | 30 +- .../vueComposition-useRequest.en.vue | 26 +- .../vueComposition-useRequest.zh.vue | 26 +- .../vueOptions-useRequest.en.vue | 46 +- .../vueOptions-useRequest.zh.vue | 46 +- .../04-use-watcher/react-search.en.jsx | 90 +- .../04-use-watcher/react-search.zh.jsx | 98 +-- .../vueComposition-search.en.vue | 73 +- .../vueComposition-search.zh.vue | 72 +- .../04-use-watcher/vueOptions-search.en.vue | 91 +- .../04-use-watcher/vueOptions-search.zh.vue | 91 +- codesandbox@3/00-create-alova/react.js | 18 +- codesandbox@3/00-create-alova/svelte.js | 18 +- .../00-create-alova/vueComposition.js | 18 +- codesandbox@3/00-create-alova/vueOptions.js | 18 +- .../vueComposition-useRequest.zh.vue | 2 +- .../02-use-watcher/react-search.en.jsx | 90 +- .../02-use-watcher/react-search.zh.jsx | 98 +-- .../vueComposition-search.en.vue | 73 +- .../vueComposition-search.zh.vue | 72 +- .../02-use-watcher/vueOptions-search.en.vue | 91 +- .../02-use-watcher/vueOptions-search.zh.vue | 91 +- docs/about/_category_.json | 12 +- docs/api/01-alova.md | 24 +- docs/api/_category_.json | 12 +- docs/contributing/01-overview.md | 2 +- docs/contributing/_category_.json | 12 +- docs/release-notes-v3.md | 6 +- .../03-framework/01-vue-composition.md | 27 + docs/resource/03-framework/01-vue-options.md | 332 -------- docs/resource/03-framework/02-react.md | 15 + docs/resource/03-framework/03-svelte.md | 15 + .../03-framework/{02-solid.md => 04-solid.md} | 0 .../{03-angular.md => 05-angular.md} | 0 .../{05-preact.md => 06-preact.md} | 0 docs/resource/03-framework/07-vue-options.md | 222 +++++ .../03-framework/{06-qwik.md => 08-qwik.md} | 0 .../{04-native-mp.md => 09-native-mp.md} | 0 .../03-framework/{07-lit.md => 10-lit.md} | 0 .../{08-stencil.md => 11-stencil.md} | 0 docs/resource/03-framework/README.md | 5 - docs/resource/03-framework/_category_.json | 12 +- .../02-getting-started/03-basic/03-method.md | 4 +- .../03-basic/05-global-interceptor.md | 2 +- .../03-basic/07-combine-framework.md | 31 +- .../03-client/01-strategy/01-use-request.md | 5 +- .../03-client/01-strategy/03-use-fetcher.md | 26 +- .../01-strategy/04-use-pagination.md | 2 +- docs/tutorial/03-client/01-strategy/README.md | 2 + docs/tutorial/05-cache/06-controlled-cache.md | 6 +- .../01-in-depth/05-custom-method-key.md | 4 +- .../06-advanced/01-in-depth/10-typescript.md | 6 +- .../06-advanced/02-custom/01-http-adapter.md | 2 +- .../03-manage-cache-by-indexeddb.md | 12 +- docsearch.json | 67 +- docusaurus.config.ts | 2 +- example-links/ActionDelegationMiddleware.tsx | 12 +- example-links/CaptchaSend.tsx | 12 +- example-links/ConditionSearch.tsx | 12 +- example-links/ControlledCacheByIndexedDB.tsx | 12 +- example-links/FormHook.tsx | 12 +- example-links/InitPage.tsx | 12 +- example-links/LoadMore.tsx | 12 +- example-links/MemoryCache.tsx | 12 +- example-links/NoteSilentReact.tsx | 12 +- example-links/Pagination.tsx | 12 +- example-links/Prefetch.tsx | 12 +- example-links/RetriableHook.tsx | 12 +- example-links/SerialRequest.tsx | 12 +- example-links/SettingSilentSvelte.tsx | 12 +- example-links/SimpleListSilentVue.tsx | 12 +- example-links/StoragePlaceholder.tsx | 12 +- example-links/StorageRestore.tsx | 12 +- example-links/SubmitForm.tsx | 12 +- example-links/UpdateState.tsx | 12 +- extract-docs.js | 101 +-- .../current/api/01-alova.md | 24 +- .../current/api/_category_.json | 12 +- .../current/contributing/01-overview.md | 2 +- .../current/contributing/_category_.json | 12 +- .../current/release-notes-v3.md | 6 +- .../03-framework/01-vue-composition.md | 27 + .../current/resource/03-framework/02-react.md | 15 + .../resource/03-framework/03-svelte.md | 15 + .../03-framework/{02-solid.md => 04-solid.md} | 0 .../{03-angular.md => 05-angular.md} | 0 .../{05-preact.md => 06-preact.md} | 0 .../{01-vue-options.md => 07-vue-options.md} | 126 +-- .../03-framework/{06-qwik.md => 08-qwik.md} | 0 .../{04-native-mp.md => 09-native-mp.md} | 0 .../03-framework/{07-lit.md => 10-lit.md} | 0 .../{08-stencil.md => 11-stencil.md} | 0 .../current/resource/03-framework/README.md | 5 - .../02-getting-started/03-basic/03-method.md | 4 +- .../03-basic/05-global-interceptor.md | 2 +- .../03-basic/07-combine-framework.md | 31 +- .../02-getting-started/_category_.json | 6 +- .../03-client/01-strategy/01-use-request.md | 5 +- .../03-client/01-strategy/03-use-fetcher.md | 30 +- .../01-strategy/04-use-pagination.md | 2 +- .../_category_.json | 6 +- .../tutorial/03-client/01-strategy/README.md | 4 + .../tutorial/05-cache/06-controlled-cache.md | 6 +- .../01-in-depth/05-custom-method-key.md | 4 +- .../06-advanced/01-in-depth/10-typescript.md | 6 +- .../06-advanced/02-custom/01-http-adapter.md | 2 +- .../03-manage-cache-by-indexeddb.md | 12 +- .../version-2.x/api/_category_.json | 12 +- .../version-2.x/contributing/_category_.json | 12 +- .../tutorial/01-example/_category_.json | 14 +- .../tutorial/02-getting-started/README.md | 296 +++---- .../02-getting-started/_category_.json | 6 +- .../03-combine-framework/_category_.json | 6 +- .../version-2.x/tutorial/04-cache/README.md | 18 +- .../tutorial/04-cache/_category_.json | 6 +- .../01-sensorless-data-interaction/README.md | 292 +++---- .../_category_.json | 6 +- .../tutorial/05-strategy/_category_.json | 6 +- .../tutorial/06-advanced/_category_.json | 6 +- .../08-request-adapter/_category_.json | 12 +- .../tutorial/10-custom/_category_.json | 12 +- .../tutorial/11-others/_category_.json | 12 +- package.json | 3 +- src/components/AdCard/index.tsx | 38 +- src/components/AdCard/styles.module.css | 28 +- src/components/EmbedCodesandbox.tsx | 46 +- src/components/EmbedSandpack.tsx | 304 +++---- src/components/IconFont.tsx | 61 +- src/components/NavCard/index.tsx | 82 +- src/components/NavCard/style.module.css | 112 +-- src/components/PageModule/index.module.css | 80 +- src/components/PageModule/index.tsx | 98 ++- src/components/SupportList/index.tsx | 419 ++++----- src/components/SupportList/style.module.css | 112 +-- .../Contributors/index.module.css | 50 +- .../_indexComponent/Contributors/index.tsx | 46 +- src/pages/_indexComponent/Features/index.tsx | 180 ++-- .../_indexComponent/Like/index.module.css | 34 +- src/pages/_indexComponent/Like/index.tsx | 132 +-- src/pages/_indexComponent/Strategy/index.tsx | 194 ++--- .../_indexComponent/Strategy/style.module.css | 170 ++-- src/pages/_indexComponent/Support/index.tsx | 30 +- src/theme/Root.tsx | 10 +- src/theme/TOC/index.js | 4 +- static/iconfont/demo.css | 102 +-- static/iconfont/demo_index.html | 802 +++++++++--------- static/iconfont/iconfont.css | 35 +- static/iconfont/iconfont.js | 67 +- tsconfig.json | 14 +- .../version-2.x/about/_category_.json | 12 +- .../version-2.x/api/_category_.json | 12 +- .../version-2.x/contributing/_category_.json | 12 +- .../tutorial/01-example/_category_.json | 14 +- .../tutorial/02-getting-started/README.md | 272 +++--- .../02-getting-started/_category_.json | 6 +- .../03-combine-framework/_category_.json | 6 +- .../version-2.x/tutorial/04-cache/README.md | 18 +- .../tutorial/04-cache/_category_.json | 6 +- .../01-sensorless-data-interaction/README.md | 292 +++---- .../_category_.json | 6 +- .../tutorial/05-strategy/_category_.json | 6 +- .../tutorial/06-advanced/_category_.json | 6 +- .../tutorial/07-best-practice/_category_.json | 12 +- .../08-request-adapter/_category_.json | 12 +- .../tutorial/09-framework/_category_.json | 12 +- .../tutorial/10-custom/_category_.json | 12 +- .../tutorial/11-others/_category_.json | 12 +- versions.json | 4 +- 179 files changed, 3945 insertions(+), 3878 deletions(-) create mode 100644 .prettierignore create mode 100644 docs/resource/03-framework/01-vue-composition.md delete mode 100644 docs/resource/03-framework/01-vue-options.md create mode 100644 docs/resource/03-framework/02-react.md create mode 100644 docs/resource/03-framework/03-svelte.md rename docs/resource/03-framework/{02-solid.md => 04-solid.md} (100%) rename docs/resource/03-framework/{03-angular.md => 05-angular.md} (100%) rename docs/resource/03-framework/{05-preact.md => 06-preact.md} (100%) create mode 100644 docs/resource/03-framework/07-vue-options.md rename docs/resource/03-framework/{06-qwik.md => 08-qwik.md} (100%) rename docs/resource/03-framework/{04-native-mp.md => 09-native-mp.md} (100%) rename docs/resource/03-framework/{07-lit.md => 10-lit.md} (100%) rename docs/resource/03-framework/{08-stencil.md => 11-stencil.md} (100%) delete mode 100644 docs/resource/03-framework/README.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/01-vue-composition.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/02-react.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/03-svelte.md rename i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/{02-solid.md => 04-solid.md} (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/{03-angular.md => 05-angular.md} (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/{05-preact.md => 06-preact.md} (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/{01-vue-options.md => 07-vue-options.md} (69%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/{06-qwik.md => 08-qwik.md} (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/{04-native-mp.md => 09-native-mp.md} (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/{07-lit.md => 10-lit.md} (100%) rename i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/{08-stencil.md => 11-stencil.md} (100%) delete mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/README.md diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index dad635e38..a594ae708 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -1,58 +1,58 @@ -name: Doc deploy - -on: - push: - branches: - - main - # 如果你想要进一步定义触发条件、路径等,可以查看文档 - # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#on - -jobs: - deploy: - name: Deploy to GitHub Pages - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: 18 - cache: npm - - - name: Install dependencies - run: npm ci - - name: Build website - run: npm run build - - # 部署到 GitHub Pages 的热门选择: - # 文档:https://github.com/peaceiris/actions-gh-pages#%EF%B8%8F-docusaurus - - name: Deploy to GitHub Pages - uses: peaceiris/actions-gh-pages@v3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - # 要发布到 `gh-pages` 分支的构建输出: - publish_dir: ./build - # 下面两行会将此次部署 commit 的作者设置为官方的 - # GH-Actions 机器人: - # https://github.com/actions/checkout/issues/13#issuecomment-724415212 - # 如果不设置这两个字段,GH actions 机器人会被默认使用。 - # 你可以用自己的用户信息替换它们。 - # user_name: github-actions[bot] - # user_email: 41898282+github-actions[bot]@users.noreply.github.com - - algolia: - name: Crawl with algolia - needs: [deploy] - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - name: Run algolia/docsearch-scraper image - env: - ALGOLIA_APP_ID: ${{ secrets.ALGOLIA_APP_ID }} - ALGOLIA_API_KEY: ${{ secrets.ALGOLIA_API_KEY }} - run: | - docker run \ - --env APPLICATION_ID=${ALGOLIA_APP_ID} \ - --env API_KEY=${ALGOLIA_API_KEY} \ - --env "CONFIG=$(cat docsearch.json | jq -r tostring)" \ - algolia/docsearch-scraper +name: Doc deploy + +on: + push: + branches: + - main + # 如果你想要进一步定义触发条件、路径等,可以查看文档 + # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#on + +jobs: + deploy: + name: Deploy to GitHub Pages + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 18 + cache: npm + + - name: Install dependencies + run: npm ci + - name: Build website + run: npm run build + + # 部署到 GitHub Pages 的热门选择: + # 文档:https://github.com/peaceiris/actions-gh-pages#%EF%B8%8F-docusaurus + - name: Deploy to GitHub Pages + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + # 要发布到 `gh-pages` 分支的构建输出: + publish_dir: ./build + # 下面两行会将此次部署 commit 的作者设置为官方的 + # GH-Actions 机器人: + # https://github.com/actions/checkout/issues/13#issuecomment-724415212 + # 如果不设置这两个字段,GH actions 机器人会被默认使用。 + # 你可以用自己的用户信息替换它们。 + # user_name: github-actions[bot] + # user_email: 41898282+github-actions[bot]@users.noreply.github.com + + algolia: + name: Crawl with algolia + needs: [deploy] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Run algolia/docsearch-scraper image + env: + ALGOLIA_APP_ID: ${{ secrets.ALGOLIA_APP_ID }} + ALGOLIA_API_KEY: ${{ secrets.ALGOLIA_API_KEY }} + run: | + docker run \ + --env APPLICATION_ID=${ALGOLIA_APP_ID} \ + --env API_KEY=${ALGOLIA_API_KEY} \ + --env "CONFIG=$(cat docsearch.json | jq -r tostring)" \ + algolia/docsearch-scraper diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 000000000..80e0344ef --- /dev/null +++ b/.prettierignore @@ -0,0 +1,3 @@ +/build +pnpm-lock.yaml +package-lock.json diff --git a/README.md b/README.md index 685789f72..d4a556871 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,3 @@ # The alova doc Website + The official website of alova. diff --git a/babel.config.js b/babel.config.js index 92df610b3..6bf8d26f1 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,3 +1,3 @@ -module.exports = { - presets: [require.resolve('@docusaurus/core/lib/babel/preset')] -}; +module.exports = { + presets: [require.resolve('@docusaurus/core/lib/babel/preset')] +}; diff --git a/codesandbox/00-create-alova/react.js b/codesandbox/00-create-alova/react.js index 4ea71c41c..61809ed88 100644 --- a/codesandbox/00-create-alova/react.js +++ b/codesandbox/00-create-alova/react.js @@ -1,9 +1,9 @@ -import { createAlova } from 'alova'; -import GlobalFetch from 'alova/GlobalFetch'; -import ReactHook from 'alova/react'; -export const alovaInstance = createAlova({ - baseURL: 'https://jsonplaceholder.typicode.com', - statesHook: ReactHook, - requestAdapter: GlobalFetch(), - responded: response => response.json() -}); +import { createAlova } from 'alova'; +import GlobalFetch from 'alova/GlobalFetch'; +import ReactHook from 'alova/react'; +export const alovaInstance = createAlova({ + baseURL: 'https://jsonplaceholder.typicode.com', + statesHook: ReactHook, + requestAdapter: GlobalFetch(), + responded: response => response.json() +}); diff --git a/codesandbox/00-create-alova/svelte.js b/codesandbox/00-create-alova/svelte.js index ee849b7f9..3c3519fb0 100644 --- a/codesandbox/00-create-alova/svelte.js +++ b/codesandbox/00-create-alova/svelte.js @@ -1,9 +1,9 @@ -import { createAlova } from 'alova'; -import GlobalFetch from 'alova/GlobalFetch'; -import SvelteHook from 'alova/svelte'; -export const alovaInstance = createAlova({ - baseURL: 'https://jsonplaceholder.typicode.com', - statesHook: SvelteHook, - requestAdapter: GlobalFetch(), - responded: response => response.json() -}); +import { createAlova } from 'alova'; +import GlobalFetch from 'alova/GlobalFetch'; +import SvelteHook from 'alova/svelte'; +export const alovaInstance = createAlova({ + baseURL: 'https://jsonplaceholder.typicode.com', + statesHook: SvelteHook, + requestAdapter: GlobalFetch(), + responded: response => response.json() +}); diff --git a/codesandbox/00-create-alova/vueComposition.js b/codesandbox/00-create-alova/vueComposition.js index 97da70a23..3562ce056 100644 --- a/codesandbox/00-create-alova/vueComposition.js +++ b/codesandbox/00-create-alova/vueComposition.js @@ -1,9 +1,9 @@ -import { createAlova } from 'alova'; -import GlobalFetch from 'alova/GlobalFetch'; -import VueHook from 'alova/vue'; -export const alovaInstance = createAlova({ - baseURL: 'https://jsonplaceholder.typicode.com', - statesHook: VueHook, - requestAdapter: GlobalFetch(), - responded: response => response.json() -}); +import { createAlova } from 'alova'; +import GlobalFetch from 'alova/GlobalFetch'; +import VueHook from 'alova/vue'; +export const alovaInstance = createAlova({ + baseURL: 'https://jsonplaceholder.typicode.com', + statesHook: VueHook, + requestAdapter: GlobalFetch(), + responded: response => response.json() +}); diff --git a/codesandbox/00-create-alova/vueOptions.js b/codesandbox/00-create-alova/vueOptions.js index f9800bffe..7b0ef301f 100644 --- a/codesandbox/00-create-alova/vueOptions.js +++ b/codesandbox/00-create-alova/vueOptions.js @@ -1,9 +1,9 @@ -import { VueOptionsHook } from '@alova/vue-options'; -import { createAlova } from 'alova'; -import GlobalFetch from 'alova/GlobalFetch'; -export const alovaInstance = createAlova({ - baseURL: 'https://jsonplaceholder.typicode.com', - statesHook: VueOptionsHook, - requestAdapter: GlobalFetch(), - responded: response => response.json() -}); +import { VueOptionsHook } from '@alova/vue-options'; +import { createAlova } from 'alova'; +import GlobalFetch from 'alova/GlobalFetch'; +export const alovaInstance = createAlova({ + baseURL: 'https://jsonplaceholder.typicode.com', + statesHook: VueOptionsHook, + requestAdapter: GlobalFetch(), + responded: response => response.json() +}); diff --git a/codesandbox/01-getting-started/02-first-request/get.js b/codesandbox/01-getting-started/02-first-request/get.js index 8ed2a64e5..20ca0b82a 100644 --- a/codesandbox/01-getting-started/02-first-request/get.js +++ b/codesandbox/01-getting-started/02-first-request/get.js @@ -1,12 +1,12 @@ -import { createAlova } from 'alova'; -import GlobalFetch from 'alova/GlobalFetch'; - -const alovaInstance = createAlova({ - requestAdapter: GlobalFetch() -}); -alovaInstance - .Get('https://jsonplaceholder.typicode.com/todos/1') - .then(response => response.text()) - .then(data => { - app.innerHTML = data; - }); +import { createAlova } from 'alova'; +import GlobalFetch from 'alova/GlobalFetch'; + +const alovaInstance = createAlova({ + requestAdapter: GlobalFetch() +}); +alovaInstance + .Get('https://jsonplaceholder.typicode.com/todos/1') + .then(response => response.text()) + .then(data => { + app.innerHTML = data; + }); diff --git a/codesandbox/01-getting-started/02-first-request/post.js b/codesandbox/01-getting-started/02-first-request/post.js index b21293408..dea6f1538 100644 --- a/codesandbox/01-getting-started/02-first-request/post.js +++ b/codesandbox/01-getting-started/02-first-request/post.js @@ -1,16 +1,16 @@ -import { createAlova } from 'alova'; -import GlobalFetch from 'alova/GlobalFetch'; - -const alovaInstance = createAlova({ - requestAdapter: GlobalFetch() -}); -alovaInstance - .Post('https://jsonplaceholder.typicode.com/posts', { - title: 'foo', - body: 'bar', - userId: 1 - }) - .then(response => response.text()) - .then(data => { - app.innerHTML = data; - }); +import { createAlova } from 'alova'; +import GlobalFetch from 'alova/GlobalFetch'; + +const alovaInstance = createAlova({ + requestAdapter: GlobalFetch() +}); +alovaInstance + .Post('https://jsonplaceholder.typicode.com/posts', { + title: 'foo', + body: 'bar', + userId: 1 + }) + .then(response => response.text()) + .then(data => { + app.innerHTML = data; + }); diff --git a/codesandbox/01-getting-started/03-combine-framework/react-useRequest.en.jsx b/codesandbox/01-getting-started/03-combine-framework/react-useRequest.en.jsx index 6c267c2f4..b30c75d05 100644 --- a/codesandbox/01-getting-started/03-combine-framework/react-useRequest.en.jsx +++ b/codesandbox/01-getting-started/03-combine-framework/react-useRequest.en.jsx @@ -1,15 +1,15 @@ -import { useRequest } from 'alova'; -import { alovaInstance } from './api'; - -const App = () => { - // Use the alova instance to create a method and pass it to useRequest to send the request - const { loading, data, error } = useRequest(alovaInstance.Get('/todos/1')); - - if (loading) { - return
Loading...
; - } else if (error) { - return
{error.message}
; - } - return responseData: {JSON.stringify(data)}; -}; -export default App; +import { useRequest } from 'alova'; +import { alovaInstance } from './api'; + +const App = () => { + // Use the alova instance to create a method and pass it to useRequest to send the request + const { loading, data, error } = useRequest(alovaInstance.Get('/todos/1')); + + if (loading) { + return
Loading...
; + } else if (error) { + return
{error.message}
; + } + return responseData: {JSON.stringify(data)}; +}; +export default App; diff --git a/codesandbox/01-getting-started/03-combine-framework/react-useRequest.zh.jsx b/codesandbox/01-getting-started/03-combine-framework/react-useRequest.zh.jsx index 65cf17ad2..25f63b87c 100644 --- a/codesandbox/01-getting-started/03-combine-framework/react-useRequest.zh.jsx +++ b/codesandbox/01-getting-started/03-combine-framework/react-useRequest.zh.jsx @@ -1,15 +1,15 @@ -import { useRequest } from 'alova'; -import { alovaInstance } from './api'; - -const App = () => { - // 使用alova实例创建method并传给useRequest即可发送请求 - const { loading, data, error } = useRequest(alovaInstance.Get('/todos/1')); - - if (loading) { - return
Loading...
; - } else if (error) { - return
{error.message}
; - } - return responseData: {JSON.stringify(data)}; -}; -export default App; +import { useRequest } from 'alova'; +import { alovaInstance } from './api'; + +const App = () => { + // 使用alova实例创建method并传给useRequest即可发送请求 + const { loading, data, error } = useRequest(alovaInstance.Get('/todos/1')); + + if (loading) { + return
Loading...
; + } else if (error) { + return
{error.message}
; + } + return responseData: {JSON.stringify(data)}; +}; +export default App; diff --git a/codesandbox/01-getting-started/03-combine-framework/vueComposition-useRequest.en.vue b/codesandbox/01-getting-started/03-combine-framework/vueComposition-useRequest.en.vue index 7e5d3b460..42ec7c004 100644 --- a/codesandbox/01-getting-started/03-combine-framework/vueComposition-useRequest.en.vue +++ b/codesandbox/01-getting-started/03-combine-framework/vueComposition-useRequest.en.vue @@ -1,13 +1,13 @@ - - - + + + diff --git a/codesandbox/01-getting-started/03-combine-framework/vueComposition-useRequest.zh.vue b/codesandbox/01-getting-started/03-combine-framework/vueComposition-useRequest.zh.vue index 788c0a79c..aee39a0b7 100644 --- a/codesandbox/01-getting-started/03-combine-framework/vueComposition-useRequest.zh.vue +++ b/codesandbox/01-getting-started/03-combine-framework/vueComposition-useRequest.zh.vue @@ -1,13 +1,13 @@ - - - + + + diff --git a/codesandbox/01-getting-started/03-combine-framework/vueOptions-useRequest.en.vue b/codesandbox/01-getting-started/03-combine-framework/vueOptions-useRequest.en.vue index 5d195399d..08b769f98 100644 --- a/codesandbox/01-getting-started/03-combine-framework/vueOptions-useRequest.en.vue +++ b/codesandbox/01-getting-started/03-combine-framework/vueOptions-useRequest.en.vue @@ -1,23 +1,23 @@ - - - + + + diff --git a/codesandbox/01-getting-started/03-combine-framework/vueOptions-useRequest.zh.vue b/codesandbox/01-getting-started/03-combine-framework/vueOptions-useRequest.zh.vue index 5908fb25d..a10ae2540 100644 --- a/codesandbox/01-getting-started/03-combine-framework/vueOptions-useRequest.zh.vue +++ b/codesandbox/01-getting-started/03-combine-framework/vueOptions-useRequest.zh.vue @@ -1,23 +1,23 @@ - - - + + + diff --git a/codesandbox/03-learning/04-use-watcher/react-search.en.jsx b/codesandbox/03-learning/04-use-watcher/react-search.en.jsx index 3ac6378a8..5fa87e4ee 100644 --- a/codesandbox/03-learning/04-use-watcher/react-search.en.jsx +++ b/codesandbox/03-learning/04-use-watcher/react-search.en.jsx @@ -1,45 +1,45 @@ -import { useWatcher } from 'alova'; -import { useState } from 'react'; -import { alovaInstance } from './api'; - -//Create method instance -const filterTodoList = userId => { - return alovaInstance.Get(`/users/${userId}/todos`); -}; - -const App = () => { - const [userId, setUserId] = useState(1); - const { loading, data = [] } = useWatcher( - // Must be set to a function that returns a method instance - () => filterTodoList(userId), - - // The monitored status array, these status changes will trigger a request - [userId] - ); - - return ( - <> - - - {/* Render the filtered todo list */} - {loading ?
Loading...
: null} - {!loading ? ( -
    - {data.map(todo => ( -
  • - {todo.completed ? '(Completed)' : ''} - {todo.title} -
  • - ))} -
- ) : null} - - ); -}; -export default App; +import { useWatcher } from 'alova'; +import { useState } from 'react'; +import { alovaInstance } from './api'; + +//Create method instance +const filterTodoList = userId => { + return alovaInstance.Get(`/users/${userId}/todos`); +}; + +const App = () => { + const [userId, setUserId] = useState(1); + const { loading, data = [] } = useWatcher( + // Must be set to a function that returns a method instance + () => filterTodoList(userId), + + // The monitored status array, these status changes will trigger a request + [userId] + ); + + return ( + <> + + + {/* Render the filtered todo list */} + {loading ?
Loading...
: null} + {!loading ? ( +
    + {data.map(todo => ( +
  • + {todo.completed ? '(Completed)' : ''} + {todo.title} +
  • + ))} +
+ ) : null} + + ); +}; +export default App; diff --git a/codesandbox/03-learning/04-use-watcher/react-search.zh.jsx b/codesandbox/03-learning/04-use-watcher/react-search.zh.jsx index 6e0ca9a25..3b78e2fde 100644 --- a/codesandbox/03-learning/04-use-watcher/react-search.zh.jsx +++ b/codesandbox/03-learning/04-use-watcher/react-search.zh.jsx @@ -1,49 +1,49 @@ -import { useWatcher } from 'alova'; -import { useState } from 'react'; -import { alovaInstance } from './api'; - -// 创建method实例 -const filterTodoList = userId => { - return alovaInstance.Get(`/users/${userId}/todos`); -}; - -const App = () => { - const [userId, setUserId] = useState(1); - const { - loading, - data = [], - error - } = useWatcher( - // 必须设置为返回method实例的函数 - () => filterTodoList(userId), - - // 被监听的状态数组,这些状态变化将会触发一次请求 - [userId] - ); - - return ( - <> - - - {/* 渲染筛选后的todo列表 */} - {loading ?
Loading...
: null} - {!loading ? ( -
    - {data.map(todo => ( -
  • - {todo.completed ? '(Completed)' : ''} - {todo.title} -
  • - ))} -
- ) : null} - - ); -}; -export default App; +import { useWatcher } from 'alova'; +import { useState } from 'react'; +import { alovaInstance } from './api'; + +// 创建method实例 +const filterTodoList = userId => { + return alovaInstance.Get(`/users/${userId}/todos`); +}; + +const App = () => { + const [userId, setUserId] = useState(1); + const { + loading, + data = [], + error + } = useWatcher( + // 必须设置为返回method实例的函数 + () => filterTodoList(userId), + + // 被监听的状态数组,这些状态变化将会触发一次请求 + [userId] + ); + + return ( + <> + + + {/* 渲染筛选后的todo列表 */} + {loading ?
Loading...
: null} + {!loading ? ( +
    + {data.map(todo => ( +
  • + {todo.completed ? '(Completed)' : ''} + {todo.title} +
  • + ))} +
+ ) : null} + + ); +}; +export default App; diff --git a/codesandbox/03-learning/04-use-watcher/vueComposition-search.en.vue b/codesandbox/03-learning/04-use-watcher/vueComposition-search.en.vue index ff0f42357..000bd7487 100644 --- a/codesandbox/03-learning/04-use-watcher/vueComposition-search.en.vue +++ b/codesandbox/03-learning/04-use-watcher/vueComposition-search.en.vue @@ -1,37 +1,36 @@ - - - \ No newline at end of file + + + diff --git a/codesandbox/03-learning/04-use-watcher/vueComposition-search.zh.vue b/codesandbox/03-learning/04-use-watcher/vueComposition-search.zh.vue index a85d907a4..b7a3729a9 100644 --- a/codesandbox/03-learning/04-use-watcher/vueComposition-search.zh.vue +++ b/codesandbox/03-learning/04-use-watcher/vueComposition-search.zh.vue @@ -1,36 +1,36 @@ - - - \ No newline at end of file + + + diff --git a/codesandbox/03-learning/04-use-watcher/vueOptions-search.en.vue b/codesandbox/03-learning/04-use-watcher/vueOptions-search.en.vue index 86931b54c..46f14732a 100644 --- a/codesandbox/03-learning/04-use-watcher/vueOptions-search.en.vue +++ b/codesandbox/03-learning/04-use-watcher/vueOptions-search.en.vue @@ -1,44 +1,47 @@ - - - \ No newline at end of file + + + diff --git a/codesandbox/03-learning/04-use-watcher/vueOptions-search.zh.vue b/codesandbox/03-learning/04-use-watcher/vueOptions-search.zh.vue index 1af46de14..562330f19 100644 --- a/codesandbox/03-learning/04-use-watcher/vueOptions-search.zh.vue +++ b/codesandbox/03-learning/04-use-watcher/vueOptions-search.zh.vue @@ -1,44 +1,47 @@ - - - \ No newline at end of file + + + diff --git a/codesandbox@3/00-create-alova/react.js b/codesandbox@3/00-create-alova/react.js index 4ea71c41c..61809ed88 100644 --- a/codesandbox@3/00-create-alova/react.js +++ b/codesandbox@3/00-create-alova/react.js @@ -1,9 +1,9 @@ -import { createAlova } from 'alova'; -import GlobalFetch from 'alova/GlobalFetch'; -import ReactHook from 'alova/react'; -export const alovaInstance = createAlova({ - baseURL: 'https://jsonplaceholder.typicode.com', - statesHook: ReactHook, - requestAdapter: GlobalFetch(), - responded: response => response.json() -}); +import { createAlova } from 'alova'; +import GlobalFetch from 'alova/GlobalFetch'; +import ReactHook from 'alova/react'; +export const alovaInstance = createAlova({ + baseURL: 'https://jsonplaceholder.typicode.com', + statesHook: ReactHook, + requestAdapter: GlobalFetch(), + responded: response => response.json() +}); diff --git a/codesandbox@3/00-create-alova/svelte.js b/codesandbox@3/00-create-alova/svelte.js index ee849b7f9..3c3519fb0 100644 --- a/codesandbox@3/00-create-alova/svelte.js +++ b/codesandbox@3/00-create-alova/svelte.js @@ -1,9 +1,9 @@ -import { createAlova } from 'alova'; -import GlobalFetch from 'alova/GlobalFetch'; -import SvelteHook from 'alova/svelte'; -export const alovaInstance = createAlova({ - baseURL: 'https://jsonplaceholder.typicode.com', - statesHook: SvelteHook, - requestAdapter: GlobalFetch(), - responded: response => response.json() -}); +import { createAlova } from 'alova'; +import GlobalFetch from 'alova/GlobalFetch'; +import SvelteHook from 'alova/svelte'; +export const alovaInstance = createAlova({ + baseURL: 'https://jsonplaceholder.typicode.com', + statesHook: SvelteHook, + requestAdapter: GlobalFetch(), + responded: response => response.json() +}); diff --git a/codesandbox@3/00-create-alova/vueComposition.js b/codesandbox@3/00-create-alova/vueComposition.js index 97da70a23..3562ce056 100644 --- a/codesandbox@3/00-create-alova/vueComposition.js +++ b/codesandbox@3/00-create-alova/vueComposition.js @@ -1,9 +1,9 @@ -import { createAlova } from 'alova'; -import GlobalFetch from 'alova/GlobalFetch'; -import VueHook from 'alova/vue'; -export const alovaInstance = createAlova({ - baseURL: 'https://jsonplaceholder.typicode.com', - statesHook: VueHook, - requestAdapter: GlobalFetch(), - responded: response => response.json() -}); +import { createAlova } from 'alova'; +import GlobalFetch from 'alova/GlobalFetch'; +import VueHook from 'alova/vue'; +export const alovaInstance = createAlova({ + baseURL: 'https://jsonplaceholder.typicode.com', + statesHook: VueHook, + requestAdapter: GlobalFetch(), + responded: response => response.json() +}); diff --git a/codesandbox@3/00-create-alova/vueOptions.js b/codesandbox@3/00-create-alova/vueOptions.js index f9800bffe..7b0ef301f 100644 --- a/codesandbox@3/00-create-alova/vueOptions.js +++ b/codesandbox@3/00-create-alova/vueOptions.js @@ -1,9 +1,9 @@ -import { VueOptionsHook } from '@alova/vue-options'; -import { createAlova } from 'alova'; -import GlobalFetch from 'alova/GlobalFetch'; -export const alovaInstance = createAlova({ - baseURL: 'https://jsonplaceholder.typicode.com', - statesHook: VueOptionsHook, - requestAdapter: GlobalFetch(), - responded: response => response.json() -}); +import { VueOptionsHook } from '@alova/vue-options'; +import { createAlova } from 'alova'; +import GlobalFetch from 'alova/GlobalFetch'; +export const alovaInstance = createAlova({ + baseURL: 'https://jsonplaceholder.typicode.com', + statesHook: VueOptionsHook, + requestAdapter: GlobalFetch(), + responded: response => response.json() +}); diff --git a/codesandbox@3/01-getting-started/07-combine-framework/vueComposition-useRequest.zh.vue b/codesandbox@3/01-getting-started/07-combine-framework/vueComposition-useRequest.zh.vue index 3b995c5d1..e27b24a93 100644 --- a/codesandbox@3/01-getting-started/07-combine-framework/vueComposition-useRequest.zh.vue +++ b/codesandbox@3/01-getting-started/07-combine-framework/vueComposition-useRequest.zh.vue @@ -37,5 +37,5 @@ const handleUpdate = () => { // 也可以直接修改data值 // data.value = { title: 'new title' }; -} +}; diff --git a/codesandbox@3/02-client/02-use-watcher/react-search.en.jsx b/codesandbox@3/02-client/02-use-watcher/react-search.en.jsx index 3ac6378a8..5fa87e4ee 100644 --- a/codesandbox@3/02-client/02-use-watcher/react-search.en.jsx +++ b/codesandbox@3/02-client/02-use-watcher/react-search.en.jsx @@ -1,45 +1,45 @@ -import { useWatcher } from 'alova'; -import { useState } from 'react'; -import { alovaInstance } from './api'; - -//Create method instance -const filterTodoList = userId => { - return alovaInstance.Get(`/users/${userId}/todos`); -}; - -const App = () => { - const [userId, setUserId] = useState(1); - const { loading, data = [] } = useWatcher( - // Must be set to a function that returns a method instance - () => filterTodoList(userId), - - // The monitored status array, these status changes will trigger a request - [userId] - ); - - return ( - <> - - - {/* Render the filtered todo list */} - {loading ?
Loading...
: null} - {!loading ? ( -
    - {data.map(todo => ( -
  • - {todo.completed ? '(Completed)' : ''} - {todo.title} -
  • - ))} -
- ) : null} - - ); -}; -export default App; +import { useWatcher } from 'alova'; +import { useState } from 'react'; +import { alovaInstance } from './api'; + +//Create method instance +const filterTodoList = userId => { + return alovaInstance.Get(`/users/${userId}/todos`); +}; + +const App = () => { + const [userId, setUserId] = useState(1); + const { loading, data = [] } = useWatcher( + // Must be set to a function that returns a method instance + () => filterTodoList(userId), + + // The monitored status array, these status changes will trigger a request + [userId] + ); + + return ( + <> + + + {/* Render the filtered todo list */} + {loading ?
Loading...
: null} + {!loading ? ( +
    + {data.map(todo => ( +
  • + {todo.completed ? '(Completed)' : ''} + {todo.title} +
  • + ))} +
+ ) : null} + + ); +}; +export default App; diff --git a/codesandbox@3/02-client/02-use-watcher/react-search.zh.jsx b/codesandbox@3/02-client/02-use-watcher/react-search.zh.jsx index 6e0ca9a25..3b78e2fde 100644 --- a/codesandbox@3/02-client/02-use-watcher/react-search.zh.jsx +++ b/codesandbox@3/02-client/02-use-watcher/react-search.zh.jsx @@ -1,49 +1,49 @@ -import { useWatcher } from 'alova'; -import { useState } from 'react'; -import { alovaInstance } from './api'; - -// 创建method实例 -const filterTodoList = userId => { - return alovaInstance.Get(`/users/${userId}/todos`); -}; - -const App = () => { - const [userId, setUserId] = useState(1); - const { - loading, - data = [], - error - } = useWatcher( - // 必须设置为返回method实例的函数 - () => filterTodoList(userId), - - // 被监听的状态数组,这些状态变化将会触发一次请求 - [userId] - ); - - return ( - <> - - - {/* 渲染筛选后的todo列表 */} - {loading ?
Loading...
: null} - {!loading ? ( -
    - {data.map(todo => ( -
  • - {todo.completed ? '(Completed)' : ''} - {todo.title} -
  • - ))} -
- ) : null} - - ); -}; -export default App; +import { useWatcher } from 'alova'; +import { useState } from 'react'; +import { alovaInstance } from './api'; + +// 创建method实例 +const filterTodoList = userId => { + return alovaInstance.Get(`/users/${userId}/todos`); +}; + +const App = () => { + const [userId, setUserId] = useState(1); + const { + loading, + data = [], + error + } = useWatcher( + // 必须设置为返回method实例的函数 + () => filterTodoList(userId), + + // 被监听的状态数组,这些状态变化将会触发一次请求 + [userId] + ); + + return ( + <> + + + {/* 渲染筛选后的todo列表 */} + {loading ?
Loading...
: null} + {!loading ? ( +
    + {data.map(todo => ( +
  • + {todo.completed ? '(Completed)' : ''} + {todo.title} +
  • + ))} +
+ ) : null} + + ); +}; +export default App; diff --git a/codesandbox@3/02-client/02-use-watcher/vueComposition-search.en.vue b/codesandbox@3/02-client/02-use-watcher/vueComposition-search.en.vue index ff0f42357..000bd7487 100644 --- a/codesandbox@3/02-client/02-use-watcher/vueComposition-search.en.vue +++ b/codesandbox@3/02-client/02-use-watcher/vueComposition-search.en.vue @@ -1,37 +1,36 @@ - - - \ No newline at end of file + + + diff --git a/codesandbox@3/02-client/02-use-watcher/vueComposition-search.zh.vue b/codesandbox@3/02-client/02-use-watcher/vueComposition-search.zh.vue index a85d907a4..b7a3729a9 100644 --- a/codesandbox@3/02-client/02-use-watcher/vueComposition-search.zh.vue +++ b/codesandbox@3/02-client/02-use-watcher/vueComposition-search.zh.vue @@ -1,36 +1,36 @@ - - - \ No newline at end of file + + + diff --git a/codesandbox@3/02-client/02-use-watcher/vueOptions-search.en.vue b/codesandbox@3/02-client/02-use-watcher/vueOptions-search.en.vue index 86931b54c..46f14732a 100644 --- a/codesandbox@3/02-client/02-use-watcher/vueOptions-search.en.vue +++ b/codesandbox@3/02-client/02-use-watcher/vueOptions-search.en.vue @@ -1,44 +1,47 @@ - - - \ No newline at end of file + + + diff --git a/codesandbox@3/02-client/02-use-watcher/vueOptions-search.zh.vue b/codesandbox@3/02-client/02-use-watcher/vueOptions-search.zh.vue index 1af46de14..562330f19 100644 --- a/codesandbox@3/02-client/02-use-watcher/vueOptions-search.zh.vue +++ b/codesandbox@3/02-client/02-use-watcher/vueOptions-search.zh.vue @@ -1,44 +1,47 @@ - - - \ No newline at end of file + + + diff --git a/docs/about/_category_.json b/docs/about/_category_.json index 07d61bd10..c562b25fb 100644 --- a/docs/about/_category_.json +++ b/docs/about/_category_.json @@ -1,6 +1,6 @@ -{ - "label": "Others", - "link": { - "type": "generated-index" - } -} +{ + "label": "Others", + "link": { + "type": "generated-index" + } +} diff --git a/docs/api/01-alova.md b/docs/api/01-alova.md index a23583020..5ab9dc7fc 100644 --- a/docs/api/01-alova.md +++ b/docs/api/01-alova.md @@ -22,7 +22,7 @@ function createAlova(options?: AlovaOptions): Alova; | statesHook | object | State management hook, optional, [View details](/next/tutorial/getting-started/basic/combine-framework) | | requestAdapter | object | Request adapter, required, [View details](/next/tutorial/advanced/custom/http-adapter) | | timeout | number | Timeout time, no timeout by default, [View details](/next/tutorial/getting-started/basic/alova) | -| localCache | object | Local cache configuration, default GET has 5000ms cache, [View details](/next/tutorial/cache/mode) | +| cacheFor | object | Local cache configuration, default GET has 5000ms cache, [View details](/next/tutorial/cache/mode) | | storageAdapter | object | Local storage adapter, default is `localStorage`, [View details](/next/tutorial/advanced/custom/storage-adapter) | | beforeRequest | function | Before request hook, [View details](/next/tutorial/getting-started/basic/global-interceptor) | | responded | object \| function | Request response hook, [View details](/next/tutorial/getting-started/basic/global-interceptor) | @@ -67,7 +67,7 @@ interface AlovaOptions { requestAdapter: AlovaRequestAdapter; baseURL?: string; timeout?: number; - localCache?: GlobalLocalCacheConfig; + cacheFor?: GlobalcacheForConfig; storageAdapter?: AlovaStorageAdapter; beforeRequest?: Function; responded?: Function | ResponsedHandlerRecord; @@ -108,16 +108,16 @@ interface Alova { 1. url: request address 2. config: configuration parameters -| Parameter name | Type | Description | -| -------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| headers | object | Request headers, [View details](/next/tutorial/getting-started/basic/method) | -| params | object | Request parameters, [View details](/next/tutorial/getting-started/basic/method) | -| name | string | method object name, in [updateState](/next/tutorial/client/in-depth/update-across-components), [invalidateCache](/next/tutorial/cache/manually-invalidate), [setCache](/next/tutorial/cache/set-and-query), and [fetch function](/next/tutorial/client/strategy/use-fetcher), you can obtain the corresponding method instance by name or wildcard | -| timeout | number | Request timeout, [View details](/next/tutorial/getting-started/basic/method) | -| localCache | LocalCacheConfig | Response cache time, [View details](/next/tutorial/cache/mode) | -| hitSource | string | Hit the source method instance. When the source method instance request is successful, the cache of the current method instance will be invalidated. [View details](/next/tutorial/cache/auto-invalidate) | -| transformData | function | Transform response data, [View details](/next/tutorial/getting-started/basic/method) | -| shareRequest | boolean | Request-level sharing request switch, [View details](/next/tutorial/getting-started/basic/method) | +| Parameter name | Type | Description | +| -------------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| headers | object | Request headers, [View details](/next/tutorial/getting-started/basic/method) | +| params | object | Request parameters, [View details](/next/tutorial/getting-started/basic/method) | +| name | string | method object name, in [updateState](/next/tutorial/client/in-depth/update-across-components), [invalidateCache](/next/tutorial/cache/manually-invalidate), [setCache](/next/tutorial/cache/set-and-query), and [fetch function](/next/tutorial/client/strategy/use-fetcher), you can obtain the corresponding method instance by name or wildcard | +| timeout | number | Request timeout, [View details](/next/tutorial/getting-started/basic/method) | +| cacheFor | cacheForConfig | Response cache time, [View details](/next/tutorial/cache/mode) | +| hitSource | string | Hit the source method instance. When the source method instance request is successful, the cache of the current method instance will be invalidated. [View details](/next/tutorial/cache/auto-invalidate) | +| transform | function | Transform response data, [View details](/next/tutorial/getting-started/basic/method) | +| shareRequest | boolean | Request-level sharing request switch, [View details](/next/tutorial/getting-started/basic/method) | > In addition to the configurable parameters above, other parameters supported by the request adapter are also supported. diff --git a/docs/api/_category_.json b/docs/api/_category_.json index 4b4861e39..de7065684 100644 --- a/docs/api/_category_.json +++ b/docs/api/_category_.json @@ -1,6 +1,6 @@ -{ - "label": "API", - "link": { - "type": "generated-index" - } -} +{ + "label": "API", + "link": { + "type": "generated-index" + } +} diff --git a/docs/contributing/01-overview.md b/docs/contributing/01-overview.md index 2e0356d13..e5dab7393 100644 --- a/docs/contributing/01-overview.md +++ b/docs/contributing/01-overview.md @@ -55,7 +55,7 @@ The design principles points out how it should be designed, the following is the 1. `Method` proxy design, high aggregation, platform-independent design, throughout the request, you should be able to access it in any request function, from another perspective, the information related to the request should also be placed in the method instance ; 2. Lightweight, try to keep the source code concise in coding, such as avoiding repeated code, merging variable declarations, prototype chain function encapsulation, no similar API, tree shaking, but long variable names are allowed, because it will be used when compiling will be replaced by a single letter; -3. Highly scalable design. First, the design of alova uses a large number of adapter patterns and hook functions. For example, adapters include `requestAdapter`, `storageAdapter`, etc., and hook functions include `beforeRequest`, `responded`, `transformData`, `localCache`, etc., and most of them have default behaviors, which are designed to be easy to use while retaining high scalability; second, global request parameters can be overwritten, such as `timeout`, `shareRequest`, etc., for These parameters can be set individually for special requests. +3. Highly scalable design. First, the design of alova uses a large number of adapter patterns and hook functions. For example, adapters include `requestAdapter`, `storageAdapter`, etc., and hook functions include `beforeRequest`, `responded`, `transform`, `cacheFor`, etc., and most of them have default behaviors, which are designed to be easy to use while retaining high scalability; second, global request parameters can be overwritten, such as `timeout`, `shareRequest`, etc., for These parameters can be set individually for special requests. 4. The api design is universal. First, it means that the function of this api has a high level of abstraction, rather than a specific business. Second, the api design is scalable to adapt to the needs of the api iteration > The api universal design is only applicable to the alova library. If you are conceiving a request strategy, you can design it according to the specific business. diff --git a/docs/contributing/_category_.json b/docs/contributing/_category_.json index d3bf2a858..66f0e1824 100644 --- a/docs/contributing/_category_.json +++ b/docs/contributing/_category_.json @@ -1,6 +1,6 @@ -{ - "label": "Contributing", - "link": { - "type": "generated-index" - } -} +{ + "label": "Contributing", + "link": { + "type": "generated-index" + } +} diff --git a/docs/release-notes-v3.md b/docs/release-notes-v3.md index a9b62b4fc..c7eefee76 100644 --- a/docs/release-notes-v3.md +++ b/docs/release-notes-v3.md @@ -268,6 +268,10 @@ useWatcher(() => method, [xxx], { These two parameters are no longer needed in method. They will be changed to automatically determine whether to enable or not. Getter will be used to determine whether uploading and downloading are used externally. +### Deprecate `placeholder` mode of method + +We have mentioned in "redesign cache mode", `placeholder` mode has been replaced by other ways. + ### Remove deprecated responsed It is recommended to use the `responded` field uniformly in createAlova. @@ -502,7 +506,7 @@ useRequest(Getter, { ## Field name modification -### `method.key` is simplified to `method.key` +### `method.__key__` is simplified to `method.key` ```js // 2.x diff --git a/docs/resource/03-framework/01-vue-composition.md b/docs/resource/03-framework/01-vue-composition.md new file mode 100644 index 000000000..dd8b5aed5 --- /dev/null +++ b/docs/resource/03-framework/01-vue-composition.md @@ -0,0 +1,27 @@ +--- +title: vue composition +--- + +Support request strategies in vue composition through `alova/vue`. + +```js +import { createAlova } from 'alova'; +import VueHook from 'alova/vue'; + +const alovaInstance = createAlova({ + // ... + statesHook: VueHook +}); +``` + +If you use composition syntax in vue2, please use `alova/vue-demi`. + +```js +import { createAlova } from 'alova'; +import VueHook from 'alova/vue-demi'; + +const alovaInstance = createAlova({ + // ... + statesHook: VueHook +}); +``` diff --git a/docs/resource/03-framework/01-vue-options.md b/docs/resource/03-framework/01-vue-options.md deleted file mode 100644 index 63033adba..000000000 --- a/docs/resource/03-framework/01-vue-options.md +++ /dev/null @@ -1,332 +0,0 @@ ---- -title: vue2/3 options ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -Usually, use hook can only be used in vue's setup, but through the helper function provided by `@alova/vue-options`, you can also use alova's use hook in vue's options, which is perfectly compatible with almost all functions of alova. - -> Available in both vue2 and vue3. - -[![npm](https://img.shields.io/npm/v/@alova/vue-options)](https://www.npmjs.com/package/@alova/vue-options) -[![build](https://github.com/alovajs/vue-options/actions/workflows/release.yml/badge.svg?branch=main)](https://github.com/alovajs/vue-options/actions/workflows/release.yml) -[![coverage status](https://coveralls.io/repos/github/alovajs/vue-options/badge.svg?branch=main)](https://coveralls.io/github/alovajs/vue-options?branch=main) -![typescript](https://badgen.net/badge/icon/typescript?icon=typescript&label) -![license](https://img.shields.io/badge/license-MIT-blue.svg) - -## Install - - - - -```bash -npm install alova @alova/vue-options --save -``` - - - - -```bash -yarn add alova @alova/vue-options -``` - - - - -:::info alova requirements - -alova version >= 2.13.2 - -::: - -## Usage - -### Map hook status and functions to vue instances - -First use `vueOptionHook` to create an alova instance. - -```javascript -import { createAlova, Method } from 'alova'; -import adapterFetch from 'alova/fetch'; -import { VueOptionsHook } from '@alova/vue-options'; - -// api.js -const alovaInst = createAlova({ - baseURL: 'http://example.com', - statesHook: VueOptionsHook, - requestAdapter: adapterFetch(), - responded: response => response.json() -}); - -/** @type {() => Method} */ -export const getData = () => alovaInst.Get('/todolist'); -``` - -Then use `mapAlovaHook` to map the return value set of use hook to the component instance. The following is how to access the reactive state and operation functions: - -1. You can access responsive status such as `loading/data/error` through the key of the collection, such as `this.key.loading`, `this.key.data`. -2. You can access the operation function through the key of the collection plus the function name, and use `$` to splice it, such as `this.key$send`, `this.key$onSuccess`. - -Below is a complete example. - -```html - - - -``` - -### Computed properties - -If you need to define a computed property that depends on hook-related request status, just write it as usual. - -```javascript -export default { - computed: { - todoRequestLoading() { - return this.todoRequest.loading; - }, - todoRequestData() { - return this.todoRequest.data; - } - } -}; -``` - -### Watch hook status changes - -Due to the limitations of vue2, all hook states are mounted on an object named `alovaHook$`, so you need to add the `alovaHook$` prefix when listening. - -```javascript -export default { - watch: { - // ❌Unable to watch - 'todoRequest.loading'(newVal, oldVal) { - // ... - }, - // ✅watching is work - 'alovaHook$.todoRequest.loading'(newVal, oldVal) { - // ... - } - } -}; -``` - -But this is a bit troublesome, so a `mapWatcher` helper function is provided, which can not only automatically add prefixes, nested watching, but also batch watching. - -#### Define single watch handler - -```javascript -export default { - watch: mapWatcher({ - // Usage 1 - 'todoRequest.loading'(newVal, oldVal) {}, - - // Usage 2 - todoRequest: { - loading(newVal, oldVal) {}, - data(newVal, oldVal) {} - } - }) -}; -``` - -Watching object is also supported. - -```javascript -export default { - watch: mapWatcher({ - todoRequest: { - data: { - handler(newVal, oldVal) {}, - deep: true - } - } - }) -}; -``` - -#### Batch define watch handlers - -Multiple watching keys are separated by `,`. - -```javascript -export default { - watch: mapWatcher({ - // Usage 1 - 'todoRequest1.data, todoRequest2.data'(newVal, oldVal) {}, - - // Usage 2 - 'todoRequest1, todoRequest2': { - loading(newVal, oldVal) {}, - data(newVal, oldVal) {} - }, - - // Usage 3 - todoRequest1: { - 'loading, data'(newVal, oldVal) {} - }, - - // Usage 4 - 'todoRequest1, todoRequest2': { - 'loading, data'(newVal, oldVal) {} - } - }) -}; -``` - -> Batch watching also supports watching object. - -## Function description - -### mapAlovaHook - -`mapAlovaHook` is used to map the state and function collection returned by alova's use hook to the vue component instance through mixins. It receives a callback function and returns the return value collection of use hook. - -It is worth noting that the callback function will be executed in the `created` phase, and you can access the vue component instance in the following way. - -```javascript -// 1. Access the component instance through this. Note that the callback function cannot be an arrow function. -mapAlovaHook(function () { - console.log(this); - return { - todoRequest: useRequest(getData) - }; -}); - -// ======================= -// 2. Access the component instance through function parameters. In this case, arrow functions can be used -mapAlovaHook(vm => { - console.log(vm); - return { - todoRequest: useRequest(getData) - }; -}); -``` - -### mapWatcher - -`mapWatcher` is an helper function used to quickly define the watching handlers of hook states. It receives an object whose key is the key of the hook state or a string representation of the nested value, and whose value is the watching handler or watching object. - -```javascript -mapWatcher({ - 'todoRequest.loading'(newVal, oldVal) { - //... - }, - todoRequest: { - data(newVal, oldVal) { - //... - } - }, - todoRequest: { - 'loading, data'(newVal, oldVal) { - //... - } - } -} -``` - -In addition to supporting watching assistance for alova useHook, `mapWatcher` can also be used to batch set watching handlers of custom states. - -```javascript -export default { - data() { - state1: '', - state2: 0 - }, - - // pass false at the second parameter to watch the custom states - watch: mapWatcher({ - 'state1, state2'(newVal, oldVal) { - //... - } - }, false) -} -``` - -## Type support - -### Automatic inference - -`@alova/vue-options` provides complete ts type support. Whether using typescript or not, the mapped value type will be automatically inferred, for example: - -```javascript -this.todoRequest.loading; // boolean -this.todoRequest.error; // Error | undefined -this.todoRequest.data; // any -this.todoRequest$send; // (...args: any[]) => Promise -this.todoRequest$onSuccess; // (handler: SuccessHandler) => void -this.todoRequest$onError; // (handler: ErrorHandler) => void -this.todoRequest$onComplete; // (handler: CompleteHandler) => void -// ... -``` - -### Add types for response data - -Except for `this.todoRequest.data`, all other values have the correct type, so how to set the type for `data` too? In fact, it is the same as alova used in other environments. - -**javascript** - -In javascript, you can use type annotations to add types. The first two generic parameters of Method are `unknown`, and the third generic parameter is the type of response data. - -```javascript -import { Method } from 'alova'; - -/** @type {() => Method} */ -export const getData = () => alovaInst.Get('/todolist'); -``` - -**typescript** - -To add response data type in typescript, please read [alova documentation typescript chapter](/next/tutorial/advanced/in-depth/typescript) - -## limit - -1. [Manage extra states](/next/tutorial/client/in-depth/manage-extra-states) is not supported yet. -2. Currently, only alova’s three core useHooks of `useRequest/useWatcher/useFetcher` are supported, as well as the encapsulation based on the core useHook in your own project. [@alova/scene](https://github.com/alovajs/scene) is not supported yet. extension useHook. diff --git a/docs/resource/03-framework/02-react.md b/docs/resource/03-framework/02-react.md new file mode 100644 index 000000000..4e78e0955 --- /dev/null +++ b/docs/resource/03-framework/02-react.md @@ -0,0 +1,15 @@ +--- +title: react +--- + +Support request strategies in react through `alova/react`. + +```js +import { createAlova } from 'alova'; +import ReactHook from 'alova/react'; + +const alovaInstance = createAlova({ + // ... + statesHook: ReactHook +}); +``` diff --git a/docs/resource/03-framework/03-svelte.md b/docs/resource/03-framework/03-svelte.md new file mode 100644 index 000000000..de775795f --- /dev/null +++ b/docs/resource/03-framework/03-svelte.md @@ -0,0 +1,15 @@ +--- +title: svelte +--- + +Support request strategies in svelte through `alova/svelte`. + +```js +import { createAlova } from 'alova'; +import SvelteHook from 'alova/svelte'; + +const alovaInstance = createAlova({ + // ... + statesHook: SvelteHook +}); +``` diff --git a/docs/resource/03-framework/02-solid.md b/docs/resource/03-framework/04-solid.md similarity index 100% rename from docs/resource/03-framework/02-solid.md rename to docs/resource/03-framework/04-solid.md diff --git a/docs/resource/03-framework/03-angular.md b/docs/resource/03-framework/05-angular.md similarity index 100% rename from docs/resource/03-framework/03-angular.md rename to docs/resource/03-framework/05-angular.md diff --git a/docs/resource/03-framework/05-preact.md b/docs/resource/03-framework/06-preact.md similarity index 100% rename from docs/resource/03-framework/05-preact.md rename to docs/resource/03-framework/06-preact.md diff --git a/docs/resource/03-framework/07-vue-options.md b/docs/resource/03-framework/07-vue-options.md new file mode 100644 index 000000000..ef376061a --- /dev/null +++ b/docs/resource/03-framework/07-vue-options.md @@ -0,0 +1,222 @@ +--- +title: vue options +--- + +import Tabs from '@theme/Tabs'; + +import TabItem from '@theme/TabItem'; + +Usually, use hook can only be used in vue's setup, but through the auxiliary function provided by `@alova/vue-options`, you can also use alova's use hook in vue's options, which is perfectly compatible with almost all functions of alova. You can use all request strategies of `alova/client` under options. + +> It can be used in both vue2 and vue3. + +[![npm](https://img.shields.io/npm/v/@alova/vue-options)](https://www.npmjs.com/package/@alova/vue-options) +[![build](https://github.com/alovajs/vue-options/actions/workflows/release.yml/badge.svg?branch=main)](https://github.com/alovajs/vue-options/actions/workflows/release.yml) +[![coverage status](https://coveralls.io/repos/github/alovajs/vue-options/badge.svg?branch=main)](https://coveralls.io/github/alovajs/vue-options?branch=main) +![typescript](https://badgen.net/badge/icon/typescript?icon=typescript&label) +![license](https://img.shields.io/badge/license-MIT-blue.svg) + +## Installation + + + + +```bash +npm install alova @alova/vue-options --save +``` + + + + +```bash +yarn add alova @alova/vue-options +``` + + + + +:::info Version requirements + +alova version >= 3.0.0 + +vue version >= 2.17.0 + +::: + +## Usage + +### Map hook status and functions to vue instance + +First use `vueOptionHook` to create an alova instance. + +```javascript +import { createAlova, Method } from 'alova'; +import adapterFetch from 'alova/fetch'; +import { VueOptionsHook } from '@alova/vue-options'; + +// api.js +const alovaInst = createAlova({ + baseURL: 'http://example.com', + statesHook: VueOptionsHook, + requestAdapter: adapterFetch(), + responded: response => response.json() +}); + +/** @type {() => Method} */ +export const getData = () => alovaInst.Get('/todolist'); +``` + +Use `mapAlovaHook` to map the return value collection of use hook to the component instance. Here is how to access the responsive state and operation function: + +1. You can use the key of the collection To access responsive states such as `loading/data/error`, such as `this.key.loading`, `this.key.data`. +2. You can access the operation function by adding the function name to the collection key and concatenating it with `$`, such as `this.key$send`, `this.key$onSuccess`. + +The following is a complete example. + +```html + + + +``` + +### Computed properties + +If you need to define a computed property that depends on the request status related to the hook, just write it as usual. + +```javascript +export default { + computed: { + todoRequestLoading() { + return this.todoRequest.loading; + }, + todoRequestData() { + return this.todoRequest.data; + } + } +}; +``` + +### Listening to hook status changes + +The usage of monitoring status is also consistent with vue. + +```javascript +export default { + watch: { + 'todoRequest.loading'(newVal, oldVal) { + // ... + } + } +}; +``` + +## Function description + +### mapAlovaHook + +`mapAlovaHook` is used to map the state and function set returned by alova's use hook to the vue component instance through mixins. It receives a callback function and returns the return value collection of the use hook. + +It is worth noting that the callback function will be executed in the `created` phase, and you can access the vue component instance in the following way. + +```javascript +// 1. Access the component instance through this. Note that the callback function cannot be an arrow function +mapAlovaHook(function () { + console.log(this); + return { + todoRequest: useRequest(getData) + }; +}); + +// ======================= +// 2. Access the component instance through function parameters. You can use arrow functions at this time +mapAlovaHook(vm => { + console.log(vm); + return { + todoRequest: useRequest(getData) + }; +}); +``` + +## Type support + +### Automatic inference + +`@alova/vue-options` provides complete ts type support. Regardless of whether typescript is used, the mapped value type will be automatically inferred, for example: + +```javascript +this.todoRequest.loading; // boolean +this.todoRequest.error; // Error | undefined +this.todoRequest.data; // any +this.todoRequest$send; // (...args: any[]) => Promise +this.todoRequest$onSuccess; // (handler: SuccessHandler) => void +this.todoRequest$onError; // (handler: ErrorHandler) => void +this.todoRequest$onComplete; // (handler: CompleteHandler) => void +// ... +``` + +### Add types to response data + +Except for `this.todoRequest.data`, other values ​​have the correct type, so how to set the type for `data`? In fact, it is the same as alova in other environments. + +**javascript** + +In javascript, you can use type annotations to add types. The first two generic parameters of Method are `unknown`, and the third generic parameter is the type of the response data + +```javascript +import { Method } from 'alova'; + +/** @type {() => Method} */ +export const getData = () => alovaInst.Get('/todolist'); +``` + +**typescript**Add response data types in typescript, please read the [alova documentation typescript chapter](/next/tutorial/advanced/in-depth/typescript) + +## Limitations + +1. [Manage extra states](/next/tutorial/client/in-depth/manage-extra-states) is not supported yet. diff --git a/docs/resource/03-framework/06-qwik.md b/docs/resource/03-framework/08-qwik.md similarity index 100% rename from docs/resource/03-framework/06-qwik.md rename to docs/resource/03-framework/08-qwik.md diff --git a/docs/resource/03-framework/04-native-mp.md b/docs/resource/03-framework/09-native-mp.md similarity index 100% rename from docs/resource/03-framework/04-native-mp.md rename to docs/resource/03-framework/09-native-mp.md diff --git a/docs/resource/03-framework/07-lit.md b/docs/resource/03-framework/10-lit.md similarity index 100% rename from docs/resource/03-framework/07-lit.md rename to docs/resource/03-framework/10-lit.md diff --git a/docs/resource/03-framework/08-stencil.md b/docs/resource/03-framework/11-stencil.md similarity index 100% rename from docs/resource/03-framework/08-stencil.md rename to docs/resource/03-framework/11-stencil.md diff --git a/docs/resource/03-framework/README.md b/docs/resource/03-framework/README.md deleted file mode 100644 index 1609bc2fc..000000000 --- a/docs/resource/03-framework/README.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: overview ---- - -sdfasfasdf diff --git a/docs/resource/03-framework/_category_.json b/docs/resource/03-framework/_category_.json index 6edd9139c..fb1ff2abe 100644 --- a/docs/resource/03-framework/_category_.json +++ b/docs/resource/03-framework/_category_.json @@ -1,6 +1,6 @@ -{ - "label": "Framework", - "link": { - "type": "generated-index" - } -} +{ + "label": "Framework", + "link": { + "type": "generated-index" + } +} diff --git a/docs/tutorial/02-getting-started/03-basic/03-method.md b/docs/tutorial/02-getting-started/03-basic/03-method.md index aeec4f13b..543bfb09d 100644 --- a/docs/tutorial/02-getting-started/03-basic/03-method.md +++ b/docs/tutorial/02-getting-started/03-basic/03-method.md @@ -213,12 +213,12 @@ The request method, request url, request header, url parameter, and request body ### Transform response data -Sometimes we need to uniformly transform response data. We can set the `transformData` function for the method instance to transform the response data into the required structure. +Sometimes we need to uniformly transform response data. We can set the `transform` function for the method instance to transform the response data into the required structure. ```javascript alovaInstance.Get('/todo/list', { // The function accepts the response data and response header data, and requires the transformed data to be returned. - transformData(rawData, headers) { + transform(rawData, headers) { return rawData.list.map(item => { return { ...item, diff --git a/docs/tutorial/02-getting-started/03-basic/05-global-interceptor.md b/docs/tutorial/02-getting-started/03-basic/05-global-interceptor.md index 78a5b2343..ebf1ccf54 100644 --- a/docs/tutorial/02-getting-started/03-basic/05-global-interceptor.md +++ b/docs/tutorial/02-getting-started/03-basic/05-global-interceptor.md @@ -82,7 +82,7 @@ const alovaInstance = createAlova({ throw new Error(json.message); } - //The parsed response data will be passed to the transformData hook function of the method instance. These functions will be explained later. + //The parsed response data will be passed to the transform hook function of the method instance. These functions will be explained later. return json.data; }, // highlight-end diff --git a/docs/tutorial/02-getting-started/03-basic/07-combine-framework.md b/docs/tutorial/02-getting-started/03-basic/07-combine-framework.md index 2f5ab96b8..d0ca6d2bf 100644 --- a/docs/tutorial/02-getting-started/03-basic/07-combine-framework.md +++ b/docs/tutorial/02-getting-started/03-basic/07-combine-framework.md @@ -169,11 +169,11 @@ flowchart LR R1[Response successfully] --> global.onSuccess global.onSuccess --> global.onComplete global.onSuccess --> throw{Is an error thrown? }:::condition - throw -->|No| method.transformData - method.transformData --> useHook.onSuccess + throw -->|No| method.transform + method.transform --> useHook.onSuccess throw -->|Yes| useHook.onError - method.transformData --> throw2{Is an error thrown? }:::condition + method.transform --> throw2{Is an error thrown? }:::condition throw2 -->|No| useHook.onSuccess throw2 -->|Yes| useHook.onError @@ -184,20 +184,20 @@ flowchart LR global.onError --> global.onComplete global.onError --> throw4{Is an error thrown? }:::condition throw4 -->|Yes| useHook.onError - throw4 -->|No| method.transformData + throw4 -->|No| method.transform ``` When no error is thrown, the next node receives the return value of the previous node. ### Transform response data -In [method detailed explanation](/next/tutorial/getting-started/basic/method), we have already learned about `transformData`, which is also very useful when used in useHook. It allows useHook's data to receive the transformed data without transform again. +In [method detailed explanation](/next/tutorial/getting-started/basic/method), we have already learned about `transform`, which is also very useful when used in useHook. It allows useHook's data to receive the transformed data without transform again. ```javascript const todoListGetter = alovaInstance.Get('/todo/list', { // The function accepts raw data and response header objects, and requires the transformed data to be returned, which will be assigned to the data state. // Note: rawData is the data filtered by the global response interceptor (if it is set). For the configuration of the response interceptor, please refer to the [Setting the Global Response Interceptor] chapter. - transformData(rawData, headers) { + transform(rawData, headers) { return rawData.list.map(item => ({ ...item, statusText: item.done ? 'Completed' : 'In progress' @@ -222,7 +222,7 @@ type data = { :::warning note -When used in usehooks, throwing an error in `transformData` will also trigger `onError`; +When used in usehooks, throwing an error in `transform` will also trigger `onError`; ::: @@ -242,7 +242,7 @@ const { // Complete the callback binding, the callback will be called on success or failure onComplete -} = useRequest(todoListGetter); // Also applicable to useWatcher +} = useRequest(todoListGetter); onSuccess(event => { console.log('The request was successful, the response data is:', event.data); console.log('The method instance of this request is:', event.method); @@ -265,6 +265,21 @@ onComplete(event => { }); ``` +We also support the chain call of binding functions in all useHooks。 + +```js +const { data, loading, error, onSuccess, onError, onComplete } = useRequest(todoListGetter) + .onSuccess(event => { + // ... + }) + .onError(event => { + // ... + }) + .onComplete(event => { + // ... + }); +``` + :::note Hint Throwing an error in `onSuccess` will trigger `onError`. diff --git a/docs/tutorial/03-client/01-strategy/01-use-request.md b/docs/tutorial/03-client/01-strategy/01-use-request.md index 8761bd35f..133fd6778 100644 --- a/docs/tutorial/03-client/01-strategy/01-use-request.md +++ b/docs/tutorial/03-client/01-strategy/01-use-request.md @@ -33,7 +33,7 @@ const { data } = useRequest(todoListGetter, { You can also set `initialData` to a function to dynamically set the initial value. For example, if you don't want the Loading icon to be displayed every time the app is entered and want to use old data instead, you can return the old data as the initial value, which provides a better experience than Loading. ```js -const { onSuccess } = useRequest(todoListGetter, { +const { data, loading, error } = useRequest(todoListGetter, { initialData() { // Set the last response data const storedData = localStorage.getItem('placeholder-data'); @@ -42,8 +42,7 @@ const { onSuccess } = useRequest(todoListGetter, { // Also use alova's level2 storage adapter // return alovaInst.l2cache.get('placeholder-data'); } -}); -onSuccess(({ data, method }) => { +}).onSuccess(({ data, method }) => { // Save response data localStorage.setItem('placeholder-data', JSON.stringify(data)); diff --git a/docs/tutorial/03-client/01-strategy/03-use-fetcher.md b/docs/tutorial/03-client/01-strategy/03-use-fetcher.md index 201febcde..0568095cb 100644 --- a/docs/tutorial/03-client/01-strategy/03-use-fetcher.md +++ b/docs/tutorial/03-client/01-strategy/03-use-fetcher.md @@ -37,7 +37,7 @@ Let's implement a paging list to automatically preload the next page of data. Be //method instance creation function const getTodoList = currentPage => { return alovaInstance.Get('/todo/list', { - localCache: 60000, + cacheFor: 60000, params: { currentPage, pageSize: 10 @@ -60,12 +60,10 @@ Let's implement a paging list to automatically preload the next page of data. Be }); const currentPage = ref(1); - const { data, onSuccess } = useWatcher(() => getTodoList(currentPage.value), [currentPage], { + const { data } = useWatcher(() => getTodoList(currentPage.value), [currentPage], { immediate: true - }); - - // After the current page is loaded successfully, pass in the method instance of the next page to pre-fetch the data of the next page. - onSuccess(() => { + }).onSuccess(() => { + // After the current page is loaded successfully, pass in the method instance of the next page to pre-fetch the data of the next page. fetch(getTodoList(currentPage.value + 1)); }); @@ -81,7 +79,7 @@ import { useFetcher } from 'alova/client'; //method instance creation function const getTodoList = currentPage => { return alovaInstance.Get('/todo/list', { - localCache: 60000, + cacheFor: 60000, params: { currentPage, pageSize: 10 @@ -106,10 +104,8 @@ const App = () => { const [currentPage, setCurrentPage] = useState(1); const { data, onSuccess } = useWatcher(() => getTodoList(currentPage), [currentPage], { immediate: true - }); - - // After the current page is loaded successfully, pass in the method instance of the next page to pre-fetch the data of the next page. - onSuccess(() => { + }).onSuccess(() => { + // After the current page is loaded successfully, pass in the method instance of the next page to pre-fetch the data of the next page. fetch(getTodoList(currentPage + 1)); }); @@ -133,7 +129,7 @@ const App = () => { //method instance creation function const getTodoList = currentPage => { return alovaInstance.Get('/todo/list', { - localCache: 60000, + cacheFor: 60000, params: { currentPage, pageSize: 10 @@ -157,10 +153,8 @@ const App = () => { const currentPage = writable(1); const { data, onSuccess } = useWatcher(() => getTodoList($currentPage), [currentPage], { immediate: true - }); - - // After the current page is loaded successfully, pass in the method instance of the next page to pre-fetch the data of the next page. - onSuccess(() => { + }).onSuccess(() => { + // After the current page is loaded successfully, pass in the method instance of the next page to pre-fetch the data of the next page. fetch(getTodoList($currentPage + 1)); }); diff --git a/docs/tutorial/03-client/01-strategy/04-use-pagination.md b/docs/tutorial/03-client/01-strategy/04-use-pagination.md index 96200e558..e19d1da0e 100644 --- a/docs/tutorial/03-client/01-strategy/04-use-pagination.md +++ b/docs/tutorial/03-client/01-strategy/04-use-pagination.md @@ -340,7 +340,7 @@ usePagination((page, pageSize) => queryStudents(page, pageSize), { When preloading switch is turned on, the next page will not be loaded blindly. The following two conditions need to be met: -1. Preloading is based on cache of alova. The method instance used for pagination must enable chache. By default, get requests will have 5 minutes of memory cache. If it is a non-get request or the cache is turned off globally, you also need to set the cache in this Method. In the instance, set `localCache` separately to enable cache. +1. Preloading is based on cache of alova. The method instance used for pagination must enable chache. By default, get requests will have 5 minutes of memory cache. If it is a non-get request or the cache is turned off globally, you also need to set the cache in this Method. In the instance, set `cacheFor` separately to enable cache. 2. Based on the `total` and `pageSize` parameters, it is determined that there is still data on the next page. ::: diff --git a/docs/tutorial/03-client/01-strategy/README.md b/docs/tutorial/03-client/01-strategy/README.md index 579ac8890..6a9c454d0 100644 --- a/docs/tutorial/03-client/01-strategy/README.md +++ b/docs/tutorial/03-client/01-strategy/README.md @@ -14,6 +14,8 @@ All client use hooks have the following in common: 3. For performance under react, all operation functions such as `send`, `update`, `abort`, etc. are wrapped with `useCallback`. +4. All binding functions starting with `on` can be chained. + ## Table of contents diff --git a/docs/tutorial/05-cache/06-controlled-cache.md b/docs/tutorial/05-cache/06-controlled-cache.md index 9b183b025..4d174c708 100644 --- a/docs/tutorial/05-cache/06-controlled-cache.md +++ b/docs/tutorial/05-cache/06-controlled-cache.md @@ -23,9 +23,9 @@ const getFile = fileName => You can also return `undefined` or no data in `cacheFor` and continue to send the request, which is useful in the case of cache misses when customizing cache management. -## Use transformData to set cache +## Use transform to set cache -Because the `transformData` function has the following two features: +Because the `transform` function has the following two features: - It is triggered only when responding, and will not be triggered when hitting the response cache; @@ -36,7 +36,7 @@ Therefore, you can also use it to save custom caches. For example, in the scenar ```javascript const fileGetter = alovaInstance.Get('/file/file_name', { // Use IndexedDB to cache files - async transformData(fileBlob) { + async transform(fileBlob) { await new Promise((resolve, reject) => { const tx = db.transaction(['files'], 'readwrite'); const putRequest = tx.objectStore('files').put({ diff --git a/docs/tutorial/06-advanced/01-in-depth/05-custom-method-key.md b/docs/tutorial/06-advanced/01-in-depth/05-custom-method-key.md index 194412be7..98425283b 100644 --- a/docs/tutorial/06-advanced/01-in-depth/05-custom-method-key.md +++ b/docs/tutorial/06-advanced/01-in-depth/05-custom-method-key.md @@ -21,9 +21,9 @@ But sometimes you want to change it so that the above three situations can be re ## Customize method instance key ```javascript -// Method key is generated when it is created, you can customize it through __key__ +// Method key is generated when it is created, you can customize it through key const methodInst = alovaInstance.Get('/api/user', {}); -methodInst.__key__ = 'my-custom-method-key'; +methodInst.key = 'my-custom-method-key'; ``` ## Customize all method instance keys diff --git a/docs/tutorial/06-advanced/01-in-depth/10-typescript.md b/docs/tutorial/06-advanced/01-in-depth/10-typescript.md index d93dd85f0..28e4e4753 100644 --- a/docs/tutorial/06-advanced/01-in-depth/10-typescript.md +++ b/docs/tutorial/06-advanced/01-in-depth/10-typescript.md @@ -69,7 +69,7 @@ When you specify a type for a data interface, you need to divide it into two sit ### Case 1 -When the response data does not need to be converted by calling `transformData`, the type can be specified directly through generics. +When the response data does not need to be converted by calling `transform`, the type can be specified directly through generics. ```typescript interface Todo { @@ -86,7 +86,7 @@ const { data } = useRequest(Get); ### Case 2 -When the response data needs to be converted by calling `transformData`, the type needs to be specified in the conversion function parameter, and then its return value type will be used as the response data type. +When the response data needs to be converted by calling `transform`, the type needs to be specified in the conversion function parameter, and then its return value type will be used as the response data type. ```typescript interface Todo { @@ -96,7 +96,7 @@ interface Todo { } const Get = alovaInstance.Get('/todo/list', { //Write the type into the data parameter, and the headers will be automatically inferred, so you don’t need to specify the type. - transformData(data: Todo[], headers) { + transform(data: Todo[], headers) { return data.map(item => ({ ...item, status: item.done ? 'Completed' : 'Not completed' diff --git a/docs/tutorial/06-advanced/02-custom/01-http-adapter.md b/docs/tutorial/06-advanced/02-custom/01-http-adapter.md index 17e507224..7490a9216 100644 --- a/docs/tutorial/06-advanced/02-custom/01-http-adapter.md +++ b/docs/tutorial/06-advanced/02-custom/01-http-adapter.md @@ -79,7 +79,7 @@ An asynchronous function, the function returns a response value, which will be p **headers (required)** -An asynchronous function, the response header object returned by the function will be passed to the transformData of the Method instance Conversion hook function; +An asynchronous function, the response header object returned by the function will be passed to the transform of the Method instance Conversion hook function; **abort (required)** diff --git a/docs/tutorial/07-project/01-best-practice/03-manage-cache-by-indexeddb.md b/docs/tutorial/07-project/01-best-practice/03-manage-cache-by-indexeddb.md index f3099ba28..d28af9d03 100644 --- a/docs/tutorial/07-project/01-best-practice/03-manage-cache-by-indexeddb.md +++ b/docs/tutorial/07-project/01-best-practice/03-manage-cache-by-indexeddb.md @@ -68,7 +68,7 @@ export const getImageFromCache = async fileName => { ## save data -When saving data, we can save the cache in the `transformData` of the method, because `transformData` will only be triggered when the network request responds, but will not be triggered when the cache is hit. In the sample code, convert the image blob instance to base64 data, cache and return this base64 data. +When saving data, we can save the cache in the `transform` of the method, because `transform` will only be triggered when the network request responds, but will not be triggered when the cache is hit. In the sample code, convert the image blob instance to base64 data, cache and return this base64 data. ```javascript-api.js import { addImage2Cache } from './db'; @@ -76,7 +76,7 @@ import { addImage2Cache } from './db'; export const image = fileName => alovaInst.Get(`/image/${fileName}`, { // highlight-start - async transformData(imgBlob) { + async transform(imgBlob) { // Asynchronously convert the blob to base64 const reader = new FileReader(); reader.readAsDataURL(imgBlob); @@ -96,19 +96,19 @@ export const image = fileName => ## retrieve data -Specify `localCache` of this method instance as an asynchronous function to change the cache into a controlled state, match the cache in IndexedDB in this function, and return it if it matches, otherwise return `undefined` and continue to initiate a request to obtain data. +Specify `cacheFor` of this method instance as an asynchronous function to change the cache into a controlled state, match the cache in IndexedDB in this function, and return it if it matches, otherwise return `undefined` and continue to initiate a request to obtain data. ```javascript title=api.js import { getImageFromCache } from './db'; export const image = fileName => alovaInst.Get(`/image/${fileName}`, { - async transformData(imgBlob) { + async transform(imgBlob) { //... }, // highlight-start - async localCache() { + async cacheFor() { // get cache const cache = await getImageFromCache(fileName); return cache && cache.data; @@ -117,6 +117,6 @@ export const image = fileName => }); ``` -In this way, a basic custom cache management is basically completed. You can also save the expiration time of the cache, and judge whether it has expired when the cache is matched in `localCache`, so as to realize the cache expiration function. +In this way, a basic custom cache management is basically completed. You can also save the expiration time of the cache, and judge whether it has expired when the cache is matched in `cacheFor`, so as to realize the cache expiration function. IndexedDB is just one example of managing caches asynchronously, you can also connect to your cache servers to manage them. diff --git a/docsearch.json b/docsearch.json index d442437f9..da06f835e 100644 --- a/docsearch.json +++ b/docsearch.json @@ -1,30 +1,37 @@ -{ - "index_name": "alova_website", - "start_urls": ["https://alova.js.org/"], - "sitemap_urls": ["https://alova.js.org/sitemap.xml"], - "sitemap_alternate_links": true, - "stop_urls": ["/tests"], - "selectors": { - "lvl0": { - "selector": "(//ul[contains(@class,'menu__list')]//a[contains(@class, 'menu__link menu__link--sublist menu__link--active')]/text() | //nav[contains(@class, 'navbar')]//a[contains(@class, 'navbar__link--active')]/text())[last()]", - "type": "xpath", - "global": true, - "default_value": "Documentation" - }, - "lvl1": "header h1", - "lvl2": "article h2", - "lvl3": "article h3", - "lvl4": "article h4", - "lvl5": "article h5, article td:first-child", - "lvl6": "article h6", - "text": "article p, article li, article td:last-child" - }, - "strip_chars": " .,;:#", - "custom_settings": { - "separatorsToIndex": "_", - "attributesForFaceting": ["language", "version", "type", "docusaurus_tag"], - "attributesToRetrieve": ["hierarchy", "content", "anchor", "url", "url_without_anchor", "type"] - }, - "conversation_id": ["833762294"], - "nb_hits": 46250 -} +{ + "index_name": "alova_website", + "start_urls": ["https://alova.js.org/"], + "sitemap_urls": ["https://alova.js.org/sitemap.xml"], + "sitemap_alternate_links": true, + "stop_urls": ["/tests"], + "selectors": { + "lvl0": { + "selector": "(//ul[contains(@class,'menu__list')]//a[contains(@class, 'menu__link menu__link--sublist menu__link--active')]/text() | //nav[contains(@class, 'navbar')]//a[contains(@class, 'navbar__link--active')]/text())[last()]", + "type": "xpath", + "global": true, + "default_value": "Documentation" + }, + "lvl1": "header h1", + "lvl2": "article h2", + "lvl3": "article h3", + "lvl4": "article h4", + "lvl5": "article h5, article td:first-child", + "lvl6": "article h6", + "text": "article p, article li, article td:last-child" + }, + "strip_chars": " .,;:#", + "custom_settings": { + "separatorsToIndex": "_", + "attributesForFaceting": ["language", "version", "type", "docusaurus_tag"], + "attributesToRetrieve": [ + "hierarchy", + "content", + "anchor", + "url", + "url_without_anchor", + "type" + ] + }, + "conversation_id": ["833762294"], + "nb_hits": 46250 +} diff --git a/docusaurus.config.ts b/docusaurus.config.ts index e104a0450..79603b831 100644 --- a/docusaurus.config.ts +++ b/docusaurus.config.ts @@ -91,7 +91,7 @@ const config: Config = { }, { label: 'UI Frameworks', - to: 'next/resource/framework' + to: 'next/category/framework' }, { label: 'Error Reference', diff --git a/example-links/ActionDelegationMiddleware.tsx b/example-links/ActionDelegationMiddleware.tsx index 798d7f2ca..c0a4d0667 100644 --- a/example-links/ActionDelegationMiddleware.tsx +++ b/example-links/ActionDelegationMiddleware.tsx @@ -1,6 +1,6 @@ -import EmbedCodesandbox from '@site/src/components/EmbedCodesandbox'; -import React from 'react'; - -export default () => ( - -); +import EmbedCodesandbox from '@site/src/components/EmbedCodesandbox'; +import React from 'react'; + +export default () => ( + +); diff --git a/example-links/CaptchaSend.tsx b/example-links/CaptchaSend.tsx index a04f0d221..c74c6c4f4 100644 --- a/example-links/CaptchaSend.tsx +++ b/example-links/CaptchaSend.tsx @@ -1,6 +1,6 @@ -import EmbedCodesandbox from '@site/src/components/EmbedCodesandbox'; -import React from 'react'; - -export default () => ( - -); +import EmbedCodesandbox from '@site/src/components/EmbedCodesandbox'; +import React from 'react'; + +export default () => ( + +); diff --git a/example-links/ConditionSearch.tsx b/example-links/ConditionSearch.tsx index b1ed01999..e3e5d0188 100644 --- a/example-links/ConditionSearch.tsx +++ b/example-links/ConditionSearch.tsx @@ -1,6 +1,6 @@ -import EmbedCodesandbox from '@site/src/components/EmbedCodesandbox'; -import React from 'react'; - -export default () => ( - -); +import EmbedCodesandbox from '@site/src/components/EmbedCodesandbox'; +import React from 'react'; + +export default () => ( + +); diff --git a/example-links/ControlledCacheByIndexedDB.tsx b/example-links/ControlledCacheByIndexedDB.tsx index b8f3e7e1a..dc61333c8 100644 --- a/example-links/ControlledCacheByIndexedDB.tsx +++ b/example-links/ControlledCacheByIndexedDB.tsx @@ -1,6 +1,6 @@ -import EmbedCodesandbox from '@site/src/components/EmbedCodesandbox'; -import React from 'react'; - -export default () => ( - -); +import EmbedCodesandbox from '@site/src/components/EmbedCodesandbox'; +import React from 'react'; + +export default () => ( + +); diff --git a/example-links/FormHook.tsx b/example-links/FormHook.tsx index 89a18f9db..2470b9f8c 100644 --- a/example-links/FormHook.tsx +++ b/example-links/FormHook.tsx @@ -1,6 +1,6 @@ -import EmbedCodesandbox from '@site/src/components/EmbedCodesandbox'; -import React from 'react'; - -export default () => ( - -); +import EmbedCodesandbox from '@site/src/components/EmbedCodesandbox'; +import React from 'react'; + +export default () => ( + +); diff --git a/example-links/InitPage.tsx b/example-links/InitPage.tsx index e33706a95..4f48a1f3e 100644 --- a/example-links/InitPage.tsx +++ b/example-links/InitPage.tsx @@ -1,6 +1,6 @@ -import EmbedCodesandbox from '@site/src/components/EmbedCodesandbox'; -import React from 'react'; - -export default () => ( - -); +import EmbedCodesandbox from '@site/src/components/EmbedCodesandbox'; +import React from 'react'; + +export default () => ( + +); diff --git a/example-links/LoadMore.tsx b/example-links/LoadMore.tsx index 9892ace36..ba36aa13b 100644 --- a/example-links/LoadMore.tsx +++ b/example-links/LoadMore.tsx @@ -1,6 +1,6 @@ -import EmbedCodesandbox from '@site/src/components/EmbedCodesandbox'; -import React from 'react'; - -export default () => ( - -); +import EmbedCodesandbox from '@site/src/components/EmbedCodesandbox'; +import React from 'react'; + +export default () => ( + +); diff --git a/example-links/MemoryCache.tsx b/example-links/MemoryCache.tsx index 253e789cc..6b25f9582 100644 --- a/example-links/MemoryCache.tsx +++ b/example-links/MemoryCache.tsx @@ -1,6 +1,6 @@ -import EmbedCodesandbox from '@site/src/components/EmbedCodesandbox'; -import React from 'react'; - -export default () => ( - -); +import EmbedCodesandbox from '@site/src/components/EmbedCodesandbox'; +import React from 'react'; + +export default () => ( + +); diff --git a/example-links/NoteSilentReact.tsx b/example-links/NoteSilentReact.tsx index 0a2b8950e..25f067e68 100644 --- a/example-links/NoteSilentReact.tsx +++ b/example-links/NoteSilentReact.tsx @@ -1,6 +1,6 @@ -import EmbedCodesandbox from '@site/src/components/EmbedCodesandbox'; -import React from 'react'; - -export default () => ( - -); +import EmbedCodesandbox from '@site/src/components/EmbedCodesandbox'; +import React from 'react'; + +export default () => ( + +); diff --git a/example-links/Pagination.tsx b/example-links/Pagination.tsx index e7c94d78b..29c9e226c 100644 --- a/example-links/Pagination.tsx +++ b/example-links/Pagination.tsx @@ -1,6 +1,6 @@ -import EmbedCodesandbox from '@site/src/components/EmbedCodesandbox'; -import React from 'react'; - -export default () => ( - -); +import EmbedCodesandbox from '@site/src/components/EmbedCodesandbox'; +import React from 'react'; + +export default () => ( + +); diff --git a/example-links/Prefetch.tsx b/example-links/Prefetch.tsx index e9adaec16..88884ca67 100644 --- a/example-links/Prefetch.tsx +++ b/example-links/Prefetch.tsx @@ -1,6 +1,6 @@ -import EmbedCodesandbox from '@site/src/components/EmbedCodesandbox'; -import React from 'react'; - -export default () => ( - -); +import EmbedCodesandbox from '@site/src/components/EmbedCodesandbox'; +import React from 'react'; + +export default () => ( + +); diff --git a/example-links/RetriableHook.tsx b/example-links/RetriableHook.tsx index 5097b51a7..8ba6bca61 100644 --- a/example-links/RetriableHook.tsx +++ b/example-links/RetriableHook.tsx @@ -1,6 +1,6 @@ -import EmbedCodesandbox from '@site/src/components/EmbedCodesandbox'; -import React from 'react'; - -export default () => ( - -); +import EmbedCodesandbox from '@site/src/components/EmbedCodesandbox'; +import React from 'react'; + +export default () => ( + +); diff --git a/example-links/SerialRequest.tsx b/example-links/SerialRequest.tsx index ae4d1eea8..bb39b3048 100644 --- a/example-links/SerialRequest.tsx +++ b/example-links/SerialRequest.tsx @@ -1,6 +1,6 @@ -import EmbedCodesandbox from '@site/src/components/EmbedCodesandbox'; -import React from 'react'; - -export default () => ( - -); +import EmbedCodesandbox from '@site/src/components/EmbedCodesandbox'; +import React from 'react'; + +export default () => ( + +); diff --git a/example-links/SettingSilentSvelte.tsx b/example-links/SettingSilentSvelte.tsx index fd50cfca8..e082eaf7b 100644 --- a/example-links/SettingSilentSvelte.tsx +++ b/example-links/SettingSilentSvelte.tsx @@ -1,6 +1,6 @@ -import EmbedCodesandbox from '@site/src/components/EmbedCodesandbox'; -import React from 'react'; - -export default () => ( - -); +import EmbedCodesandbox from '@site/src/components/EmbedCodesandbox'; +import React from 'react'; + +export default () => ( + +); diff --git a/example-links/SimpleListSilentVue.tsx b/example-links/SimpleListSilentVue.tsx index a364ee80a..ec3f8e057 100644 --- a/example-links/SimpleListSilentVue.tsx +++ b/example-links/SimpleListSilentVue.tsx @@ -1,6 +1,6 @@ -import EmbedCodesandbox from '@site/src/components/EmbedCodesandbox'; -import React from 'react'; - -export default () => ( - -); +import EmbedCodesandbox from '@site/src/components/EmbedCodesandbox'; +import React from 'react'; + +export default () => ( + +); diff --git a/example-links/StoragePlaceholder.tsx b/example-links/StoragePlaceholder.tsx index 823be6e39..1ff7cd6b1 100644 --- a/example-links/StoragePlaceholder.tsx +++ b/example-links/StoragePlaceholder.tsx @@ -1,6 +1,6 @@ -import EmbedCodesandbox from '@site/src/components/EmbedCodesandbox'; -import React from 'react'; - -export default () => ( - -); +import EmbedCodesandbox from '@site/src/components/EmbedCodesandbox'; +import React from 'react'; + +export default () => ( + +); diff --git a/example-links/StorageRestore.tsx b/example-links/StorageRestore.tsx index dc5523451..054f0a3c0 100644 --- a/example-links/StorageRestore.tsx +++ b/example-links/StorageRestore.tsx @@ -1,6 +1,6 @@ -import EmbedCodesandbox from '@site/src/components/EmbedCodesandbox'; -import React from 'react'; - -export default () => ( - -); +import EmbedCodesandbox from '@site/src/components/EmbedCodesandbox'; +import React from 'react'; + +export default () => ( + +); diff --git a/example-links/SubmitForm.tsx b/example-links/SubmitForm.tsx index 9630b0339..82f029a9c 100644 --- a/example-links/SubmitForm.tsx +++ b/example-links/SubmitForm.tsx @@ -1,6 +1,6 @@ -import EmbedCodesandbox from '@site/src/components/EmbedCodesandbox'; -import React from 'react'; - -export default () => ( - -); +import EmbedCodesandbox from '@site/src/components/EmbedCodesandbox'; +import React from 'react'; + +export default () => ( + +); diff --git a/example-links/UpdateState.tsx b/example-links/UpdateState.tsx index c0f2122fc..27cc7acab 100644 --- a/example-links/UpdateState.tsx +++ b/example-links/UpdateState.tsx @@ -1,6 +1,6 @@ -import EmbedCodesandbox from '@site/src/components/EmbedCodesandbox'; -import React from 'react'; - -export default () => ( - -); +import EmbedCodesandbox from '@site/src/components/EmbedCodesandbox'; +import React from 'react'; + +export default () => ( + +); diff --git a/extract-docs.js b/extract-docs.js index 576edb328..e86d20ac9 100644 --- a/extract-docs.js +++ b/extract-docs.js @@ -1,49 +1,52 @@ -const { join } = require('path'); -const { resolve: urlResolve } = require('url'); -const fs = require('fs'); - -const baseURL = 'https://alova.js.org/'; -const filterFile = ['_category_.json', 'example']; -const replaceTargetContent = (content, link) => { - const buildModule = mat => `++++++++++\nDocument link: ${link}\n${mat}`; - return ( - content - // 章节标题改为一级标题 - .replace(/---\s+?title:([\s\S]*?)\n[\s\S]*?---/g, (_, rep) => buildModule(rep)) - // 添加分隔符 - .replace(/[^#]##\s+(.*?)\s/g, mat => buildModule(mat)) - // 替换链接 - .replace(/\(\/((category|tutorial|api|contributing).+?)\)/g, `(${baseURL}/$1)`) - ); -}; -const buildLink = filesrc => - urlResolve(baseURL, filesrc.split('docs')[1]) - .replace(/[0-9]+-/g, '') - .replace('.README.md', '') - .replace('.md', ''); - -const extract = async targetPath => { - let distFileContent = ''; - const filepaths = fs.readdirSync(targetPath); - for (let path of filepaths) { - const filesrc = join(targetPath, path); - if (filterFile.some(item => filesrc.includes(item))) { - continue; - } - let stats = fs.statSync(filesrc); - if (stats.isFile()) { - const docLink = buildLink(filesrc); - distFileContent += replaceTargetContent(fs.readFileSync(filesrc, { encoding: 'utf8' }), docLink); - distFileContent += '\n\n\n'; - } else { - distFileContent += await extract(filesrc); - } - } - return distFileContent; -}; - -const basePath = 'docs/tutorial'; -(async () => { - const content = await extract(join(process.cwd(), basePath)); - fs.writeFileSync(join(process.cwd(), './distFile.md'), content); -})(); +const { join } = require('path'); +const { resolve: urlResolve } = require('url'); +const fs = require('fs'); + +const baseURL = 'https://alova.js.org/'; +const filterFile = ['_category_.json', 'example']; +const replaceTargetContent = (content, link) => { + const buildModule = mat => `++++++++++\nDocument link: ${link}\n${mat}`; + return ( + content + // 章节标题改为一级标题 + .replace(/---\s+?title:([\s\S]*?)\n[\s\S]*?---/g, (_, rep) => buildModule(rep)) + // 添加分隔符 + .replace(/[^#]##\s+(.*?)\s/g, mat => buildModule(mat)) + // 替换链接 + .replace(/\(\/((category|tutorial|api|contributing).+?)\)/g, `(${baseURL}/$1)`) + ); +}; +const buildLink = filesrc => + urlResolve(baseURL, filesrc.split('docs')[1]) + .replace(/[0-9]+-/g, '') + .replace('.README.md', '') + .replace('.md', ''); + +const extract = async targetPath => { + let distFileContent = ''; + const filepaths = fs.readdirSync(targetPath); + for (let path of filepaths) { + const filesrc = join(targetPath, path); + if (filterFile.some(item => filesrc.includes(item))) { + continue; + } + let stats = fs.statSync(filesrc); + if (stats.isFile()) { + const docLink = buildLink(filesrc); + distFileContent += replaceTargetContent( + fs.readFileSync(filesrc, { encoding: 'utf8' }), + docLink + ); + distFileContent += '\n\n\n'; + } else { + distFileContent += await extract(filesrc); + } + } + return distFileContent; +}; + +const basePath = 'docs/tutorial'; +(async () => { + const content = await extract(join(process.cwd(), basePath)); + fs.writeFileSync(join(process.cwd(), './distFile.md'), content); +})(); diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/api/01-alova.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/api/01-alova.md index b1bf84241..99dbf3d96 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/api/01-alova.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/api/01-alova.md @@ -22,7 +22,7 @@ function createAlova(options?: AlovaOptions): Alova; | statesHook | object | 状态管理钩子,选填,[查看详情](/next/tutorial/getting-started/basic/combine-framework) | | requestAdapter | object | 请求适配器,必填,[查看详情](/next/tutorial/advanced/custom/http-adapter) | | timeout | number | 超时时间,默认不超时,[查看详情](/next/tutorial/getting-started/basic/alova) | -| localCache | object | 本地缓存配置,默认 GET 有 5000ms 缓存,[查看详情](/next/tutorial/cache/mode) | +| cacheFor | object | 本地缓存配置,默认 GET 有 5000ms 缓存,[查看详情](/next/tutorial/cache/mode) | | storageAdapter | object | 本地存储适配器,默认为`localStorage`,[查看详情](/next/tutorial/advanced/custom/storage-adapter) | | beforeRequest | function | 请求前钩子,[查看详情](/next/tutorial/getting-started/basic/global-interceptor) | | responded | object \| function | 请求响应钩子,[查看详情](/next/tutorial/getting-started/basic/global-interceptor) | @@ -67,7 +67,7 @@ interface AlovaOptions { requestAdapter: AlovaRequestAdapter; baseURL?: string; timeout?: number; - localCache?: GlobalLocalCacheConfig; + cacheFor?: GlobalcacheForConfig; storageAdapter?: AlovaStorageAdapter; beforeRequest?: Function; responded?: Function | ResponsedHandlerRecord; @@ -108,16 +108,16 @@ interface Alova { 1. url: 请求地址 2. config: 配置参数 -| 参数名 | 类型 | 说明 | -| ------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| headers | object | 请求头,[查看详情](/next/tutorial/getting-started/basic/method) | -| params | object | 请求参数,[查看详情](/next/tutorial/getting-started/basic/method) | -| name | string | method 对象名称,在 [updateState](/next/tutorial/client/in-depth/update-across-components)、[invalidateCache](/next/tutorial/cache/manually-invalidate)、[setCache](/next/tutorial/cache/set-and-query)、以及 [fetch 函数](/next/tutorial/client/strategy/use-fetcher)中可以通过名称或通配符获取对应 method 实例 | -| timeout | number | 请求超时时间,[查看详情](/next/tutorial/getting-started/basic/method) | -| localCache | LocalCacheConfig | 响应缓存时间,[查看详情](/next/tutorial/cache/mode) | -| hitSource | string | 打击源方法实例,当源方法实例请求成功时,当前方法实例的缓存将被失效,[查看详情](/next/tutorial/cache/auto-invalidate) | -| transformData | function | 转换响应数据,[查看详情](/next/tutorial/getting-started/basic/method) | -| shareRequest | boolean | 请求级共享请求开关,[查看详情](/next/tutorial/getting-started/basic/method) | +| 参数名 | 类型 | 说明 | +| ------------ | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| headers | object | 请求头,[查看详情](/next/tutorial/getting-started/basic/method) | +| params | object | 请求参数,[查看详情](/next/tutorial/getting-started/basic/method) | +| name | string | method 对象名称,在 [updateState](/next/tutorial/client/in-depth/update-across-components)、[invalidateCache](/next/tutorial/cache/manually-invalidate)、[setCache](/next/tutorial/cache/set-and-query)、以及 [fetch 函数](/next/tutorial/client/strategy/use-fetcher)中可以通过名称或通配符获取对应 method 实例 | +| timeout | number | 请求超时时间,[查看详情](/next/tutorial/getting-started/basic/method) | +| cacheFor | cacheForConfig | 响应缓存时间,[查看详情](/next/tutorial/cache/mode) | +| hitSource | string | 打击源方法实例,当源方法实例请求成功时,当前方法实例的缓存将被失效,[查看详情](/next/tutorial/cache/auto-invalidate) | +| transform | function | 转换响应数据,[查看详情](/next/tutorial/getting-started/basic/method) | +| shareRequest | boolean | 请求级共享请求开关,[查看详情](/next/tutorial/getting-started/basic/method) | > 除了可配置上面的参数外,还支持请求适配器支持的其他参数。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/api/_category_.json b/i18n/zh-CN/docusaurus-plugin-content-docs/current/api/_category_.json index 4b4861e39..de7065684 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/api/_category_.json +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/api/_category_.json @@ -1,6 +1,6 @@ -{ - "label": "API", - "link": { - "type": "generated-index" - } -} +{ + "label": "API", + "link": { + "type": "generated-index" + } +} diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/contributing/01-overview.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/contributing/01-overview.md index 9ca89693e..5db6dcf88 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/contributing/01-overview.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/contributing/01-overview.md @@ -55,7 +55,7 @@ alova 是一个轻量级的请求策略库,**它的使命就是让开发者在 1. Method 代理设计,高聚合、平台无关的设计,贯穿请求始终,你在任意请求函数中都应该可以访问到它,从另一个角度说,与请求相关的信息也应该被放在 method 实例中; 2. 轻量级,在编码中尽量保持源码简洁,例如避免重复代码、合并变量声明、原型链函数封装、无相似 api、tree shaking,但长变量名是被允许的,因为在编译时它将会被单个字母替代; -3. 高扩展性设计,其一,alova 的设计中大量使用了适配器模式和钩子函数,例如适配器有`requestAdapter`、`storageAdapter`等,钩子函数有`beforeRequest`、`reseponded`、`transformData`、`localCache`等,而且大多存在默认行为,这样设计的目的是为了在保留高扩展性的同时,使用也足够简单;其二,全局请求参数可覆盖,例如`timeout`、`shareRequest`等,对于特别的请求可单独设置这些参数。 +3. 高扩展性设计,其一,alova 的设计中大量使用了适配器模式和钩子函数,例如适配器有`requestAdapter`、`storageAdapter`等,钩子函数有`beforeRequest`、`reseponded`、`transform`、`cacheFor`等,而且大多存在默认行为,这样设计的目的是为了在保留高扩展性的同时,使用也足够简单;其二,全局请求参数可覆盖,例如`timeout`、`shareRequest`等,对于特别的请求可单独设置这些参数。 4. api 设计具有普适性,其一,它表示此 api 的功能具有较高的抽象层级,而不是针对某一个具体业务而提出的;其二,api 设计具有可扩展性,以适应 api 的迭代 > api 普适性设计仅适用于 alova 库,如果你正在构思一个请求策略,那么可以根据具体业务来设计。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/contributing/_category_.json b/i18n/zh-CN/docusaurus-plugin-content-docs/current/contributing/_category_.json index 96d3ac477..38748dc4b 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/contributing/_category_.json +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/contributing/_category_.json @@ -1,6 +1,6 @@ -{ - "label": "贡献指南", - "link": { - "type": "generated-index" - } -} +{ + "label": "贡献指南", + "link": { + "type": "generated-index" + } +} diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/release-notes-v3.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/release-notes-v3.md index a3a3eb8c5..a16d7b449 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/release-notes-v3.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/release-notes-v3.md @@ -265,6 +265,10 @@ useWatcher(() => method, [xxx], { 在 method 中不再需要这两个参数,将改为自动判断是否开启,通过 getter 判断外部是否使用了 uploading 和 downloading。 +### 废弃 method 的 `placeholder`缓存模式 + +正如在“重新设计缓存模式”中所说,`placeholder`模式已使用其他方式替代实现。 + ### 移除废弃的 responsed 在 createAlova 中推荐统一使用`responded`字段。 @@ -497,7 +501,7 @@ useRequest(Getter, { ## 字段修改 -### `method.key` 简化为 `method.key` +### `method.__key__` 简化为 `method.key` ```js // 2.x diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/01-vue-composition.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/01-vue-composition.md new file mode 100644 index 000000000..e142aa4c2 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/01-vue-composition.md @@ -0,0 +1,27 @@ +--- +title: vue composition +--- + +通过`alova/vue`支持在 vue composition 使用请求策略。 + +```js +import { createAlova } from 'alova'; +import VueHook from 'alova/vue'; + +const alovaInstance = createAlova({ + // ... + statesHook: VueHook +}); +``` + +如果在 vue2 中使用 composition 语法,请使用`alova/vue-demi`。 + +```js +import { createAlova } from 'alova'; +import VueHook from 'alova/vue-demi'; + +const alovaInstance = createAlova({ + // ... + statesHook: VueHook +}); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/02-react.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/02-react.md new file mode 100644 index 000000000..043c73a84 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/02-react.md @@ -0,0 +1,15 @@ +--- +title: react +--- + +通过`alova/react`支持在 react 中使用请求策略。 + +```js +import { createAlova } from 'alova'; +import ReactHook from 'alova/react'; + +const alovaInstance = createAlova({ + // ... + statesHook: ReactHook +}); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/03-svelte.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/03-svelte.md new file mode 100644 index 000000000..9e545b90f --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/03-svelte.md @@ -0,0 +1,15 @@ +--- +title: svelte +--- + +通过`alova/svelte`支持在 svelte 中使用请求策略。 + +```js +import { createAlova } from 'alova'; +import SvelteHook from 'alova/svelte'; + +const alovaInstance = createAlova({ + // ... + statesHook: SvelteHook +}); +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/02-solid.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/04-solid.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/02-solid.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/04-solid.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/03-angular.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/05-angular.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/03-angular.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/05-angular.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/05-preact.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/06-preact.md similarity index 100% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/05-preact.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/06-preact.md diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/01-vue-options.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/07-vue-options.md similarity index 69% rename from i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/01-vue-options.md rename to i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/07-vue-options.md index 120decd4f..8b203b31d 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/01-vue-options.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/07-vue-options.md @@ -1,11 +1,11 @@ --- -title: vue2/3 options +title: vue options --- import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -通常,use hook 只能在 vue 的 setup 中使用,但通过`@alova/vue-options`提供的辅助函数,你也可以在 vue 的 options 中使用 alova 的 use hook,完美兼容 alova 的几乎所有功能。 +通常,use hook 只能在 vue 的 setup 中使用,但通过`@alova/vue-options`提供的辅助函数,你也可以在 vue 的 options 中使用 alova 的 use hook,完美兼容 alova 的几乎所有功能,你可以在 options 下使用`alova/client`的所有请求策略。 > vue2 和 vue3 中均可使用。 @@ -34,9 +34,11 @@ yarn add alova @alova/vue-options -:::info alova 版本要求 +:::info 版本要求 -alova 版本 >= 2.13.2 +alova 版本 >= 3.0.0 + +vue 版本 >= 2.17.0 ::: @@ -85,7 +87,7 @@ export const getData = () => alovaInst.Get('/todolist'); @@ -81,7 +79,7 @@ import { useFetcher } from 'alova/client'; // method实例创建函数 const getTodoList = currentPage => { return alovaInstance.Get('/todo/list', { - localCache: 60000, + cacheFor: 60000, params: { currentPage, pageSize: 10 @@ -104,12 +102,10 @@ const App = () => { updateState: false }); const [currentPage, setCurrentPage] = useState(1); - const { data, onSuccess } = useWatcher(() => getTodoList(currentPage), [currentPage], { + const { data } = useWatcher(() => getTodoList(currentPage), [currentPage], { immediate: true - }); - - // 在当前页加载成功后,传入下一页的method实例,即可预拉取下一页的数据 - onSuccess(() => { + }).onSuccess(() => { + // 在当前页加载成功后,传入下一页的method实例,即可预拉取下一页的数据 fetch(getTodoList(currentPage + 1)); }); @@ -133,7 +129,7 @@ const App = () => { // method实例创建函数 const getTodoList = currentPage => { return alovaInstance.Get('/todo/list', { - localCache: 60000, + cacheFor: 60000, params: { currentPage, pageSize: 10 @@ -155,12 +151,10 @@ const App = () => { updateState: false }); const currentPage = writable(1); - const { data, onSuccess } = useWatcher(() => getTodoList($currentPage), [currentPage], { + const { data } = useWatcher(() => getTodoList($currentPage), [currentPage], { immediate: true - }); - - // 在当前页加载成功后,传入下一页的method实例,即可预拉取下一页的数据 - onSuccess(() => { + }).onSuccess(() => { + // 在当前页加载成功后,传入下一页的method实例,即可预拉取下一页的数据 fetch(getTodoList($currentPage + 1)); }); diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/04-use-pagination.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/04-use-pagination.md index b9584d804..7e51d454f 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/04-use-pagination.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/04-use-pagination.md @@ -340,7 +340,7 @@ usePagination((page, pageSize) => queryStudents(page, pageSize), { 在开启预加载时,并不会一味地加载下一页,需要满足以下两个条件: -1. 预加载是基于缓存的,用于分页加载的 Method 实例必须开启缓存,默认情况下 get 请求会有 5 分钟的 memory 缓存,如果是非 get 请求或者全局关闭了缓存,你还需要在这个 Method 实例中单独设置`localCache`开启缓存。 +1. 预加载是基于缓存的,用于分页加载的 Method 实例必须开启缓存,默认情况下 get 请求会有 5 分钟的 memory 缓存,如果是非 get 请求或者全局关闭了缓存,你还需要在这个 Method 实例中单独设置`cacheFor`开启缓存。 2. 根据`total`和`pageSize`参数判断出下一页还有数据。 ::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/09-sensorless-data-interaction/_category_.json b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/09-sensorless-data-interaction/_category_.json index 68d7d2cf7..76152ffa6 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/09-sensorless-data-interaction/_category_.json +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/09-sensorless-data-interaction/_category_.json @@ -1,3 +1,3 @@ -{ - "label": "无感数据交互" -} +{ + "label": "无感数据交互" +} diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/README.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/README.md index 76d38da3c..d947ebafc 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/README.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/03-client/01-strategy/README.md @@ -9,9 +9,13 @@ import DocCardList from '@theme/DocCardList'; 所有的客户端 use hooks 都有以下共同点: 1. 它们都依赖 statesHook,请在使用前[设置 statesHook](/next/tutorial/getting-started/basic/combine-framework)。 + 2. 它们的返回值中都包含`update`函数,用于主动更新导出的状态值。 + 3. 在 react 下为了性能表现,所有的操作函数例如`send`、`update`、`abort`等都使用了`useCallback`包装过。 +4. 以`on`开头的绑定函数都可以进行链式调用。 + ## 目录 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/06-controlled-cache.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/06-controlled-cache.md index 8fa6183a3..1aa8e9c95 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/06-controlled-cache.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/05-cache/06-controlled-cache.md @@ -23,9 +23,9 @@ const getFile = fileName => 你也可以在`cacheFor`中返回 `undefined` 或不返回任何数据,继续发送请求,这在自定义管理缓存时未命中缓存的情况下很有用。 -## 使用 transformData 设置缓存 +## 使用 transform 设置缓存 -由于 `transformData` 函数具有以下两个特性: +由于 `transform` 函数具有以下两个特性: - 只有在响应时才被触发,而命中响应缓存时不会触发; - 支持异步函数; @@ -35,7 +35,7 @@ const getFile = fileName => ```javascript const fileGetter = alovaInstance.Get('/file/file_name', { // 使用IndexedDB缓存文件 - async transformData(fileBlob) { + async transform(fileBlob) { await new Promise((resolve, reject) => { const tx = db.transaction(['files'], 'readwrite'); const putRequest = tx.objectStore('files').put({ diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/01-in-depth/05-custom-method-key.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/01-in-depth/05-custom-method-key.md index 288a3918c..f585a1eb6 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/01-in-depth/05-custom-method-key.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/01-in-depth/05-custom-method-key.md @@ -21,9 +21,9 @@ method key 用来标识一切与 method 实例关联的数据,有很大的作 ## 自定义 method 实例 key ```javascript -// method key在创建时生成,可以通过__key__自定义它 +// method key在创建时生成,可以通过key自定义它 const methodInst = alovaInstance.Get('/api/user', {}); -methodInst.__key__ = 'my-custom-method-key'; +methodInst.key = 'my-custom-method-key'; ``` ## 自定义全部 method 实例 key diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/01-in-depth/10-typescript.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/01-in-depth/10-typescript.md index 49181413c..ecfc6e436 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/01-in-depth/10-typescript.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/01-in-depth/10-typescript.md @@ -69,7 +69,7 @@ data 的类型将会根据不同的 Method 实例中指定的响应数据类型 ### 情况 1 -当响应数据不需要再调用`transformData`转换,直接通过泛型指定类型 +当响应数据不需要再调用`transform`转换,直接通过泛型指定类型 ```typescript interface Todo { @@ -86,7 +86,7 @@ const { data } = useRequest(Get); ### 情况 2 -当响应数据需要再调用`transformData`转换,那就需要在转换函数参数中指定类型,然后它的返回值类型将会作为响应数据类型。 +当响应数据需要再调用`transform`转换,那就需要在转换函数参数中指定类型,然后它的返回值类型将会作为响应数据类型。 ```typescript interface Todo { @@ -96,7 +96,7 @@ interface Todo { } const Get = alovaInstance.Get('/todo/list', { // 将类型写到data参数中,而headers会自动推断,可以不用指定类型 - transformData(data: Todo[], headers) { + transform(data: Todo[], headers) { return data.map(item => ({ ...item, status: item.done ? '已完成' : '未完成' diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/02-custom/01-http-adapter.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/02-custom/01-http-adapter.md index 547f3033d..e785adbfb 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/02-custom/01-http-adapter.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/06-advanced/02-custom/01-http-adapter.md @@ -79,7 +79,7 @@ interface RequestElements { **headers(必填)** -一个异步函数,函数返回的响应头对象将传递给 Method 实例的 transformData 转换钩子函数; +一个异步函数,函数返回的响应头对象将传递给 Method 实例的 transform 转换钩子函数; **abort(必填)** diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-project/01-best-practice/03-manage-cache-by-indexeddb.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-project/01-best-practice/03-manage-cache-by-indexeddb.md index 559436a36..2933df92b 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-project/01-best-practice/03-manage-cache-by-indexeddb.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/07-project/01-best-practice/03-manage-cache-by-indexeddb.md @@ -68,7 +68,7 @@ export const getImageFromCache = async fileName => { ## 保存数据 -在保存数据时,我们可以在 method 的`transformData`中保存缓存,因为`transformData`只会在网络请求响应时被触发,而命中缓存时不会触发的特性。在示例代码中,将图片 blob 实例转换为 base64 数据,缓存并返回这个 base64 数据。 +在保存数据时,我们可以在 method 的`transform`中保存缓存,因为`transform`只会在网络请求响应时被触发,而命中缓存时不会触发的特性。在示例代码中,将图片 blob 实例转换为 base64 数据,缓存并返回这个 base64 数据。 ```javascript api.js import { addImage2Cache } from './db'; @@ -76,7 +76,7 @@ import { addImage2Cache } from './db'; export const image = fileName => alovaInst.Get(`/image/${fileName}`, { // highlight-start - async transformData(imgBlob) { + async transform(imgBlob) { // 将blob异步转换为base64 const reader = new FileReader(); reader.readAsDataURL(imgBlob); @@ -96,19 +96,19 @@ export const image = fileName => ## 获取数据 -将这个 method 实例的`localCache`指定为一个异步函数,让缓存转变为受控状态,在这个函数中匹配 IndexedDB 中的缓存,如果匹配则返回它,否则返回`undefined`继续发起请求获取数据。 +将这个 method 实例的`cacheFor`指定为一个异步函数,让缓存转变为受控状态,在这个函数中匹配 IndexedDB 中的缓存,如果匹配则返回它,否则返回`undefined`继续发起请求获取数据。 ```javascript title=api.js import { getImageFromCache } from './db'; export const image = fileName => alovaInst.Get(`/image/${fileName}`, { - async transformData(imgBlob) { + async transform(imgBlob) { // ... }, // highlight-start - async localCache() { + async cacheFor() { // 获取缓存 const cache = await getImageFromCache(fileName); return cache && cache.data; @@ -117,6 +117,6 @@ export const image = fileName => }); ``` -这样就基本完成了一个基本的自定义缓存管理,你也可以保存缓存的过期时间,并在`localCache`中匹配到缓存时再判断是否已过期,从而实现缓存过期功能。 +这样就基本完成了一个基本的自定义缓存管理,你也可以保存缓存的过期时间,并在`cacheFor`中匹配到缓存时再判断是否已过期,从而实现缓存过期功能。 IndexedDB 只是其中一个异步管理缓存的案例,你也可以连接你的缓存服务器来管理它们。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/_category_.json b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/_category_.json index 4b4861e39..de7065684 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/_category_.json +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/api/_category_.json @@ -1,6 +1,6 @@ -{ - "label": "API", - "link": { - "type": "generated-index" - } -} +{ + "label": "API", + "link": { + "type": "generated-index" + } +} diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/contributing/_category_.json b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/contributing/_category_.json index 96d3ac477..38748dc4b 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/contributing/_category_.json +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/contributing/_category_.json @@ -1,6 +1,6 @@ -{ - "label": "贡献指南", - "link": { - "type": "generated-index" - } -} +{ + "label": "贡献指南", + "link": { + "type": "generated-index" + } +} diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/_category_.json b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/_category_.json index a03cd40f2..5b87d8d8e 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/_category_.json +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/01-example/_category_.json @@ -1,7 +1,7 @@ -{ - "label": "示例", - "link": { - "type": "generated-index", - "description": "这里有丰富的示例,来展示alova在不同请求场景下的表现,示例运行前会先安装依赖,要稍微等待一会儿!" - } -} +{ + "label": "示例", + "link": { + "type": "generated-index", + "description": "这里有丰富的示例,来展示alova在不同请求场景下的表现,示例运行前会先安装依赖,要稍微等待一会儿!" + } +} diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/README.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/README.md index 4e79cfb41..c14492607 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/README.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/README.md @@ -1,148 +1,148 @@ ---- -title: alova 是什么 ---- - -import Link from '@docusaurus/Link'; -import NavCard from '@site/src/components/NavCard'; -import SupportList from '@site/src/components/SupportList'; - -alova 是一个轻量级的请求策略库,它提供了一套完整的应对复杂请求场景的方案,我们称之为**请求策略**,只需一行代码就能快速实现各种复杂的请求逻辑,不仅能帮你提升开发效率,还能帮你提升 App 的运行效率,降低服务端压力。 - -这是一个最简单的请求示例: - -```javascript -const response = await alova.Get('/api/user'); -``` - -平平无奇吧?让我们再看一个自动管理请求状态的示例,**loading、error、data 是响应式的数据**,在 react、vue、svelte 等 UI 框架中可以直接在视图中绑定它们,而且会根据请求状态自动维护它这些响应式数据。 - -```javascript -const { loading, error, data } = useRequest(alova.Get('/api/user')); -``` - -以下是一个分页请求策略的示例,**当 page、pageSize 等发生变化时会自动以不同参数触发请求**。 - -```javascript -const { loading, error, data, page, pageSize, total } = usePagination((page, size) => - alova.Get('/api/user/list', { - params: { page, size } - }) -); -``` - -alova 提供了 10+个基于[RSM](/tutorial/others/RSM)规范的请求策略模块,它们以 useHook 的形式实现。 - -## 核心 useHook - -useRequest -useWatcher -useFetcher - -## 场景化请求策略 - -usePagination -useSQRequest -useForm -TokenAuthentication -useAutoRequest -useCaptcha -actionDelegationMiddleware -useSerialRequest -useSerialWatcher -useRetriableRequest -useUploader -useSSE - -## 高灵活性 - -得益于 alova 的高灵活性,你可以在以下不同 JS 环境下搭配不同的请求库使用(灰色部分将在未来逐渐支持)。 - - - -## 有什么不同吗? - -与其他请求库不同的是,alova 的目标是让请求变得更简单并保持更高效的数据交互。 - -我们为开发者和 App 使用者双方考虑,对于开发者来说,alova 为他们提供了简单的请求 api,和开箱即用的高性能请求策略模块,对于应用的用户来说,他们可以享受到 alova 的高性能数据交互带来的流畅体验。 - -此外,再从具体的特性来看看: - -- 与 axios 相似的 api 设计,让使用者学习成本更低; -- 10+个开箱即用的高性能请求策略,让应用更流畅; -- alova 是轻量级的,只有 4kb+,是 axios 的 30%+; -- 灵活性高,alova 的适配器可以让 alova 在任何 js 环境下,与任何 UI 框架协作使用(内置支持的 UI 框架为`vue/react/svelte`),并且提供了统一的使用体验和完美的代码迁移; -- 3 种缓存模式和请求共享机制,提升请求性能并降低服务端压力; -- api 代码的高聚合组织,每个 api 的请求参数、缓存行为、响应数据转换等都将聚集在相同的代码块中,这对于管理大量的 api 有很大的优势; - -在[alova 的未来](/tutorial/others/future)中,将实现更进一步的请求简单化。 - -:::info 与其他请求库的对比 - -你还可以查看请[与其他请求库比较](/tutorial/others/comparison)详细了解 alova 的不同之处。 - -::: - -## 在线试用 - -你可以通过 Codesandbox [在线可编辑示例尝试 alovajs](/category/examples)直接在浏览器中运行项目,因此它与本地开发几乎无差别,同时无需在你的机器上安装任何东西。 - -## 脚手架推荐 - -, -title: 'Uniapp 脚手架 - unibest', -desc: '集成了最新前端技术栈的跨端解决方案', -link: 'https://codercup.github.io/unibest-docs/', -target: '__blank' -} -]}> - -## 加入 alova 社区 - -import ImgDiscord from '@site/static/img/discord.svg'; -import ImgX from '@site/static/img/x.svg'; -import ImgWechat from '@site/static/img/wechat.svg'; -import wechatQrcode from '@site/static/img/wechat_qrcode.jpg'; - -, -title: 'Discord', -desc: '社区的 GPT 机器人为你解答', -link: 'https://discord.gg/S47QGJgkVb', -target: '__blank' -}, -{ -Image: , -title: '微信', -desc: '在群聊交流,更快获得回应', -link: wechatQrcode, -target: '__blank' -}, -{ -Image: , -title: 'X', -desc: '关注我们,持续获得最新动态', -link: 'https://x.com/alovajs', -target: '__blank' -} -]}> - -## 欢迎参与贡献 - -在参与贡献前,请务必详细阅读 [贡献指南](/contributing/overview),以保证你的有效贡献。 - -## 开始 - -接下来,我们将从最简单的请求开始,再到请求策略的讲解,了解 alova 如何简化你的工作,再深入到进阶指南,以及在实际项目中总结的最佳实践。 - -让我们开始学习发送第一个请求吧! - - +--- +title: alova 是什么 +--- + +import Link from '@docusaurus/Link'; +import NavCard from '@site/src/components/NavCard'; +import SupportList from '@site/src/components/SupportList'; + +alova 是一个轻量级的请求策略库,它提供了一套完整的应对复杂请求场景的方案,我们称之为**请求策略**,只需一行代码就能快速实现各种复杂的请求逻辑,不仅能帮你提升开发效率,还能帮你提升 App 的运行效率,降低服务端压力。 + +这是一个最简单的请求示例: + +```javascript +const response = await alova.Get('/api/user'); +``` + +平平无奇吧?让我们再看一个自动管理请求状态的示例,**loading、error、data 是响应式的数据**,在 react、vue、svelte 等 UI 框架中可以直接在视图中绑定它们,而且会根据请求状态自动维护它这些响应式数据。 + +```javascript +const { loading, error, data } = useRequest(alova.Get('/api/user')); +``` + +以下是一个分页请求策略的示例,**当 page、pageSize 等发生变化时会自动以不同参数触发请求**。 + +```javascript +const { loading, error, data, page, pageSize, total } = usePagination((page, size) => + alova.Get('/api/user/list', { + params: { page, size } + }) +); +``` + +alova 提供了 10+个基于[RSM](/tutorial/others/RSM)规范的请求策略模块,它们以 useHook 的形式实现。 + +## 核心 useHook + +useRequest +useWatcher +useFetcher + +## 场景化请求策略 + +usePagination +useSQRequest +useForm +TokenAuthentication +useAutoRequest +useCaptcha +actionDelegationMiddleware +useSerialRequest +useSerialWatcher +useRetriableRequest +useUploader +useSSE + +## 高灵活性 + +得益于 alova 的高灵活性,你可以在以下不同 JS 环境下搭配不同的请求库使用(灰色部分将在未来逐渐支持)。 + + + +## 有什么不同吗? + +与其他请求库不同的是,alova 的目标是让请求变得更简单并保持更高效的数据交互。 + +我们为开发者和 App 使用者双方考虑,对于开发者来说,alova 为他们提供了简单的请求 api,和开箱即用的高性能请求策略模块,对于应用的用户来说,他们可以享受到 alova 的高性能数据交互带来的流畅体验。 + +此外,再从具体的特性来看看: + +- 与 axios 相似的 api 设计,让使用者学习成本更低; +- 10+个开箱即用的高性能请求策略,让应用更流畅; +- alova 是轻量级的,只有 4kb+,是 axios 的 30%+; +- 灵活性高,alova 的适配器可以让 alova 在任何 js 环境下,与任何 UI 框架协作使用(内置支持的 UI 框架为`vue/react/svelte`),并且提供了统一的使用体验和完美的代码迁移; +- 3 种缓存模式和请求共享机制,提升请求性能并降低服务端压力; +- api 代码的高聚合组织,每个 api 的请求参数、缓存行为、响应数据转换等都将聚集在相同的代码块中,这对于管理大量的 api 有很大的优势; + +在[alova 的未来](/tutorial/others/future)中,将实现更进一步的请求简单化。 + +:::info 与其他请求库的对比 + +你还可以查看请[与其他请求库比较](/tutorial/others/comparison)详细了解 alova 的不同之处。 + +::: + +## 在线试用 + +你可以通过 Codesandbox [在线可编辑示例尝试 alovajs](/category/examples)直接在浏览器中运行项目,因此它与本地开发几乎无差别,同时无需在你的机器上安装任何东西。 + +## 脚手架推荐 + +, +title: 'Uniapp 脚手架 - unibest', +desc: '集成了最新前端技术栈的跨端解决方案', +link: 'https://codercup.github.io/unibest-docs/', +target: '__blank' +} +]}> + +## 加入 alova 社区 + +import ImgDiscord from '@site/static/img/discord.svg'; +import ImgX from '@site/static/img/x.svg'; +import ImgWechat from '@site/static/img/wechat.svg'; +import wechatQrcode from '@site/static/img/wechat_qrcode.jpg'; + +, +title: 'Discord', +desc: '社区的 GPT 机器人为你解答', +link: 'https://discord.gg/S47QGJgkVb', +target: '__blank' +}, +{ +Image: , +title: '微信', +desc: '在群聊交流,更快获得回应', +link: wechatQrcode, +target: '__blank' +}, +{ +Image: , +title: 'X', +desc: '关注我们,持续获得最新动态', +link: 'https://x.com/alovajs', +target: '__blank' +} +]}> + +## 欢迎参与贡献 + +在参与贡献前,请务必详细阅读 [贡献指南](/contributing/overview),以保证你的有效贡献。 + +## 开始 + +接下来,我们将从最简单的请求开始,再到请求策略的讲解,了解 alova 如何简化你的工作,再深入到进阶指南,以及在实际项目中总结的最佳实践。 + +让我们开始学习发送第一个请求吧! + + diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/_category_.json b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/_category_.json index b66c2bf01..9b16b5b8e 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/_category_.json +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/02-getting-started/_category_.json @@ -1,3 +1,3 @@ -{ - "label": "开始" -} +{ + "label": "开始" +} diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/_category_.json b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/_category_.json index 55d121aef..c292e5ee2 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/_category_.json +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/03-combine-framework/_category_.json @@ -1,3 +1,3 @@ -{ - "label": "Combine framework" -} +{ + "label": "Combine framework" +} diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/README.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/README.md index b0d7c75bb..d2f817b6a 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/README.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/README.md @@ -1,9 +1,9 @@ ---- -title: 响应缓存 ---- - -响应缓存是一个将服务端返回的数据缓存到客户端的技术,在可重复利用服务端数据时避免重复发送请求,既能立即响应用户请求,也能节省服务端资源。根据不同的缓存场景,alova 提供了 3 种模式,分别为**内存模式、缓存占位模式、恢复模式**,选择适合你的使用即可。 - -此外,使用缓存操作 API,你还可以自由添加、修改和删除缓存,以及自定义缓存匹配规则。 - -接下来,让我们从缓存模式开始理解吧! +--- +title: 响应缓存 +--- + +响应缓存是一个将服务端返回的数据缓存到客户端的技术,在可重复利用服务端数据时避免重复发送请求,既能立即响应用户请求,也能节省服务端资源。根据不同的缓存场景,alova 提供了 3 种模式,分别为**内存模式、缓存占位模式、恢复模式**,选择适合你的使用即可。 + +此外,使用缓存操作 API,你还可以自由添加、修改和删除缓存,以及自定义缓存匹配规则。 + +接下来,让我们从缓存模式开始理解吧! diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/_category_.json b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/_category_.json index 2360c70d8..1b6419623 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/_category_.json +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/04-cache/_category_.json @@ -1,3 +1,3 @@ -{ - "label": "Response Cache" -} +{ + "label": "Response Cache" +} diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/README.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/README.md index ff66e617e..90f7af39f 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/README.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/README.md @@ -1,146 +1,146 @@ ---- -title: 无感数据交互 - 概览 ---- - -无感数据交互是指用户在与应用进行交互时,无需等待即可立即展示相关内容,或者提交信息时也无需等待即可展示操作结果,就像和本地数据交互一样,从而大幅提升应用的流畅性,它让用户感知不到数据传输带来的卡顿。 - -这并不是新鲜事,早在 2015 年以前就已经有乐观更新的概念了,它是指在服务端响应前将提交结果展示到界面中,它是建立在大部分的提交都成功的假设上,与之相对的是保守更新,即服务端响应前将展示等待状态,直到请求完成。在处理失败方面,目前的乐观更新方案通常是通过回退来处理,例如下面的示例代码: - -```javascript -const list = []; -const data = {}; -addTodo(data).catch(() => { - list = list.filter(item => item !== data); -}); -list.push(data); -``` - -这可能会导致以下问题: - -1. 回退将增加用户的理解成本和操作成本; -2. 请求时序问题; -3. 如果后续的请求存在对本次提交依赖,则本次失败将导致后续的请求变得没有意义; -4. 可能丢失请求; - -经过数个月的方案设计和不断迭代,alova 已经在这方面走出了一大步,在我们的方案中对以上存在的问题进行了解决,可以更稳定地保证请求成功,虽然还存在技术限制,但在许多场景中得到了应用。在我们的技术方案中,可以更高限度地降低网络波动带来的问题,你的应用在高延迟网络甚至是断网状态下依然可用,在刷新页面后也依然能保持最新状态的数据。 - -## 应用场景 - -无感数据交互虽然不能大规模地使用,但在一定场景下它又是非常合适的,在探索中,我们发现了至少包含但不限于以下几个场景,供你参考。 - -### 编辑器类应用 - -笔记类应用如印象笔记,画布编辑类应用如 MasterGO,它们分别需要实现以下需求: - -1. 进入笔记或图纸列表时将全量拉取列表数据,下次进入将使用本地的缓存数据; -2. 编辑过程中实时同步到服务端,且同步过程发生在后台,不会影响用户正常使用; -3. 网络差或断网状态下也可以继续使用; - -:::info 示例 - -我们提供了一个[笔记应用示例](/tutorial/example/silent-submit-notes),你可以进入体验。 - -::: - -### 设置模块 - -由常用的开关、选择器组成的设置模块,需实现的需求是,用户操作后实时同步到服务端,同时不再展示提交状态,而是直接展示操作后的最新状态。 - -:::info 示例 - -我们提供了一个[设置页示例](/tutorial/example/silent-submit-setting),你可以进入体验。 - -::: - -### 简单的列表管理 - -我们将创建列表项时填写的数据足够用于列表页的展示,称为简单的列表,例如一个学生列表页展示学生的姓名、性别、年级三个数据,这三个数据在创建学生时都需要填写。在简单列表中将实现以下需求: - -1. 在新增、编辑和删除列表项时立即在列表页中展示最新状态,无需在提交完成后才迟迟展示,同时也不受网络波动限制; -2. 刷新页面时,列表页始终保持最新状态; - -:::info 示例 - -我们提供了一个[简单列表页示例](/tutorial/example/silent-submit-simple-list),你可以进入体验。 - -::: - -### 复杂的列表管理 - -复杂列表是指,创建列表项时填写的数据不足以在列表页用于展示,而需要根据服务端的计算产生额外的数据,例如一个 Todo 列表页除了展示基本信息外,还需要列出具体的执行日期,而在创建页只指定了执行日期范围和相关规则,因此执行日期由服务端根据日期范围和规则统一计算生成。 - -在复杂列表中将实现以下需求: - -1. 在新增、编辑和删除列表项时立即在列表页中展示最新状态,并在服务端响应后将服务端计算的数据更新到此列表项中; -2. 刷新页面时,列表页始终保持最新状态; - -:::info 示例 - -复杂列表示例敬请期待... - -::: - -### 自由模式 - -在以上的几种场景下,你可能希望根据一个条件来判断当前是使用无感交互策略还是最常见的保守请求策略,需求如下: - -1. 当网络状态良好时,或付费用户将使用无感交互策略,而网络波动大时,或免费用户则不能享受到无感交互策略; -2. 策略自由切换; - -:::info 示例 - -在以上的示例中你都可以体验自由切换策略 - -::: - -## 不推荐的应用场景 - -### 信息共享类 - -提交的信息需要同步给其他人,例如订单信息,这类信息具有高实时性的要求,我们应该确保提交成功。 - -### 复杂的数据交互类 - -复杂的数据交互是指,数据的编辑和筛选混合进行,例如对某个列表混合进行新增、编辑、删除和筛选,在这种情况下 alova 目前还无法很好地支持,在后续的版本中也将尝试解决这个难题。 - -## 技术方案 - -在无感数据交互的技术方案上,alova 分别实现了数据预拉取和静默提交,接下来我们来了解这两种技术方案。 - -:::info - -阅读前请确保已经掌握了以下章节内容 - -- [基础学习](/tutorial/getting-started) - -::: - -### 数据预拉取 - -在 html 中你可能见过这样的标签``,它告诉浏览器在闲时去预加载样式文件,并放在缓存中,当真正需要使用的时候从缓存中取出即可,alova 也使用了类似的方案,通过[useFetcher](/tutorial/advanced/use-fetcher)来预拉取需要的数据,它将保存在本地缓存中。你可以在任意情况下预判用户的需要阅读的内容,然后预先拉取对应的内容,如在流程类的页面中可以预加载下一页的内容,又或者,用户在某个按钮上停留了 200ms,我们可以预先拉取下一个界面需要的数据,这点类似于**Next.js**的页面预加载。 - -我们提供了一个[预加载的示例](/tutorial/example/prefetch),你可以进入体验。 - -### 静默提交 - -静默提交是一种提交即响应的机制,方案中将保证提交的完成,因此可以将它看作更安全的乐观更新方案。静默提交主要通过**静默队列**来持久化请求信息,以及保证请求时序问题,通过**虚拟数据**来作为服务端响应数据的占位符,当请求完成后替换为实际的响应数据,通过这两项技术实现了本地化的数据创建,并对新建数据进行编辑、删除等操作,即使创建的数据还未真正在服务端提交成功。为了让开发成本降到最低,这些在 alova 中都是自动完成的。 - -### 静默队列 - -静默队列用于保证请求时序问题,我们可以任意创建队列,进入队列的请求都将会以**SilentMethod**实例的形式保存在队列中,每个**SilentMethod**除了包含请求信息外,还包含静默提交的相关配置,如*唯一 id*、*错误重试参数*等。队列内的请求只有在前一个响应后才会发起下一个请求,从而保证队列内的请求时序。你可以将有依赖关系的请求放到同一个队列中,这样也可以保证数据的一致性。 - -![静默队列](https://user-images.githubusercontent.com/29848971/220057005-dd467392-4a43-45a7-90dc-999dd1d95531.png) - -在方案中,还分别提供了`queue`、`silent`、`static`三种行为模式,用于区分一个请求需要以怎样的行为进行请求。 - -- queue:请求将进入静默队列,但不会被持久化,将等待前面的请求完成才发送请求,响应回调将会在响应后触发,一般用于需要依赖前项请求的数据获取; -- silent:请求将进入静默队列,且会被持久化,然后立即触发响应回调,这种行为模式下,onSuccess 将接收到虚拟数据,onError 永远不会被触发,在进行提交即响应的场景下需使用此模式; -- static:请求不会进入静默队列,也不会被持久化,它会立即发出请求,在禁用静默提交时可使用此模式; - -### 虚拟数据 - -在提交即响应的机制中,虚拟数据起到了重要的作用,它表示在服务端真正响应之前,作为响应数据的替代数据进行占位,并通过追查机制,虚拟数据即使分布在应用各个位置,也能在响应后自动替换为实际的响应数据。同时在静默队列中也起到了重要作用,它可以标识队列内请求的依赖关系,并在依赖项响应后将依赖数据替换为实际数据,例如创建一条数据时将返回这条数据的 id,当服务端还未响应时,用户又进行了删除操作,需要将 id 作为删除标识,此时删除请求将依赖创建请求。在创建请求响应前,虚拟数据将作为 id 占位符作为删除的参数,并在创建请求响应后替换虚拟数据 id,至此就可以完成删除的请求。 - -![虚拟数据](https://user-images.githubusercontent.com/29848971/220005505-d30b7ae2-ddd0-4080-81a4-65c9be2cb0bd.png) - -接下来,我们将会具体了解虚拟数据的特性。 +--- +title: 无感数据交互 - 概览 +--- + +无感数据交互是指用户在与应用进行交互时,无需等待即可立即展示相关内容,或者提交信息时也无需等待即可展示操作结果,就像和本地数据交互一样,从而大幅提升应用的流畅性,它让用户感知不到数据传输带来的卡顿。 + +这并不是新鲜事,早在 2015 年以前就已经有乐观更新的概念了,它是指在服务端响应前将提交结果展示到界面中,它是建立在大部分的提交都成功的假设上,与之相对的是保守更新,即服务端响应前将展示等待状态,直到请求完成。在处理失败方面,目前的乐观更新方案通常是通过回退来处理,例如下面的示例代码: + +```javascript +const list = []; +const data = {}; +addTodo(data).catch(() => { + list = list.filter(item => item !== data); +}); +list.push(data); +``` + +这可能会导致以下问题: + +1. 回退将增加用户的理解成本和操作成本; +2. 请求时序问题; +3. 如果后续的请求存在对本次提交依赖,则本次失败将导致后续的请求变得没有意义; +4. 可能丢失请求; + +经过数个月的方案设计和不断迭代,alova 已经在这方面走出了一大步,在我们的方案中对以上存在的问题进行了解决,可以更稳定地保证请求成功,虽然还存在技术限制,但在许多场景中得到了应用。在我们的技术方案中,可以更高限度地降低网络波动带来的问题,你的应用在高延迟网络甚至是断网状态下依然可用,在刷新页面后也依然能保持最新状态的数据。 + +## 应用场景 + +无感数据交互虽然不能大规模地使用,但在一定场景下它又是非常合适的,在探索中,我们发现了至少包含但不限于以下几个场景,供你参考。 + +### 编辑器类应用 + +笔记类应用如印象笔记,画布编辑类应用如 MasterGO,它们分别需要实现以下需求: + +1. 进入笔记或图纸列表时将全量拉取列表数据,下次进入将使用本地的缓存数据; +2. 编辑过程中实时同步到服务端,且同步过程发生在后台,不会影响用户正常使用; +3. 网络差或断网状态下也可以继续使用; + +:::info 示例 + +我们提供了一个[笔记应用示例](/tutorial/example/silent-submit-notes),你可以进入体验。 + +::: + +### 设置模块 + +由常用的开关、选择器组成的设置模块,需实现的需求是,用户操作后实时同步到服务端,同时不再展示提交状态,而是直接展示操作后的最新状态。 + +:::info 示例 + +我们提供了一个[设置页示例](/tutorial/example/silent-submit-setting),你可以进入体验。 + +::: + +### 简单的列表管理 + +我们将创建列表项时填写的数据足够用于列表页的展示,称为简单的列表,例如一个学生列表页展示学生的姓名、性别、年级三个数据,这三个数据在创建学生时都需要填写。在简单列表中将实现以下需求: + +1. 在新增、编辑和删除列表项时立即在列表页中展示最新状态,无需在提交完成后才迟迟展示,同时也不受网络波动限制; +2. 刷新页面时,列表页始终保持最新状态; + +:::info 示例 + +我们提供了一个[简单列表页示例](/tutorial/example/silent-submit-simple-list),你可以进入体验。 + +::: + +### 复杂的列表管理 + +复杂列表是指,创建列表项时填写的数据不足以在列表页用于展示,而需要根据服务端的计算产生额外的数据,例如一个 Todo 列表页除了展示基本信息外,还需要列出具体的执行日期,而在创建页只指定了执行日期范围和相关规则,因此执行日期由服务端根据日期范围和规则统一计算生成。 + +在复杂列表中将实现以下需求: + +1. 在新增、编辑和删除列表项时立即在列表页中展示最新状态,并在服务端响应后将服务端计算的数据更新到此列表项中; +2. 刷新页面时,列表页始终保持最新状态; + +:::info 示例 + +复杂列表示例敬请期待... + +::: + +### 自由模式 + +在以上的几种场景下,你可能希望根据一个条件来判断当前是使用无感交互策略还是最常见的保守请求策略,需求如下: + +1. 当网络状态良好时,或付费用户将使用无感交互策略,而网络波动大时,或免费用户则不能享受到无感交互策略; +2. 策略自由切换; + +:::info 示例 + +在以上的示例中你都可以体验自由切换策略 + +::: + +## 不推荐的应用场景 + +### 信息共享类 + +提交的信息需要同步给其他人,例如订单信息,这类信息具有高实时性的要求,我们应该确保提交成功。 + +### 复杂的数据交互类 + +复杂的数据交互是指,数据的编辑和筛选混合进行,例如对某个列表混合进行新增、编辑、删除和筛选,在这种情况下 alova 目前还无法很好地支持,在后续的版本中也将尝试解决这个难题。 + +## 技术方案 + +在无感数据交互的技术方案上,alova 分别实现了数据预拉取和静默提交,接下来我们来了解这两种技术方案。 + +:::info + +阅读前请确保已经掌握了以下章节内容 + +- [基础学习](/tutorial/getting-started) + +::: + +### 数据预拉取 + +在 html 中你可能见过这样的标签``,它告诉浏览器在闲时去预加载样式文件,并放在缓存中,当真正需要使用的时候从缓存中取出即可,alova 也使用了类似的方案,通过[useFetcher](/tutorial/advanced/use-fetcher)来预拉取需要的数据,它将保存在本地缓存中。你可以在任意情况下预判用户的需要阅读的内容,然后预先拉取对应的内容,如在流程类的页面中可以预加载下一页的内容,又或者,用户在某个按钮上停留了 200ms,我们可以预先拉取下一个界面需要的数据,这点类似于**Next.js**的页面预加载。 + +我们提供了一个[预加载的示例](/tutorial/example/prefetch),你可以进入体验。 + +### 静默提交 + +静默提交是一种提交即响应的机制,方案中将保证提交的完成,因此可以将它看作更安全的乐观更新方案。静默提交主要通过**静默队列**来持久化请求信息,以及保证请求时序问题,通过**虚拟数据**来作为服务端响应数据的占位符,当请求完成后替换为实际的响应数据,通过这两项技术实现了本地化的数据创建,并对新建数据进行编辑、删除等操作,即使创建的数据还未真正在服务端提交成功。为了让开发成本降到最低,这些在 alova 中都是自动完成的。 + +### 静默队列 + +静默队列用于保证请求时序问题,我们可以任意创建队列,进入队列的请求都将会以**SilentMethod**实例的形式保存在队列中,每个**SilentMethod**除了包含请求信息外,还包含静默提交的相关配置,如*唯一 id*、*错误重试参数*等。队列内的请求只有在前一个响应后才会发起下一个请求,从而保证队列内的请求时序。你可以将有依赖关系的请求放到同一个队列中,这样也可以保证数据的一致性。 + +![静默队列](https://user-images.githubusercontent.com/29848971/220057005-dd467392-4a43-45a7-90dc-999dd1d95531.png) + +在方案中,还分别提供了`queue`、`silent`、`static`三种行为模式,用于区分一个请求需要以怎样的行为进行请求。 + +- queue:请求将进入静默队列,但不会被持久化,将等待前面的请求完成才发送请求,响应回调将会在响应后触发,一般用于需要依赖前项请求的数据获取; +- silent:请求将进入静默队列,且会被持久化,然后立即触发响应回调,这种行为模式下,onSuccess 将接收到虚拟数据,onError 永远不会被触发,在进行提交即响应的场景下需使用此模式; +- static:请求不会进入静默队列,也不会被持久化,它会立即发出请求,在禁用静默提交时可使用此模式; + +### 虚拟数据 + +在提交即响应的机制中,虚拟数据起到了重要的作用,它表示在服务端真正响应之前,作为响应数据的替代数据进行占位,并通过追查机制,虚拟数据即使分布在应用各个位置,也能在响应后自动替换为实际的响应数据。同时在静默队列中也起到了重要作用,它可以标识队列内请求的依赖关系,并在依赖项响应后将依赖数据替换为实际数据,例如创建一条数据时将返回这条数据的 id,当服务端还未响应时,用户又进行了删除操作,需要将 id 作为删除标识,此时删除请求将依赖创建请求。在创建请求响应前,虚拟数据将作为 id 占位符作为删除的参数,并在创建请求响应后替换虚拟数据 id,至此就可以完成删除的请求。 + +![虚拟数据](https://user-images.githubusercontent.com/29848971/220005505-d30b7ae2-ddd0-4080-81a4-65c9be2cb0bd.png) + +接下来,我们将会具体了解虚拟数据的特性。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/_category_.json b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/_category_.json index 68d7d2cf7..76152ffa6 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/_category_.json +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/_category_.json @@ -1,3 +1,3 @@ -{ - "label": "无感数据交互" -} +{ + "label": "无感数据交互" +} diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/_category_.json b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/_category_.json index 7d893e035..5d5382d2d 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/_category_.json +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/05-strategy/_category_.json @@ -1,3 +1,3 @@ -{ - "label": "请求策略" -} +{ + "label": "请求策略" +} diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/_category_.json b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/_category_.json index 3462df72e..aa8953a08 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/_category_.json +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/06-advanced/_category_.json @@ -1,3 +1,3 @@ -{ - "label": "Advanced" -} +{ + "label": "Advanced" +} diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/_category_.json b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/_category_.json index b2734b673..da7e58a8e 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/_category_.json +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/08-request-adapter/_category_.json @@ -1,6 +1,6 @@ -{ - "label": "扩展", - "link": { - "type": "generated-index" - } -} +{ + "label": "扩展", + "link": { + "type": "generated-index" + } +} diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/10-custom/_category_.json b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/10-custom/_category_.json index 505fadaab..6298d8ec1 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/10-custom/_category_.json +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/10-custom/_category_.json @@ -1,6 +1,6 @@ -{ - "label": "Custom", - "link": { - "type": "generated-index" - } -} +{ + "label": "Custom", + "link": { + "type": "generated-index" + } +} diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/_category_.json b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/_category_.json index 352959533..d80adabc2 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/_category_.json +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.x/tutorial/11-others/_category_.json @@ -1,6 +1,6 @@ -{ - "label": "其他", - "link": { - "type": "generated-index" - } -} +{ + "label": "其他", + "link": { + "type": "generated-index" + } +} diff --git a/package.json b/package.json index 5d8d694da..1eb98b050 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,8 @@ "serve": "docusaurus serve", "write-translations": "docusaurus write-translations", "write-heading-ids": "docusaurus write-heading-ids", - "typecheck": "tsc" + "typecheck": "tsc", + "format:fix": "prettier --write ." }, "dependencies": { "@codesandbox/sandpack-react": "^2.9.0", diff --git a/src/components/AdCard/index.tsx b/src/components/AdCard/index.tsx index 55190f429..ea7b4e2b9 100644 --- a/src/components/AdCard/index.tsx +++ b/src/components/AdCard/index.tsx @@ -1,19 +1,19 @@ -import clsx from 'clsx'; -import React, { FC, ReactNode } from 'react'; -import styles from './styles.module.css'; - -const AdCard: FC<{ img: string; children: ReactNode; className: string; href: string }> = ({ - img, - children, - className, - href -}) => ( - - - {children} - -); -export default AdCard; +import clsx from 'clsx'; +import React, { FC, ReactNode } from 'react'; +import styles from './styles.module.css'; + +const AdCard: FC<{ img: string; children: ReactNode; className: string; href: string }> = ({ + img, + children, + className, + href +}) => ( + + + {children} + +); +export default AdCard; diff --git a/src/components/AdCard/styles.module.css b/src/components/AdCard/styles.module.css index 37a9c3fba..828d285ff 100644 --- a/src/components/AdCard/styles.module.css +++ b/src/components/AdCard/styles.module.css @@ -1,14 +1,14 @@ -.wrapper { - background: var(--ifm-color-emphasis-200); - border-radius: 0.33rem; - font-size: 0.8rem; - display: block; - color: var(--ifm-font-color-base); - width: 200px; -} -.wrapper:hover { - color: var(--ifm-font-color-base); -} -.wrapper img { - border-radius: 0.33rem; -} +.wrapper { + background: var(--ifm-color-emphasis-200); + border-radius: 0.33rem; + font-size: 0.8rem; + display: block; + color: var(--ifm-font-color-base); + width: 200px; +} +.wrapper:hover { + color: var(--ifm-font-color-base); +} +.wrapper img { + border-radius: 0.33rem; +} diff --git a/src/components/EmbedCodesandbox.tsx b/src/components/EmbedCodesandbox.tsx index 4d6f0090c..ab1d6704f 100644 --- a/src/components/EmbedCodesandbox.tsx +++ b/src/components/EmbedCodesandbox.tsx @@ -1,22 +1,24 @@ -import Link from '@docusaurus/Link'; -import Translate from '@docusaurus/Translate'; - -const EmbedCodesandbox = ({ src }) => ( -
- - - Encounter troubles? Click to open this in new page. - -
-); -export default EmbedCodesandbox; +import Link from '@docusaurus/Link'; +import Translate from '@docusaurus/Translate'; + +const EmbedCodesandbox = ({ src }) => ( +
+ + + + Encounter troubles? Click to open this in new page. + + +
+); +export default EmbedCodesandbox; diff --git a/src/components/EmbedSandpack.tsx b/src/components/EmbedSandpack.tsx index ad0b37bb2..9648b0eb7 100644 --- a/src/components/EmbedSandpack.tsx +++ b/src/components/EmbedSandpack.tsx @@ -1,152 +1,152 @@ -import alovaReact from '!!raw-loader!@site/codesandbox/00-create-alova/react'; -import alovaSvelte from '!!raw-loader!@site/codesandbox/00-create-alova/svelte'; -import alovaVueComposition from '!!raw-loader!@site/codesandbox/00-create-alova/vueComposition'; -import alovaVueOptions from '!!raw-loader!@site/codesandbox/00-create-alova/vueOptions'; -import { Sandpack } from '@codesandbox/sandpack-react'; -import { SandpackPredefinedTemplate } from '@codesandbox/sandpack-react/unstyled'; -import { amethyst, monokaiPro } from '@codesandbox/sandpack-themes'; -import { useColorMode } from '@docusaurus/theme-common'; - -const fileEntry = { - vue: { - root: '/src/App.vue', - files: { - '/src/api.js': alovaVueComposition - } - }, - 'vue-options': { - root: '/src/App.vue', - files: { - '/src/api.js': alovaVueOptions - }, - deps: { - '@alova/vue-options': 'latest' - } - }, - react: { - root: '/App.js', - files: { - '/api.js': alovaReact - } - }, - svelte: { - root: '/App.svelte', - files: { - '/api.js': alovaSvelte - } - }, - static: { - root: '/index.html' - }, - vanilla: { - root: '/index.js' - } -}; -const customSetup = { - svelte: (commonConfig: Record) => ({ - files: { - '/index.js': { - code: `import App from "./App.svelte"; - const app = new App({ - target: document.body - }); - export default app; - `, - hidden: true - }, - '/public/index.html': { - code: ` - - - - - Svelte app - - - - - - `, - hidden: true - }, - ...commonConfig.files - }, - customSetup: { - entry: '/index.js', - dependencies: { - svelte: '^3.59.2', - ...commonConfig.customSetup.dependencies - } - }, - main: '/App.svelte', - environment: 'svelte' - }) -}; - -interface Props { - template: SandpackPredefinedTemplate; - mainFile: string; - externalFiles?: Record; - containBaseURL?: boolean; - containResponded?: boolean; - editorHeight?: number; - style?: 'options'; -} -const EmbedSandpack = ({ - template, - mainFile, - externalFiles = {}, - containBaseURL = true, - containResponded = true, - editorHeight, - style -}: Props) => { - const themes = { - light: amethyst, - dark: monokaiPro - }; - const targetEntry = fileEntry[template + (style ? `-${style}` : '')]; - const files = { - [targetEntry.root]: mainFile, - ...(targetEntry.files ? targetEntry.files : {}), - ...externalFiles - }; - - const apiFileKey = Object.keys(files).find(file => /api\.js$/.test(file)); - if (files[apiFileKey]) { - // if don't need to contain baseURL, remove it. - if (!containBaseURL) { - files[apiFileKey] = files[apiFileKey].replace(/baseURL.+?\s{4}/, ''); - } - // if don't need to contain responded, remove it. - if (!containResponded) { - files[apiFileKey] = files[apiFileKey].replace(/,\s+responded.+json\(\)/, ''); - } - } - - const dependencies = { - alova: 'latest', - ...(targetEntry.deps || {}) - }; - const { colorMode } = useColorMode(); - let config = { - files, - template, - customSetup: { - dependencies - } - }; - config = customSetup[template] ? customSetup[template](config) : config; - return ( - - ); -}; - -export default EmbedSandpack; +import alovaReact from '!!raw-loader!@site/codesandbox/00-create-alova/react'; +import alovaSvelte from '!!raw-loader!@site/codesandbox/00-create-alova/svelte'; +import alovaVueComposition from '!!raw-loader!@site/codesandbox/00-create-alova/vueComposition'; +import alovaVueOptions from '!!raw-loader!@site/codesandbox/00-create-alova/vueOptions'; +import { Sandpack } from '@codesandbox/sandpack-react'; +import { SandpackPredefinedTemplate } from '@codesandbox/sandpack-react/unstyled'; +import { amethyst, monokaiPro } from '@codesandbox/sandpack-themes'; +import { useColorMode } from '@docusaurus/theme-common'; + +const fileEntry = { + vue: { + root: '/src/App.vue', + files: { + '/src/api.js': alovaVueComposition + } + }, + 'vue-options': { + root: '/src/App.vue', + files: { + '/src/api.js': alovaVueOptions + }, + deps: { + '@alova/vue-options': 'latest' + } + }, + react: { + root: '/App.js', + files: { + '/api.js': alovaReact + } + }, + svelte: { + root: '/App.svelte', + files: { + '/api.js': alovaSvelte + } + }, + static: { + root: '/index.html' + }, + vanilla: { + root: '/index.js' + } +}; +const customSetup = { + svelte: (commonConfig: Record) => ({ + files: { + '/index.js': { + code: `import App from "./App.svelte"; + const app = new App({ + target: document.body + }); + export default app; + `, + hidden: true + }, + '/public/index.html': { + code: ` + + + + + Svelte app + + + + + + `, + hidden: true + }, + ...commonConfig.files + }, + customSetup: { + entry: '/index.js', + dependencies: { + svelte: '^3.59.2', + ...commonConfig.customSetup.dependencies + } + }, + main: '/App.svelte', + environment: 'svelte' + }) +}; + +interface Props { + template: SandpackPredefinedTemplate; + mainFile: string; + externalFiles?: Record; + containBaseURL?: boolean; + containResponded?: boolean; + editorHeight?: number; + style?: 'options'; +} +const EmbedSandpack = ({ + template, + mainFile, + externalFiles = {}, + containBaseURL = true, + containResponded = true, + editorHeight, + style +}: Props) => { + const themes = { + light: amethyst, + dark: monokaiPro + }; + const targetEntry = fileEntry[template + (style ? `-${style}` : '')]; + const files = { + [targetEntry.root]: mainFile, + ...(targetEntry.files ? targetEntry.files : {}), + ...externalFiles + }; + + const apiFileKey = Object.keys(files).find(file => /api\.js$/.test(file)); + if (files[apiFileKey]) { + // if don't need to contain baseURL, remove it. + if (!containBaseURL) { + files[apiFileKey] = files[apiFileKey].replace(/baseURL.+?\s{4}/, ''); + } + // if don't need to contain responded, remove it. + if (!containResponded) { + files[apiFileKey] = files[apiFileKey].replace(/,\s+responded.+json\(\)/, ''); + } + } + + const dependencies = { + alova: 'latest', + ...(targetEntry.deps || {}) + }; + const { colorMode } = useColorMode(); + let config = { + files, + template, + customSetup: { + dependencies + } + }; + config = customSetup[template] ? customSetup[template](config) : config; + return ( + + ); +}; + +export default EmbedSandpack; diff --git a/src/components/IconFont.tsx b/src/components/IconFont.tsx index f3c8738d2..504d66b39 100644 --- a/src/components/IconFont.tsx +++ b/src/components/IconFont.tsx @@ -1,27 +1,34 @@ -import React from 'react'; - -interface IconFontProps { - name: string; - className?: string; - style?: React.CSSProperties; - width?: number | string; - height?: number | string; - size?: number | string; -} -export default function IconFont({ className, width, size = 20, name, height, style }: IconFontProps) { - return ( - - ); -} +import React from 'react'; + +interface IconFontProps { + name: string; + className?: string; + style?: React.CSSProperties; + width?: number | string; + height?: number | string; + size?: number | string; +} +export default function IconFont({ + className, + width, + size = 20, + name, + height, + style +}: IconFontProps) { + return ( + + ); +} diff --git a/src/components/NavCard/index.tsx b/src/components/NavCard/index.tsx index 99beea57d..0aaae021f 100644 --- a/src/components/NavCard/index.tsx +++ b/src/components/NavCard/index.tsx @@ -1,41 +1,41 @@ -import Link from '@docusaurus/Link'; -import IconFont from '@site/src/components/IconFont'; -import clsx from 'clsx'; -import styles from './style.module.css'; - -interface Card { - Image: React.JSX.Element; - title: string; - desc?: string; - link: string; - target?: string; -} -interface IProps { - list: Card[]; -} -const NavCard = ({ list }: IProps) => { - return ( -
- {list.map(({ Image, target, title, desc, link }) => ( - -
-
- {Image ?
{Image}
: null} - {title} -
- -
- {desc ? {desc} : null} - - ))} -
- ); -}; - -export default NavCard; +import Link from '@docusaurus/Link'; +import IconFont from '@site/src/components/IconFont'; +import clsx from 'clsx'; +import styles from './style.module.css'; + +interface Card { + Image: React.JSX.Element; + title: string; + desc?: string; + link: string; + target?: string; +} +interface IProps { + list: Card[]; +} +const NavCard = ({ list }: IProps) => { + return ( +
+ {list.map(({ Image, target, title, desc, link }) => ( + +
+
+ {Image ?
{Image}
: null} + {title} +
+ +
+ {desc ? {desc} : null} + + ))} +
+ ); +}; + +export default NavCard; diff --git a/src/components/NavCard/style.module.css b/src/components/NavCard/style.module.css index 2bf520cbe..f36e65c2e 100644 --- a/src/components/NavCard/style.module.css +++ b/src/components/NavCard/style.module.css @@ -1,56 +1,56 @@ -.cardWrapper { - display: flex; -} -.cardItem { - display: flex; - flex-direction: column; - padding: 1.4rem 2rem; - border-radius: 6px; - border: solid 1px var(--ifm-color-emphasis-200); - background: var(--ifm-color-emphasis-100); - text-decoration: none; - transition: all 0.3s; - flex: 1; -} -.cardItem:hover { - text-decoration: none; - border-color: transparent; - background: transparent; - box-shadow: 2px 2px 8px var(--ifm-color-emphasis-300); -} -.cardItem:not(:last-child) { - margin-right: 1rem; -} -.cardTitle { - display: flex; - align-items: center; - justify-content: space-between; -} -.cardTitle, -.cardTitle strong { - color: var(--ifm-color-emphasis-900); -} -.cardDesc { - margin-top: 0.6rem; - font-size: 0.9rem; - color: var(--ifm-color-emphasis-600); -} -.image { - width: 1.8rem; - margin-right: 0.6rem; -} -.image svg { - width: 100%; - height: auto; - display: block; -} - -@media screen and (max-width: 996px) { - .cardWrapper { - flex-direction: column; - } - .cardItem:not(:last-child) { - margin-bottom: 1rem; - margin-right: 0; - } -} +.cardWrapper { + display: flex; +} +.cardItem { + display: flex; + flex-direction: column; + padding: 1.4rem 2rem; + border-radius: 6px; + border: solid 1px var(--ifm-color-emphasis-200); + background: var(--ifm-color-emphasis-100); + text-decoration: none; + transition: all 0.3s; + flex: 1; +} +.cardItem:hover { + text-decoration: none; + border-color: transparent; + background: transparent; + box-shadow: 2px 2px 8px var(--ifm-color-emphasis-300); +} +.cardItem:not(:last-child) { + margin-right: 1rem; +} +.cardTitle { + display: flex; + align-items: center; + justify-content: space-between; +} +.cardTitle, +.cardTitle strong { + color: var(--ifm-color-emphasis-900); +} +.cardDesc { + margin-top: 0.6rem; + font-size: 0.9rem; + color: var(--ifm-color-emphasis-600); +} +.image { + width: 1.8rem; + margin-right: 0.6rem; +} +.image svg { + width: 100%; + height: auto; + display: block; +} + +@media screen and (max-width: 996px) { + .cardWrapper { + flex-direction: column; + } + .cardItem:not(:last-child) { + margin-bottom: 1rem; + margin-right: 0; + } +} diff --git a/src/components/PageModule/index.module.css b/src/components/PageModule/index.module.css index 897fbd3b6..1f82731f7 100644 --- a/src/components/PageModule/index.module.css +++ b/src/components/PageModule/index.module.css @@ -1,40 +1,40 @@ -.line { - height: 6rem; - border-left: solid 1px var(--ifm-color-primary); -} -.decorator { - width: 2.6rem; - height: 2.6rem; - border-radius: 2.6rem; - color: var(--ifm-color-primary); - background: rgba(var(--ifm-color-primary-rgb), 0.1); - display: flex; - align-items: center; - justify-content: center; -} - -.wrapper { - margin: 0 auto; - padding-top: 4rem; - padding-bottom: 2rem; -} -.title { - font-size: var(--ifm-h1-font-size); - margin-bottom: 0; -} -.subtitle { - color: var(--ifm-color-emphasis-600); -} -.content { - margin-top: 2rem; -} - -@media screen and (max-width: 996px) { - .title { - font-size: var(--ifm-h2-font-size); - } - .wrapper { - padding-top: 3rem; - padding-bottom: 3rem; - } -} +.line { + height: 6rem; + border-left: solid 1px var(--ifm-color-primary); +} +.decorator { + width: 2.6rem; + height: 2.6rem; + border-radius: 2.6rem; + color: var(--ifm-color-primary); + background: rgba(var(--ifm-color-primary-rgb), 0.1); + display: flex; + align-items: center; + justify-content: center; +} + +.wrapper { + margin: 0 auto; + padding-top: 4rem; + padding-bottom: 2rem; +} +.title { + font-size: var(--ifm-h1-font-size); + margin-bottom: 0; +} +.subtitle { + color: var(--ifm-color-emphasis-600); +} +.content { + margin-top: 2rem; +} + +@media screen and (max-width: 996px) { + .title { + font-size: var(--ifm-h2-font-size); + } + .wrapper { + padding-top: 3rem; + padding-bottom: 3rem; + } +} diff --git a/src/components/PageModule/index.tsx b/src/components/PageModule/index.tsx index 9943afa34..e701a9853 100644 --- a/src/components/PageModule/index.tsx +++ b/src/components/PageModule/index.tsx @@ -1,43 +1,55 @@ -import Translate from '@docusaurus/Translate'; -import clsx from 'clsx'; -import React, { ReactNode } from 'react'; -import IconFont from '../IconFont'; -import styles from './index.module.css'; - -interface TitleProps { - text: string; - textTransId?: string; - desc?: string; - descTransId?: string; - children?: ReactNode; - className?: string; - align?: 'center'; -} -export default function PageModule({ text, desc, textTransId, descTransId, children, className, align }: TitleProps) { - return ( -
-
-
-
- -
-

- {text} -

-
- {desc ? ( -

- {desc} -

- ) : null} -
{children}
-
-
- ); -} +import Translate from '@docusaurus/Translate'; +import clsx from 'clsx'; +import React, { ReactNode } from 'react'; +import IconFont from '../IconFont'; +import styles from './index.module.css'; + +interface TitleProps { + text: string; + textTransId?: string; + desc?: string; + descTransId?: string; + children?: ReactNode; + className?: string; + align?: 'center'; +} +export default function PageModule({ + text, + desc, + textTransId, + descTransId, + children, + className, + align +}: TitleProps) { + return ( +
+
+
+
+ +
+

+ {text} +

+
+ {desc ? ( +

+ {desc} +

+ ) : null} +
{children}
+
+
+ ); +} diff --git a/src/components/SupportList/index.tsx b/src/components/SupportList/index.tsx index 99762d176..1d2b85be4 100644 --- a/src/components/SupportList/index.tsx +++ b/src/components/SupportList/index.tsx @@ -1,207 +1,212 @@ -import Link from '@docusaurus/Link'; -import IconFont from '@site/src/components/IconFont'; -import clsx from 'clsx'; -import React from 'react'; -import styles from './style.module.css'; - -type SupportItem = { - id: string; - available: boolean; - Image: React.ComponentType>; - link: string; -}; - -const jsEnvList: SupportItem[] = [ - { - id: 'Vue3', - Image: require('@site/static/img/vue.svg').default, - available: true, - link: 'https://vuejs.org' - }, - { - id: 'React', - available: true, - Image: require('@site/static/img/react.svg').default, - link: 'https://react.dev/' - }, - { - id: 'Svelte', - available: true, - Image: require('@site/static/img/svelte.svg').default, - link: 'https://svelte.dev' - }, - { - id: 'Vue options', - available: true, - Image: require('@site/static/img/vue.svg').default, - link: '/tutorial/framework/vue-options' - }, - { - id: 'Next', - available: true, - Image: require('@site/static/img/next.svg').default, - link: '/tutorial/advanced/ssr#nextjs' - }, - { - id: 'Nuxt', - available: true, - Image: require('@site/static/img/nuxt.svg').default, - link: '/tutorial/advanced/ssr#nuxt3x' - }, - { - id: 'nodejs', - available: true, - Image: require('@site/static/img/nodejs.svg').default, - link: 'https://nodejs.org' - }, - { - id: 'bun', - available: true, - Image: require('@site/static/img/bun.svg').default, - link: 'https://bun.sh' - }, - { - id: 'deno', - available: true, - Image: require('@site/static/img/deno.svg').default, - link: 'https://deno.com' - }, - { - id: 'Sveltekit', - available: true, - Image: require('@site/static/img/svelte.svg').default, - link: '/tutorial/advanced/ssr#sveltekit' - }, - { - id: 'Solid', - available: false, - Image: require('@site/static/img/solid.svg').default, - link: '/tutorial/framework/solid' - }, - { - id: 'Mini program🇨🇳', - available: false, - Image: require('@site/static/img/miniprogram.svg').default, - link: '/tutorial/framework/native-mp' - }, - { - id: 'uniapp', - available: true, - Image: ({ className }) => ( - - ), - link: '/tutorial/request-adapter/alova-adapter-uniapp' - }, - { - id: 'Taro', - available: true, - Image: ({ className }) => ( - - ), - link: '/tutorial/request-adapter/alova-adapter-taro' - }, - { - id: 'Angular', - available: false, - Image: require('@site/static/img/angular.svg').default, - link: '/tutorial/framework/angular' - }, - { - id: 'Preact', - available: false, - Image: require('@site/static/img/preact.svg').default, - link: '/tutorial/framework/preact' - }, - { - id: 'Qwik', - available: false, - Image: require('@site/static/img/qwik.svg').default, - link: '/tutorial/framework/qwik' - }, - { - id: 'Lit', - available: false, - Image: require('@site/static/img/lit.svg').default, - link: '/tutorial/framework/lit' - }, - { - id: 'Stencil', - available: false, - Image: require('@site/static/img/stencil.svg').default, - link: '/tutorial/framework/stencil' - } -]; - -const requestTools: SupportItem[] = [ - { - id: 'Fetch Api', - available: true, - Image: require('@site/static/img/fetchapi.svg').default, - link: '/tutorial/getting-started/method/#other-parameters-supported-by-the-request-adapter' - }, - { - id: 'Axios', - available: true, - Image: require('@site/static/img/axios.svg').default, - link: '/tutorial/request-adapter/alova-adapter-axios' - }, - { - id: 'XMLHttpRequest', - available: true, - Image: require('@site/static/img/xhr.svg').default, - link: '/tutorial/request-adapter/alova-adapter-xhr' - }, - { - id: 'GraphQL', - available: false, - Image: require('@site/static/img/graphql.svg').default, - link: 'https://graphql.org/' - }, - { - id: 'SuperAgent', - available: false, - Image: require('@site/static/img/superagent.svg').default, - link: 'https://github.com/ladjs/superagent' - } -]; - -interface Props { - showStatus?: boolean; -} -export default function Support({ showStatus = false }: Props): JSX.Element { - const ListView = (list: SupportItem[], colSpan = 7) => ( -
- {list.map(({ id, Image, link, available }) => ( - - - {id} - - ))} -
- ); - - return ( -
- {ListView(jsEnvList)} -
- -
- {ListView(requestTools, 4)} -
- ); -} +import Link from '@docusaurus/Link'; +import IconFont from '@site/src/components/IconFont'; +import clsx from 'clsx'; +import React from 'react'; +import styles from './style.module.css'; + +type SupportItem = { + id: string; + available: boolean; + Image: React.ComponentType>; + link: string; +}; + +const jsEnvList: SupportItem[] = [ + { + id: 'Vue3', + Image: require('@site/static/img/vue.svg').default, + available: true, + link: 'https://vuejs.org' + }, + { + id: 'React', + available: true, + Image: require('@site/static/img/react.svg').default, + link: 'https://react.dev/' + }, + { + id: 'Svelte', + available: true, + Image: require('@site/static/img/svelte.svg').default, + link: 'https://svelte.dev' + }, + { + id: 'Vue options', + available: true, + Image: require('@site/static/img/vue.svg').default, + link: '/tutorial/framework/vue-options' + }, + { + id: 'Next', + available: true, + Image: require('@site/static/img/next.svg').default, + link: '/tutorial/advanced/ssr#nextjs' + }, + { + id: 'Nuxt', + available: true, + Image: require('@site/static/img/nuxt.svg').default, + link: '/tutorial/advanced/ssr#nuxt3x' + }, + { + id: 'nodejs', + available: true, + Image: require('@site/static/img/nodejs.svg').default, + link: 'https://nodejs.org' + }, + { + id: 'bun', + available: true, + Image: require('@site/static/img/bun.svg').default, + link: 'https://bun.sh' + }, + { + id: 'deno', + available: true, + Image: require('@site/static/img/deno.svg').default, + link: 'https://deno.com' + }, + { + id: 'Sveltekit', + available: true, + Image: require('@site/static/img/svelte.svg').default, + link: '/tutorial/advanced/ssr#sveltekit' + }, + { + id: 'Solid', + available: false, + Image: require('@site/static/img/solid.svg').default, + link: '/tutorial/framework/solid' + }, + { + id: 'Mini program🇨🇳', + available: false, + Image: require('@site/static/img/miniprogram.svg').default, + link: '/tutorial/framework/native-mp' + }, + { + id: 'uniapp', + available: true, + Image: ({ className }) => ( + + ), + link: '/tutorial/request-adapter/alova-adapter-uniapp' + }, + { + id: 'Taro', + available: true, + Image: ({ className }) => ( + + ), + link: '/tutorial/request-adapter/alova-adapter-taro' + }, + { + id: 'Angular', + available: false, + Image: require('@site/static/img/angular.svg').default, + link: '/tutorial/framework/angular' + }, + { + id: 'Preact', + available: false, + Image: require('@site/static/img/preact.svg').default, + link: '/tutorial/framework/preact' + }, + { + id: 'Qwik', + available: false, + Image: require('@site/static/img/qwik.svg').default, + link: '/tutorial/framework/qwik' + }, + { + id: 'Lit', + available: false, + Image: require('@site/static/img/lit.svg').default, + link: '/tutorial/framework/lit' + }, + { + id: 'Stencil', + available: false, + Image: require('@site/static/img/stencil.svg').default, + link: '/tutorial/framework/stencil' + } +]; + +const requestTools: SupportItem[] = [ + { + id: 'Fetch Api', + available: true, + Image: require('@site/static/img/fetchapi.svg').default, + link: '/tutorial/getting-started/method/#other-parameters-supported-by-the-request-adapter' + }, + { + id: 'Axios', + available: true, + Image: require('@site/static/img/axios.svg').default, + link: '/tutorial/request-adapter/alova-adapter-axios' + }, + { + id: 'XMLHttpRequest', + available: true, + Image: require('@site/static/img/xhr.svg').default, + link: '/tutorial/request-adapter/alova-adapter-xhr' + }, + { + id: 'GraphQL', + available: false, + Image: require('@site/static/img/graphql.svg').default, + link: 'https://graphql.org/' + }, + { + id: 'SuperAgent', + available: false, + Image: require('@site/static/img/superagent.svg').default, + link: 'https://github.com/ladjs/superagent' + } +]; + +interface Props { + showStatus?: boolean; +} +export default function Support({ showStatus = false }: Props): JSX.Element { + const ListView = (list: SupportItem[], colSpan = 7) => ( +
+ {list.map(({ id, Image, link, available }) => ( + + + {id} + + ))} +
+ ); + + return ( +
+ {ListView(jsEnvList)} +
+ +
+ {ListView(requestTools, 4)} +
+ ); +} diff --git a/src/components/SupportList/style.module.css b/src/components/SupportList/style.module.css index 6222b7d0d..02d59307d 100644 --- a/src/components/SupportList/style.module.css +++ b/src/components/SupportList/style.module.css @@ -1,56 +1,56 @@ -.wrapper { - --ifm-spacing-horizontal: 0; -} -.framework { - padding: 0.6rem 1rem; - border: solid 1px var(--ifm-color-emphasis-300); - border-radius: 0.5rem; - transition: all 0.3s; - font-size: 0.9rem; - color: var(--ifm-font-color-secondary); - margin-right: 0.8rem; - margin-bottom: 0.8rem; - background: var(--ifm-color-emphasis-0); - text-decoration: none; -} -[data-theme='dark'] .framework { - background: var(--ifm-color-emphasis-200); -} -.unavailableWrapper { - background: var(--ifm-color-emphasis-200) !important; -} -[data-theme='dark'] .unavailableWrapper { - background: var(--ifm-color-emphasis-100) !important; -} - -.framework:hover { - text-decoration: none; - color: var(--ifm-font-color-secondary); - transform: scale(1.1); - border-color: transparent; - box-shadow: 1px 1px 18px var(--ifm-color-emphasis-200); -} -.icon { - width: auto; - height: 1.6rem; - padding: 0 1rem; - transition: all 0.3s; - margin-bottom: 0.2rem; -} -.iconUnavailable { - filter: grayscale(100%); -} - -@media screen and (max-width: 996px) { - .wrapper { - width: 100%; - } - .icon { - height: 1.4rem; - padding: 0 1rem; - } - .iconPlus { - text-align: center; - margin-bottom: 0.8rem; - } -} +.wrapper { + --ifm-spacing-horizontal: 0; +} +.framework { + padding: 0.6rem 1rem; + border: solid 1px var(--ifm-color-emphasis-300); + border-radius: 0.5rem; + transition: all 0.3s; + font-size: 0.9rem; + color: var(--ifm-font-color-secondary); + margin-right: 0.8rem; + margin-bottom: 0.8rem; + background: var(--ifm-color-emphasis-0); + text-decoration: none; +} +[data-theme='dark'] .framework { + background: var(--ifm-color-emphasis-200); +} +.unavailableWrapper { + background: var(--ifm-color-emphasis-200) !important; +} +[data-theme='dark'] .unavailableWrapper { + background: var(--ifm-color-emphasis-100) !important; +} + +.framework:hover { + text-decoration: none; + color: var(--ifm-font-color-secondary); + transform: scale(1.1); + border-color: transparent; + box-shadow: 1px 1px 18px var(--ifm-color-emphasis-200); +} +.icon { + width: auto; + height: 1.6rem; + padding: 0 1rem; + transition: all 0.3s; + margin-bottom: 0.2rem; +} +.iconUnavailable { + filter: grayscale(100%); +} + +@media screen and (max-width: 996px) { + .wrapper { + width: 100%; + } + .icon { + height: 1.4rem; + padding: 0 1rem; + } + .iconPlus { + text-align: center; + margin-bottom: 0.8rem; + } +} diff --git a/src/pages/_indexComponent/Contributors/index.module.css b/src/pages/_indexComponent/Contributors/index.module.css index 2ec9975df..f9526615b 100644 --- a/src/pages/_indexComponent/Contributors/index.module.css +++ b/src/pages/_indexComponent/Contributors/index.module.css @@ -1,25 +1,25 @@ -.contributorsWrapper { - display: flex; - flex-direction: column; - align-items: center; - box-shadow: 2px 24px 70px var(--ifm-color-emphasis-300); - width: 60%; - margin: 0 auto; - padding: 4rem; - padding-top: 0rem; - border-radius: 1rem; -} -.rewards { - width: 7rem; - height: auto; - position: relative; - top: -2rem; -} - -@media screen and (max-width: 996px) { - .contributorsWrapper { - width: 100%; - padding: 2rem; - padding-top: 0rem; - } -} +.contributorsWrapper { + display: flex; + flex-direction: column; + align-items: center; + box-shadow: 2px 24px 70px var(--ifm-color-emphasis-300); + width: 60%; + margin: 0 auto; + padding: 4rem; + padding-top: 0rem; + border-radius: 1rem; +} +.rewards { + width: 7rem; + height: auto; + position: relative; + top: -2rem; +} + +@media screen and (max-width: 996px) { + .contributorsWrapper { + width: 100%; + padding: 2rem; + padding-top: 0rem; + } +} diff --git a/src/pages/_indexComponent/Contributors/index.tsx b/src/pages/_indexComponent/Contributors/index.tsx index 00f7ce302..48802afff 100644 --- a/src/pages/_indexComponent/Contributors/index.tsx +++ b/src/pages/_indexComponent/Contributors/index.tsx @@ -1,23 +1,23 @@ -import PageModule from '@site/src/components/PageModule'; -import ImgReward from '@site/static/img/reward.svg'; -import styles from './index.module.css'; - -export default function Like(): JSX.Element { - return ( - -
- - - - -
-
- ); -} +import PageModule from '@site/src/components/PageModule'; +import ImgReward from '@site/static/img/reward.svg'; +import styles from './index.module.css'; + +export default function Like(): JSX.Element { + return ( + +
+ + + + +
+
+ ); +} diff --git a/src/pages/_indexComponent/Features/index.tsx b/src/pages/_indexComponent/Features/index.tsx index 9e1eb3314..1d83740e4 100644 --- a/src/pages/_indexComponent/Features/index.tsx +++ b/src/pages/_indexComponent/Features/index.tsx @@ -1,86 +1,94 @@ -import Translate from '@docusaurus/Translate'; -import IconFont from '@site/src/components/IconFont'; -import clsx from 'clsx'; -import React from 'react'; -import styles from './styles.module.css'; - -type FeatureItem = { - title: JSX.Element; - icon: string; - description: JSX.Element; -}; - -const FeatureList: FeatureItem[] = [ - { - title: ( - High performance request strategy - ), - icon: 'shandian', - description: ( - - 10+ request strategy modules that can be used directly, just choose the one you want to use, and it can also - reduce performance problems caused by requests. - - ) - }, - { - title: Simple and familiar, - icon: 'check', - description: ( - - API design similar to axios, making it easier and more familiar for you to get started - - ) - }, - { - title: Request-level cache, - icon: 'shujuku', - description: ( - - Provides various server-side data cache modes such as memory mode and persistence mode to improve user - experience and reduce server pressure - - ) - }, - { - title: Lightweight, - icon: 'box', - description: ( - compressed version is only 4kb+, only 30% of axios - ) - } -]; - -function Feature({ title, icon, description }: FeatureItem) { - return ( -
-
- -
-
-

{title}

-

{description}

-
-
- ); -} - -export default function HomepageFeatures(): JSX.Element { - return ( -
-
-
- {FeatureList.map((props, idx) => ( - - ))} -
-
-
- ); -} +import Translate from '@docusaurus/Translate'; +import IconFont from '@site/src/components/IconFont'; +import clsx from 'clsx'; +import React from 'react'; +import styles from './styles.module.css'; + +type FeatureItem = { + title: JSX.Element; + icon: string; + description: JSX.Element; +}; + +const FeatureList: FeatureItem[] = [ + { + title: ( + + High performance request strategy + + ), + icon: 'shandian', + description: ( + + 10+ request strategy modules that can be used directly, just choose the one you want to + use, and it can also reduce performance problems caused by requests. + + ) + }, + { + title: ( + Simple and familiar + ), + icon: 'check', + description: ( + + API design similar to axios, making it easier and more familiar for you to get started + + ) + }, + { + title: ( + Request-level cache + ), + icon: 'shujuku', + description: ( + + Provides various server-side data cache modes such as memory mode and persistence mode + to improve user experience and reduce server pressure + + ) + }, + { + title: Lightweight, + icon: 'box', + description: ( + + compressed version is only 4kb+, only 30% of axios + + ) + } +]; + +function Feature({ title, icon, description }: FeatureItem) { + return ( +
+
+ +
+
+

{title}

+

{description}

+
+
+ ); +} + +export default function HomepageFeatures(): JSX.Element { + return ( +
+
+
+ {FeatureList.map((props, idx) => ( + + ))} +
+
+
+ ); +} diff --git a/src/pages/_indexComponent/Like/index.module.css b/src/pages/_indexComponent/Like/index.module.css index 5545f5931..d217d0656 100644 --- a/src/pages/_indexComponent/Like/index.module.css +++ b/src/pages/_indexComponent/Like/index.module.css @@ -1,17 +1,17 @@ -.btnWrapper { - display: flex; - justify-content: center; -} -.btnWrapper a:not(:last-child) { - margin-right: 1rem; -} - -@media screen and (max-width: 996px) { - .btnWrapper { - flex-direction: column; - } - .btnWrapper a:not(:last-child) { - margin-right: 0; - margin-bottom: 1rem; - } -} +.btnWrapper { + display: flex; + justify-content: center; +} +.btnWrapper a:not(:last-child) { + margin-right: 1rem; +} + +@media screen and (max-width: 996px) { + .btnWrapper { + flex-direction: column; + } + .btnWrapper a:not(:last-child) { + margin-right: 0; + margin-bottom: 1rem; + } +} diff --git a/src/pages/_indexComponent/Like/index.tsx b/src/pages/_indexComponent/Like/index.tsx index d8193e60d..631a2b7af 100644 --- a/src/pages/_indexComponent/Like/index.tsx +++ b/src/pages/_indexComponent/Like/index.tsx @@ -1,66 +1,66 @@ -import Link from '@docusaurus/Link'; -import Translate from '@docusaurus/Translate'; -import IconFont from '@site/src/components/IconFont'; -import PageModule from '@site/src/components/PageModule'; -import styles from './index.module.css'; - -const buttons = [ - { - icon: ( - - ), - text: Get Started, - link: '/tutorial/getting-started', - target: '_self' - }, - { - icon: , - text: Follow on X, - link: 'https://x.com/alovajs' - }, - { - icon: , - text: Star on Github, - link: 'https://github.com/alovajs/alova' - }, - { - icon: , - text: Say "Hi" on Discord, - link: 'https://discord.gg/S47QGJgkVb' - }, - { - icon: ( - - ), - text: Join Wechat group, - link: '/img/wechat_qrcode.jpg', - target: '_blank' - } -]; - -export default function Like(): JSX.Element { - return ( - -
- {buttons.map(({ icon, text, link, target }) => ( - - {icon} - {text} - - ))} -
-
- ); -} +import Link from '@docusaurus/Link'; +import Translate from '@docusaurus/Translate'; +import IconFont from '@site/src/components/IconFont'; +import PageModule from '@site/src/components/PageModule'; +import styles from './index.module.css'; + +const buttons = [ + { + icon: ( + + ), + text: Get Started, + link: '/tutorial/getting-started', + target: '_self' + }, + { + icon: , + text: Follow on X, + link: 'https://x.com/alovajs' + }, + { + icon: , + text: Star on Github, + link: 'https://github.com/alovajs/alova' + }, + { + icon: , + text: Say "Hi" on Discord, + link: 'https://discord.gg/S47QGJgkVb' + }, + { + icon: ( + + ), + text: Join Wechat group, + link: '/img/wechat_qrcode.jpg', + target: '_blank' + } +]; + +export default function Like(): JSX.Element { + return ( + +
+ {buttons.map(({ icon, text, link, target }) => ( + + {icon} + {text} + + ))} +
+
+ ); +} diff --git a/src/pages/_indexComponent/Strategy/index.tsx b/src/pages/_indexComponent/Strategy/index.tsx index ece92f561..2f33f2d24 100644 --- a/src/pages/_indexComponent/Strategy/index.tsx +++ b/src/pages/_indexComponent/Strategy/index.tsx @@ -1,97 +1,97 @@ -import Link from '@docusaurus/Link'; -import Translate from '@docusaurus/Translate'; -import IconFont from '@site/src/components/IconFont'; -import PageModule from '@site/src/components/PageModule'; -import CodeBlock from '@theme/CodeBlock'; -import clsx from 'clsx'; -import { useRef, useState } from 'react'; -import { strategyList } from './data'; -import styles from './style.module.css'; - -const StrategySelect = ({ active, setActive }) => { - const ulRef = useRef(null); - const [isMouseEnter, setIsMouseEnter] = useState(false); - - const handleItemClick = (i: number) => { - setActive(i); - if (ulRef.current) { - const ulWidth = ulRef.current.offsetWidth; - const liWidth = ulRef.current.children[i].offsetWidth; - const scrollPosition = liWidth * i - ulWidth / 2 + liWidth / 2; - ulRef.current.scrollTo({ left: scrollPosition, behavior: 'smooth' }); - } - }; - - return ( -
    setIsMouseEnter(true)} - onMouseLeave={() => setIsMouseEnter(false)} - style={{ overflowX: 'auto', whiteSpace: 'nowrap' }}> - {strategyList.map(({ title, link }, i) => ( -
  • handleItemClick(i)} - style={{ display: 'inline-block', cursor: 'pointer' }}> - {title} -
  • - ))} -
- ); -}; - -export default function Strategy() { - const [active, setActive] = useState(0); - const activeStrategy = strategyList[active]; - return ( - -
-
- -
-
-
-

{activeStrategy.title}

-

{activeStrategy.describe}

-
- {(activeStrategy.features || []).map((text, i) => ( -
- - {text} -
- ))} -
- - More details - -
- - {activeStrategy.code} - - - More details - -
-
-
- ); -} +import Link from '@docusaurus/Link'; +import Translate from '@docusaurus/Translate'; +import IconFont from '@site/src/components/IconFont'; +import PageModule from '@site/src/components/PageModule'; +import CodeBlock from '@theme/CodeBlock'; +import clsx from 'clsx'; +import { useRef, useState } from 'react'; +import { strategyList } from './data'; +import styles from './style.module.css'; + +const StrategySelect = ({ active, setActive }) => { + const ulRef = useRef(null); + const [isMouseEnter, setIsMouseEnter] = useState(false); + + const handleItemClick = (i: number) => { + setActive(i); + if (ulRef.current) { + const ulWidth = ulRef.current.offsetWidth; + const liWidth = ulRef.current.children[i].offsetWidth; + const scrollPosition = liWidth * i - ulWidth / 2 + liWidth / 2; + ulRef.current.scrollTo({ left: scrollPosition, behavior: 'smooth' }); + } + }; + + return ( +
    setIsMouseEnter(true)} + onMouseLeave={() => setIsMouseEnter(false)} + style={{ overflowX: 'auto', whiteSpace: 'nowrap' }}> + {strategyList.map(({ title, link }, i) => ( +
  • handleItemClick(i)} + style={{ display: 'inline-block', cursor: 'pointer' }}> + {title} +
  • + ))} +
+ ); +}; + +export default function Strategy() { + const [active, setActive] = useState(0); + const activeStrategy = strategyList[active]; + return ( + +
+
+ +
+
+
+

{activeStrategy.title}

+

{activeStrategy.describe}

+
+ {(activeStrategy.features || []).map((text, i) => ( +
+ + {text} +
+ ))} +
+ + More details + +
+ + {activeStrategy.code} + + + More details + +
+
+
+ ); +} diff --git a/src/pages/_indexComponent/Strategy/style.module.css b/src/pages/_indexComponent/Strategy/style.module.css index 4732c83b5..0a1ee68ff 100644 --- a/src/pages/_indexComponent/Strategy/style.module.css +++ b/src/pages/_indexComponent/Strategy/style.module.css @@ -1,85 +1,85 @@ -.tabs { - display: flex; - margin-bottom: 0.8rem; - overflow-x: auto; - padding-left: 0; -} -.tabs li { - padding: 0.6rem 1rem; - cursor: pointer; - position: relative; - overflow: hidden; - border: solid 1px var(--ifm-color-emphasis-300); - border-radius: 0.5rem; - transition: all 0.3s; - font-size: 0.9rem; - color: var(--ifm-color-emphasis-600); - margin-right: 1rem; - flex-shrink: 0; -} -.tabs li:not(.activeTabs):hover { - box-shadow: 1px 1px 10px var(--ifm-color-emphasis-300) inset; -} -.activeTabs { - border-color: var(--ifm-color-primary) !important; - color: var(--ifm-color-primary) !important; - background: rgba(var(--ifm-color-primary-rgb), 0.1); -} - -.iconCheck { - color: var(--ifm-color-primary); - vertical-align: -0.25em !important; -} -.codeBlock { - --ifm-leading: 0; -} -.featureItem { - color: var(--ifm-color-emphasis-600); -} -.wrapper { - background: rgba(var(--ifm-color-primary-rgb), 0.1); - /* background: var(--docusaurus-highlighted-code-line-bg); */ - border-radius: 1rem; - padding: 4rem; - --ifm-heading-margin-bottom: 0.5rem; - --ifm-paragraph-margin-bottom: 0; -} -.btnMobile { - width: 100%; - display: none; -} - -@media screen and (min-width: 996px) { - .tabs::-webkit-scrollbar { - height: 6px; - } - .tabs::-webkit-scrollbar-thumb { - background: transparent; - border-radius: 1rem; - } - .tabsHover::-webkit-scrollbar-thumb { - background: #e7e7e7; - } -} - -@media screen and (max-width: 996px) { - .wrapper { - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; - margin-bottom: 0; - padding: 1.6rem 0.8rem; - } - .tabs li { - padding: 0.3rem 0.5rem; - margin-right: 0.5rem; - } - .codeBlock { - margin-top: 1rem; - } - .btnMobile { - display: block; - } - .btnPC { - display: none; - } -} +.tabs { + display: flex; + margin-bottom: 0.8rem; + overflow-x: auto; + padding-left: 0; +} +.tabs li { + padding: 0.6rem 1rem; + cursor: pointer; + position: relative; + overflow: hidden; + border: solid 1px var(--ifm-color-emphasis-300); + border-radius: 0.5rem; + transition: all 0.3s; + font-size: 0.9rem; + color: var(--ifm-color-emphasis-600); + margin-right: 1rem; + flex-shrink: 0; +} +.tabs li:not(.activeTabs):hover { + box-shadow: 1px 1px 10px var(--ifm-color-emphasis-300) inset; +} +.activeTabs { + border-color: var(--ifm-color-primary) !important; + color: var(--ifm-color-primary) !important; + background: rgba(var(--ifm-color-primary-rgb), 0.1); +} + +.iconCheck { + color: var(--ifm-color-primary); + vertical-align: -0.25em !important; +} +.codeBlock { + --ifm-leading: 0; +} +.featureItem { + color: var(--ifm-color-emphasis-600); +} +.wrapper { + background: rgba(var(--ifm-color-primary-rgb), 0.1); + /* background: var(--docusaurus-highlighted-code-line-bg); */ + border-radius: 1rem; + padding: 4rem; + --ifm-heading-margin-bottom: 0.5rem; + --ifm-paragraph-margin-bottom: 0; +} +.btnMobile { + width: 100%; + display: none; +} + +@media screen and (min-width: 996px) { + .tabs::-webkit-scrollbar { + height: 6px; + } + .tabs::-webkit-scrollbar-thumb { + background: transparent; + border-radius: 1rem; + } + .tabsHover::-webkit-scrollbar-thumb { + background: #e7e7e7; + } +} + +@media screen and (max-width: 996px) { + .wrapper { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + margin-bottom: 0; + padding: 1.6rem 0.8rem; + } + .tabs li { + padding: 0.3rem 0.5rem; + margin-right: 0.5rem; + } + .codeBlock { + margin-top: 1rem; + } + .btnMobile { + display: block; + } + .btnPC { + display: none; + } +} diff --git a/src/pages/_indexComponent/Support/index.tsx b/src/pages/_indexComponent/Support/index.tsx index 6a31b787e..5bb035844 100644 --- a/src/pages/_indexComponent/Support/index.tsx +++ b/src/pages/_indexComponent/Support/index.tsx @@ -1,15 +1,15 @@ -import PageModule from '@site/src/components/PageModule'; -import SupportList from '@site/src/components/SupportList'; - -export default function Support(): JSX.Element { - return ( - - - - ); -} +import PageModule from '@site/src/components/PageModule'; +import SupportList from '@site/src/components/SupportList'; + +export default function Support(): JSX.Element { + return ( + + + + ); +} diff --git a/src/theme/Root.tsx b/src/theme/Root.tsx index 0c9a6b6e4..4de930388 100644 --- a/src/theme/Root.tsx +++ b/src/theme/Root.tsx @@ -6,14 +6,18 @@ try { const storageKey = 'language.toggle'; const locale = window.navigator.language; const href = window.location.href; - if (['zh-CN'].includes(locale) && href.indexOf(locale) < 0 && !sessionStorage.getItem(storageKey)) { + if ( + ['zh-CN'].includes(locale) && + href.indexOf(locale) < 0 && + !sessionStorage.getItem(storageKey) + ) { const hrefSplited = href.split('/'); hrefSplited.splice(3, 0, locale); - location.href = hrefSplited.join('/'); + location.href = hrefSplited.join('/'); sessionStorage.setItem(storageKey, '1'); } } -} catch(e) {} +} catch (e) {} // 默认实现,你可以自定义 export default function Root({ children }) { diff --git a/src/theme/TOC/index.js b/src/theme/TOC/index.js index 4a7ff089e..e31b7c1bc 100644 --- a/src/theme/TOC/index.js +++ b/src/theme/TOC/index.js @@ -21,7 +21,9 @@ export default function TOCWrapper(props) { img={require('@site/static/img/collect.jpg').default} className="margin-bottom--sm" href="https://github.com/alovajs/alova/issues/165"> - Is using alova in your project? please tell me! + + Is using alova in your project? please tell me! +
diff --git a/static/iconfont/demo.css b/static/iconfont/demo.css index a67054a0a..a60ec3c94 100644 --- a/static/iconfont/demo.css +++ b/static/iconfont/demo.css @@ -1,15 +1,18 @@ /* Logo 字体 */ @font-face { - font-family: "iconfont logo"; + font-family: 'iconfont logo'; src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834'); - src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'), + src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') + format('embedded-opentype'), url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'), - url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'), - url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg'); + url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') + format('truetype'), + url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') + format('svg'); } .logo { - font-family: "iconfont logo"; + font-family: 'iconfont logo'; font-size: 160px; font-style: normal; -webkit-font-smoothing: antialiased; @@ -48,7 +51,6 @@ color: #666; } - #tabs .active { border-bottom-color: #f00; color: #222; @@ -215,35 +217,35 @@ margin: 1em 0; } -.markdown>p, -.markdown>blockquote, -.markdown>.highlight, -.markdown>ol, -.markdown>ul { +.markdown > p, +.markdown > blockquote, +.markdown > .highlight, +.markdown > ol, +.markdown > ul { width: 80%; } -.markdown ul>li { +.markdown ul > li { list-style: circle; } -.markdown>ul li, -.markdown blockquote ul>li { +.markdown > ul li, +.markdown blockquote ul > li { margin-left: 20px; padding-left: 4px; } -.markdown>ul li p, -.markdown>ol li p { +.markdown > ul li p, +.markdown > ol li p { margin: 0.6em 0; } -.markdown ol>li { +.markdown ol > li { list-style: decimal; } -.markdown>ol li, -.markdown blockquote ol>li { +.markdown > ol li, +.markdown blockquote ol > li { margin-left: 20px; padding-left: 4px; } @@ -260,7 +262,7 @@ font-weight: 600; } -.markdown>table { +.markdown > table { border-collapse: collapse; border-spacing: 0px; empty-cells: show; @@ -269,21 +271,21 @@ margin-bottom: 24px; } -.markdown>table th { +.markdown > table th { white-space: nowrap; color: #333; font-weight: 600; } -.markdown>table th, -.markdown>table td { +.markdown > table th, +.markdown > table td { border: 1px solid #e9e9e9; padding: 8px 16px; text-align: left; } -.markdown>table th { - background: #F7F7F7; +.markdown > table th { + background: #f7f7f7; } .markdown blockquote { @@ -318,12 +320,11 @@ display: inline-block; } -.markdown>br, -.markdown>p>br { +.markdown > br, +.markdown > p > br { clear: both; } - .hljs { display: block; background: white; @@ -399,8 +400,8 @@ https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javasc * Based on dabblet (http://dabblet.com) * @author Lea Verou */ -code[class*="language-"], -pre[class*="language-"] { +code[class*='language-'], +pre[class*='language-'] { color: black; background: none; text-shadow: 0 1px white; @@ -422,46 +423,45 @@ pre[class*="language-"] { hyphens: none; } -pre[class*="language-"]::-moz-selection, -pre[class*="language-"] ::-moz-selection, -code[class*="language-"]::-moz-selection, -code[class*="language-"] ::-moz-selection { +pre[class*='language-']::-moz-selection, +pre[class*='language-'] ::-moz-selection, +code[class*='language-']::-moz-selection, +code[class*='language-'] ::-moz-selection { text-shadow: none; background: #b3d4fc; } -pre[class*="language-"]::selection, -pre[class*="language-"] ::selection, -code[class*="language-"]::selection, -code[class*="language-"] ::selection { +pre[class*='language-']::selection, +pre[class*='language-'] ::selection, +code[class*='language-']::selection, +code[class*='language-'] ::selection { text-shadow: none; background: #b3d4fc; } @media print { - - code[class*="language-"], - pre[class*="language-"] { + code[class*='language-'], + pre[class*='language-'] { text-shadow: none; } } /* Code blocks */ -pre[class*="language-"] { +pre[class*='language-'] { padding: 1em; - margin: .5em 0; + margin: 0.5em 0; overflow: auto; } -:not(pre)>code[class*="language-"], -pre[class*="language-"] { +:not(pre) > code[class*='language-'], +pre[class*='language-'] { background: #f5f2f0; } /* Inline code */ -:not(pre)>code[class*="language-"] { - padding: .1em; - border-radius: .3em; +:not(pre) > code[class*='language-'] { + padding: 0.1em; + border-radius: 0.3em; white-space: normal; } @@ -477,7 +477,7 @@ pre[class*="language-"] { } .namespace { - opacity: .7; + opacity: 0.7; } .token.property, @@ -505,7 +505,7 @@ pre[class*="language-"] { .language-css .token.string, .style .token.string { color: #9a6e3a; - background: hsla(0, 0%, 100%, .5); + background: hsla(0, 0%, 100%, 0.5); } .token.atrule, @@ -516,7 +516,7 @@ pre[class*="language-"] { .token.function, .token.class-name { - color: #DD4A68; + color: #dd4a68; } .token.regex, diff --git a/static/iconfont/demo_index.html b/static/iconfont/demo_index.html index 1ab4d5a1c..def39c89f 100644 --- a/static/iconfont/demo_index.html +++ b/static/iconfont/demo_index.html @@ -1,153 +1,180 @@ - - - iconfont Demo - - - - - - - - - - - - - -
-

- - -

- -
-
+ + + iconfont Demo + + + + + + + + + + + + + +
+

+ + + +

+ +
+
    -
  • -
    开始
    -
    &#xe63e;
    -
  • - +
    开始
    +
    &#xe63e;
    + +
  • -
    微信
    -
    &#xe637;
    -
  • - +
    微信
    +
    &#xe637;
    + +
  • -
    爱心
    -
    &#xe85c;
    -
  • - +
    爱心
    +
    &#xe85c;
    + +
  • -
    discord
    -
    &#xebf8;
    -
  • - +
    discord
    +
    &#xebf8;
    + +
  • -
    logo line
    -
    &#xe62b;
    -
  • - +
    logo line
    +
    &#xe62b;
    + +
  • -
    logo line
    -
    &#xe62c;
    -
  • - +
    logo line
    +
    &#xe62c;
    + +
  • -
    plus
    -
    &#xe62d;
    -
  • - +
    plus
    +
    &#xe62d;
    + +
  • -
    uf_check
    -
    &#xe626;
    -
  • - +
    uf_check
    +
    &#xe626;
    + +
  • -
    右箭头
    -
    &#xe62a;
    -
  • - +
    右箭头
    +
    &#xe62a;
    + +
  • -
    闪电
    -
    &#xe628;
    -
  • - +
    闪电
    +
    &#xe628;
    + +
  • -
    数据库
    -
    &#xe629;
    -
  • - +
    数据库
    +
    &#xe629;
    + +
  • -
    check
    -
    &#xe73c;
    -
  • - +
    check
    +
    &#xe73c;
    + +
  • -
    箱子
    -
    &#xe8c6;
    -
  • - +
    箱子
    +
    &#xe8c6;
    +
-

Unicode 引用

-
+

Unicode 引用

+
-

Unicode 是字体在网页端最原始的应用方式,特点是:

-
    -
  • 支持按字体的方式去动态调整图标大小,颜色等等。
  • -
  • 默认情况下不支持多色,直接添加多色图标会自动去色。
  • -
-
-

注意:新版 iconfont 支持两种方式引用多色图标:SVG symbol 引用方式和彩色字体图标模式。(使用彩色字体图标需要在「编辑项目」中开启「彩色」选项后并重新生成。)

-
-

Unicode 使用步骤如下:

-

第一步:拷贝项目下面生成的 @font-face

-
Unicode 是字体在网页端最原始的应用方式,特点是:

+
    +
  • 支持按字体的方式去动态调整图标大小,颜色等等。
  • +
  • 默认情况下不支持多色,直接添加多色图标会自动去色。
  • +
+
+

+ 注意:新版 iconfont 支持两种方式引用多色图标:SVG symbol + 引用方式和彩色字体图标模式。(使用彩色字体图标需要在「编辑项目」中开启「彩色」选项后并重新生成。) +

+
+

Unicode 使用步骤如下:

+

第一步:拷贝项目下面生成的 @font-face

+
@font-face {
   font-family: 'iconfont';
   src: url('iconfont.woff2?t=1710246562126') format('woff2'),
@@ -155,8 +182,8 @@ 

第一步:拷贝项目下面生成的 @font-face

-

第二步:定义使用 iconfont 的样式

-
第二步:定义使用 iconfont 的样式
+            
.iconfont {
   font-family: "iconfont" !important;
   font-size: 16px;
@@ -165,286 +192,286 @@ 

第二步:定义使用 iconfont 的样式

-moz-osx-font-smoothing: grayscale; }
-

第三步:挑选相应图标并获取字体编码,应用于页面

-
+            

第三步:挑选相应图标并获取字体编码,应用于页面

+
 <span class="iconfont">&#x33;</span>
 
-
-

"iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。

-
+
+

+ "iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。 +

+
-
-
-
    - -
  • - -
    - 开始 -
    -
    .icon-kaishi -
    -
  • - -
  • - -
    - 微信 -
    -
    .icon-weixin -
    -
  • - -
  • - -
    - 爱心 -
    -
    .icon-aixin -
    -
  • - -
  • - -
    - discord -
    -
    .icon-discord -
    -
  • - -
  • - -
    - logo line -
    -
    .icon-a-logoline1 -
    -
  • - -
  • - -
    - logo line -
    -
    .icon-a-logoline -
    -
  • - -
  • - -
    - plus -
    -
    .icon-plus -
    -
  • - -
  • - -
    - uf_check -
    -
    .icon-uf_check -
    -
  • - -
  • - -
    - 右箭头 -
    -
    .icon-youjiantou -
    -
  • - -
  • - -
    - 闪电 -
    -
    .icon-shandian -
    -
  • - -
  • - -
    - 数据库 -
    -
    .icon-shujuku -
    -
  • - -
  • - -
    - check -
    -
    .icon-check -
    -
  • - -
  • - -
    - 箱子 -
    -
    .icon-box -
    -
  • - -
-
-

font-class 引用

-
- -

font-class 是 Unicode 使用方式的一种变种,主要是解决 Unicode 书写不直观,语意不明确的问题。

-

与 Unicode 使用方式相比,具有如下特点:

-
    -
  • 相比于 Unicode 语意明确,书写更直观。可以很容易分辨这个 icon 是什么。
  • -
  • 因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class 里面的 Unicode 引用。
  • -
-

使用步骤如下:

-

第一步:引入项目下面生成的 fontclass 代码:

-
<link rel="stylesheet" href="./iconfont.css">
+        
+
+
    +
  • + +
    开始
    +
    .icon-kaishi
    +
  • + +
  • + +
    微信
    +
    .icon-weixin
    +
  • + +
  • + +
    爱心
    +
    .icon-aixin
    +
  • + +
  • + +
    discord
    +
    .icon-discord
    +
  • + +
  • + +
    logo line
    +
    .icon-a-logoline1
    +
  • + +
  • + +
    logo line
    +
    .icon-a-logoline
    +
  • + +
  • + +
    plus
    +
    .icon-plus
    +
  • + +
  • + +
    uf_check
    +
    .icon-uf_check
    +
  • + +
  • + +
    右箭头
    +
    .icon-youjiantou
    +
  • + +
  • + +
    闪电
    +
    .icon-shandian
    +
  • + +
  • + +
    数据库
    +
    .icon-shujuku
    +
  • + +
  • + +
    check
    +
    .icon-check
    +
  • + +
  • + +
    箱子
    +
    .icon-box
    +
  • +
+
+

font-class 引用

+
+ +

+ font-class 是 Unicode 使用方式的一种变种,主要是解决 Unicode + 书写不直观,语意不明确的问题。 +

+

与 Unicode 使用方式相比,具有如下特点:

+
    +
  • 相比于 Unicode 语意明确,书写更直观。可以很容易分辨这个 icon 是什么。
  • +
  • + 因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class 里面的 Unicode + 引用。 +
  • +
+

使用步骤如下:

+

第一步:引入项目下面生成的 fontclass 代码:

+
<link rel="stylesheet" href="./iconfont.css">
 
-

第二步:挑选相应图标并获取类名,应用于页面:

-
<span class="iconfont icon-xxx"></span>
+            

第二步:挑选相应图标并获取类名,应用于页面:

+
<span class="iconfont icon-xxx"></span>
 
-
-

" - iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。

-
-
-
-
+
+

+ " iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。 +

+
+
+
+
    -
  • - -
    开始
    -
    #icon-kaishi
    + +
    开始
    +
    #icon-kaishi
  • - +
  • - -
    微信
    -
    #icon-weixin
    + +
    微信
    +
    #icon-weixin
  • - +
  • - -
    爱心
    -
    #icon-aixin
    + +
    爱心
    +
    #icon-aixin
  • - +
  • - -
    discord
    -
    #icon-discord
    + +
    discord
    +
    #icon-discord
  • - +
  • - -
    logo line
    -
    #icon-a-logoline1
    + +
    logo line
    +
    #icon-a-logoline1
  • - +
  • - -
    logo line
    -
    #icon-a-logoline
    + +
    logo line
    +
    #icon-a-logoline
  • - +
  • - -
    plus
    -
    #icon-plus
    + +
    plus
    +
    #icon-plus
  • - +
  • - -
    uf_check
    -
    #icon-uf_check
    + +
    uf_check
    +
    #icon-uf_check
  • - +
  • - -
    右箭头
    -
    #icon-youjiantou
    + +
    右箭头
    +
    #icon-youjiantou
  • - +
  • - -
    闪电
    -
    #icon-shandian
    + +
    闪电
    +
    #icon-shandian
  • - +
  • - -
    数据库
    -
    #icon-shujuku
    + +
    数据库
    +
    #icon-shujuku
  • - +
  • - -
    check
    -
    #icon-check
    + +
    check
    +
    #icon-check
  • - +
  • - -
    箱子
    -
    #icon-box
    + +
    箱子
    +
    #icon-box
  • -
-

Symbol 引用

-
- -

这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇文章 - 这种用法其实是做了一个 SVG 的集合,与另外两种相比具有如下特点:

-
    -
  • 支持多色图标了,不再受单色限制。
  • -
  • 通过一些技巧,支持像字体那样,通过 font-size, color 来调整样式。
  • -
  • 兼容性较差,支持 IE9+,及现代浏览器。
  • -
  • 浏览器渲染 SVG 的性能一般,还不如 png。
  • -
-

使用步骤如下:

-

第一步:引入项目下面生成的 symbol 代码:

-
<script src="./iconfont.js"></script>
+            

Symbol 引用

+
+ +

+ 这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇文章 + 这种用法其实是做了一个 SVG 的集合,与另外两种相比具有如下特点: +

+
    +
  • 支持多色图标了,不再受单色限制。
  • +
  • + 通过一些技巧,支持像字体那样,通过 font-size, + color 来调整样式。 +
  • +
  • 兼容性较差,支持 IE9+,及现代浏览器。
  • +
  • 浏览器渲染 SVG 的性能一般,还不如 png。
  • +
+

使用步骤如下:

+

第一步:引入项目下面生成的 symbol 代码:

+
<script src="./iconfont.js"></script>
 
-

第二步:加入通用 CSS 代码(引入一次就行):

-
<style>
+            

第二步:加入通用 CSS 代码(引入一次就行):

+
<style>
 .icon {
   width: 1em;
   height: 1em;
@@ -454,34 +481,33 @@ 

第二步:加入通用 CSS 代码(引入一次就行):

-

第三步:挑选相应图标并获取类名,应用于页面:

-
<svg class="icon" aria-hidden="true">
+            

第三步:挑选相应图标并获取类名,应用于页面:

+
<svg class="icon" aria-hidden="true">
   <use xlink:href="#icon-xxx"></use>
 </svg>
 
+
-
-
- - + + diff --git a/static/iconfont/iconfont.css b/static/iconfont/iconfont.css index 99ee8ba4b..2862f7186 100644 --- a/static/iconfont/iconfont.css +++ b/static/iconfont/iconfont.css @@ -1,12 +1,12 @@ @font-face { - font-family: "iconfont"; /* Project id 4123843 */ + font-family: 'iconfont'; /* Project id 4123843 */ src: url('iconfont.woff2?t=1710246562126') format('woff2'), - url('iconfont.woff?t=1710246562126') format('woff'), - url('iconfont.ttf?t=1710246562126') format('truetype'); + url('iconfont.woff?t=1710246562126') format('woff'), + url('iconfont.ttf?t=1710246562126') format('truetype'); } .iconfont { - font-family: "iconfont" !important; + font-family: 'iconfont' !important; font-size: 16px; font-style: normal; -webkit-font-smoothing: antialiased; @@ -14,54 +14,53 @@ } .icon-kaishi:before { - content: "\e63e"; + content: '\e63e'; } .icon-weixin:before { - content: "\e637"; + content: '\e637'; } .icon-aixin:before { - content: "\e85c"; + content: '\e85c'; } .icon-discord:before { - content: "\ebf8"; + content: '\ebf8'; } .icon-a-logoline1:before { - content: "\e62b"; + content: '\e62b'; } .icon-a-logoline:before { - content: "\e62c"; + content: '\e62c'; } .icon-plus:before { - content: "\e62d"; + content: '\e62d'; } .icon-uf_check:before { - content: "\e626"; + content: '\e626'; } .icon-youjiantou:before { - content: "\e62a"; + content: '\e62a'; } .icon-shandian:before { - content: "\e628"; + content: '\e628'; } .icon-shujuku:before { - content: "\e629"; + content: '\e629'; } .icon-check:before { - content: "\e73c"; + content: '\e73c'; } .icon-box:before { - content: "\e8c6"; + content: '\e8c6'; } - diff --git a/static/iconfont/iconfont.js b/static/iconfont/iconfont.js index 2a6fdd6a6..ef4982f8f 100644 --- a/static/iconfont/iconfont.js +++ b/static/iconfont/iconfont.js @@ -1 +1,66 @@ -window._iconfont_svg_string_4123843='',function(e){var t=(t=document.getElementsByTagName("script"))[t.length-1],l=t.getAttribute("data-injectcss"),t=t.getAttribute("data-disable-injectsvg");if(!t){var c,o,i,n,a,s=function(t,l){l.parentNode.insertBefore(t,l)};if(l&&!e.__iconfont__svg__cssinject__){e.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(t){console&&console.log(t)}}c=function(){var t,l=document.createElement("div");l.innerHTML=e._iconfont_svg_string_4123843,(l=l.getElementsByTagName("svg")[0])&&(l.setAttribute("aria-hidden","true"),l.style.position="absolute",l.style.width=0,l.style.height=0,l.style.overflow="hidden",l=l,(t=document.body).firstChild?s(l,t.firstChild):t.appendChild(l))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(c,0):(o=function(){document.removeEventListener("DOMContentLoaded",o,!1),c()},document.addEventListener("DOMContentLoaded",o,!1)):document.attachEvent&&(i=c,n=e.document,a=!1,h(),n.onreadystatechange=function(){"complete"==n.readyState&&(n.onreadystatechange=null,d())})}function d(){a||(a=!0,i())}function h(){try{n.documentElement.doScroll("left")}catch(t){return void setTimeout(h,50)}d()}}(window); \ No newline at end of file +(window._iconfont_svg_string_4123843 = + ''), + (function (e) { + var t = (t = document.getElementsByTagName('script'))[t.length - 1], + l = t.getAttribute('data-injectcss'), + t = t.getAttribute('data-disable-injectsvg'); + if (!t) { + var c, + o, + i, + n, + a, + s = function (t, l) { + l.parentNode.insertBefore(t, l); + }; + if (l && !e.__iconfont__svg__cssinject__) { + e.__iconfont__svg__cssinject__ = !0; + try { + document.write( + '' + ); + } catch (t) { + console && console.log(t); + } + } + (c = function () { + var t, + l = document.createElement('div'); + (l.innerHTML = e._iconfont_svg_string_4123843), + (l = l.getElementsByTagName('svg')[0]) && + (l.setAttribute('aria-hidden', 'true'), + (l.style.position = 'absolute'), + (l.style.width = 0), + (l.style.height = 0), + (l.style.overflow = 'hidden'), + (l = l), + (t = document.body).firstChild ? s(l, t.firstChild) : t.appendChild(l)); + }), + document.addEventListener + ? ~['complete', 'loaded', 'interactive'].indexOf(document.readyState) + ? setTimeout(c, 0) + : ((o = function () { + document.removeEventListener('DOMContentLoaded', o, !1), c(); + }), + document.addEventListener('DOMContentLoaded', o, !1)) + : document.attachEvent && + ((i = c), + (n = e.document), + (a = !1), + h(), + (n.onreadystatechange = function () { + 'complete' == n.readyState && ((n.onreadystatechange = null), d()); + })); + } + function d() { + a || ((a = !0), i()); + } + function h() { + try { + n.documentElement.doScroll('left'); + } catch (t) { + return void setTimeout(h, 50); + } + d(); + } + })(window); diff --git a/tsconfig.json b/tsconfig.json index 3c2167899..314eab8a4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,7 @@ -{ - // This file is not used in compilation. It is here just for a nice editor experience. - "extends": "@docusaurus/tsconfig", - "compilerOptions": { - "baseUrl": "." - } -} +{ + // This file is not used in compilation. It is here just for a nice editor experience. + "extends": "@docusaurus/tsconfig", + "compilerOptions": { + "baseUrl": "." + } +} diff --git a/versioned_docs/version-2.x/about/_category_.json b/versioned_docs/version-2.x/about/_category_.json index 07d61bd10..c562b25fb 100644 --- a/versioned_docs/version-2.x/about/_category_.json +++ b/versioned_docs/version-2.x/about/_category_.json @@ -1,6 +1,6 @@ -{ - "label": "Others", - "link": { - "type": "generated-index" - } -} +{ + "label": "Others", + "link": { + "type": "generated-index" + } +} diff --git a/versioned_docs/version-2.x/api/_category_.json b/versioned_docs/version-2.x/api/_category_.json index 4b4861e39..de7065684 100644 --- a/versioned_docs/version-2.x/api/_category_.json +++ b/versioned_docs/version-2.x/api/_category_.json @@ -1,6 +1,6 @@ -{ - "label": "API", - "link": { - "type": "generated-index" - } -} +{ + "label": "API", + "link": { + "type": "generated-index" + } +} diff --git a/versioned_docs/version-2.x/contributing/_category_.json b/versioned_docs/version-2.x/contributing/_category_.json index d3bf2a858..66f0e1824 100644 --- a/versioned_docs/version-2.x/contributing/_category_.json +++ b/versioned_docs/version-2.x/contributing/_category_.json @@ -1,6 +1,6 @@ -{ - "label": "Contributing", - "link": { - "type": "generated-index" - } -} +{ + "label": "Contributing", + "link": { + "type": "generated-index" + } +} diff --git a/versioned_docs/version-2.x/tutorial/01-example/_category_.json b/versioned_docs/version-2.x/tutorial/01-example/_category_.json index 52fa495e9..82e0066dc 100644 --- a/versioned_docs/version-2.x/tutorial/01-example/_category_.json +++ b/versioned_docs/version-2.x/tutorial/01-example/_category_.json @@ -1,7 +1,7 @@ -{ - "label": "Examples", - "link": { - "type": "generated-index", - "description": "Here are rich examples to show how alova behaves in different request scenarios.The dependencies will be installed before the example runs, so wait for a while!" - } -} +{ + "label": "Examples", + "link": { + "type": "generated-index", + "description": "Here are rich examples to show how alova behaves in different request scenarios.The dependencies will be installed before the example runs, so wait for a while!" + } +} diff --git a/versioned_docs/version-2.x/tutorial/02-getting-started/README.md b/versioned_docs/version-2.x/tutorial/02-getting-started/README.md index 6d845e06c..a5c34b190 100644 --- a/versioned_docs/version-2.x/tutorial/02-getting-started/README.md +++ b/versioned_docs/version-2.x/tutorial/02-getting-started/README.md @@ -1,136 +1,136 @@ ---- -title: What is alova ---- - -import Link from '@docusaurus/Link'; -import NavCard from '@site/src/components/NavCard'; -import SupportList from '@site/src/components/SupportList'; - -alova is a lightweight request strategy library. It provides a complete set of solutions to deal with complex request scenarios. We call it **Request Strategy**. It only takes one line of code to quickly implement various complex request logics. , not only can help you improve development efficiency, but also help you improve the operating efficiency of the App and reduce the pressure on the server. - -Here is the simplest request example: - -```javascript -const response = await alova.Get('/api/user'); -``` - -Ordinary? Let's look at another example of automatically managing request status. **loading, error, and data are responsive data**. In UI frameworks such as react, vue, svelte, etc., they can be bound directly in the view and will be processed according to the request. State automatically maintains this responsive data. - -```javascript -const { loading, error, data } = useRequest(alova.Get('/api/user')); -``` - -The following is an example of a paging request strategy that automatically triggers requests with different parameters when page, pageSize, etc. change. - -```javascript -const { loading, error, data, page, pageSize, total } = usePagination((page, size) => - alova.Get('/api/user/list', { - params: { page, size } - }) -); -``` - -alova provides 10+ request strategy modules based on the [RSM](/tutorial/others/RSM) specification, which are implemented in the form of useHook. - -## Core useHook - -useRequest -useWatcher -useFetcher - -## Scenario-based request strategy - -usePagination -useSQRequest -useForm -TokenAuthentication -useAutoRequest -useCaptcha -actionDelegationMiddleware -useSerialRequest -useSerialWatcher -useRetriableRequest -useUploader -useSSE - -## High flexibility - -Thanks to the high flexibility of alova, you can use it with different request libraries in the following different JS environments (the gray part will be gradually supported in the future). - - - -## Is there any difference? - -Unlike other request libraries, alova's goal is to make requests simpler and maintain more efficient data interaction. - -We consider both developers and App users. For developers, alova provides them with a simple request API and an out-of-the-box high-performance request strategy module. For application users, they can enjoy Alova's high-performance data interaction brings a smooth experience. - -In addition, let’s take a look at the specific features: - -- API design similar to axios, allowing users to learn at a lower cost; -- 10+ out-of-the-box high-performance request strategies to make applications smoother; -- alova is lightweight, only 4kb+, which is 30%+ of axios; -- High flexibility, alova's adapter allows alova to be used in any js environment and with any UI framework (the built-in supported UI framework is `vue/react/svelte`), and provides a unified experience and perfect code migrate; -- 3 caching modes and request sharing mechanism to improve request performance and reduce server pressure; -- Highly aggregated organization of API code. The request parameters, cache behavior, response data conversion, etc. of each API will be gathered in the same code block, which has great advantages for managing a large number of APIs; - -In [alova's future](/tutorial/others/future), further request simplification will be implemented. - -:::info compared to other request libraries - -You can also check out [Comparison with other request libraries](/tutorial/others/comparison) to learn more about how alova is different. - -::: - -## Online trial - -You can run the project directly in the browser via Codesandbox [online editable examples try alovajs](/category/examples), so it's almost indistinguishable from local development without having to install anything on your machine. - -## Join alova community - -import ImgDiscord from '@site/static/img/discord.svg'; -import ImgX from '@site/static/img/x.svg'; -import ImgWechat from '@site/static/img/wechat.svg'; -import wechatQrcode from '@site/static/img/wechat_qrcode.jpg'; - -, -title: 'Discord', -desc: 'The community\'s GPT robot will answer your questions', -link: 'https://discord.gg/S47QGJgkVb', -target: '__blank' -}, -{ -Image: , -title: 'WeChat', -desc: 'Communicate in group chat and get responses faster', -link: wechatQrcode, -target: '__blank' -}, -{ -Image: , -title: 'X', -desc: 'Follow us and continue to receive the latest news', -link: 'https://x.com/alovajs', -target: '__blank' -} -]}> - -## Welcome to contribute - -Before contributing, please be sure to read the [Contribution Guide](/contributing/overview) in detail to ensure your effective contribution. - -## Start - -Next, we will start with the simplest request, then explain the request strategy, understand how alova simplifies your work, and then go into the advanced guide and the best practices summarized in actual projects. - -Let’s start learning to send our first request! - - +--- +title: What is alova +--- + +import Link from '@docusaurus/Link'; +import NavCard from '@site/src/components/NavCard'; +import SupportList from '@site/src/components/SupportList'; + +alova is a lightweight request strategy library. It provides a complete set of solutions to deal with complex request scenarios. We call it **Request Strategy**. It only takes one line of code to quickly implement various complex request logics. , not only can help you improve development efficiency, but also help you improve the operating efficiency of the App and reduce the pressure on the server. + +Here is the simplest request example: + +```javascript +const response = await alova.Get('/api/user'); +``` + +Ordinary? Let's look at another example of automatically managing request status. **loading, error, and data are responsive data**. In UI frameworks such as react, vue, svelte, etc., they can be bound directly in the view and will be processed according to the request. State automatically maintains this responsive data. + +```javascript +const { loading, error, data } = useRequest(alova.Get('/api/user')); +``` + +The following is an example of a paging request strategy that automatically triggers requests with different parameters when page, pageSize, etc. change. + +```javascript +const { loading, error, data, page, pageSize, total } = usePagination((page, size) => + alova.Get('/api/user/list', { + params: { page, size } + }) +); +``` + +alova provides 10+ request strategy modules based on the [RSM](/tutorial/others/RSM) specification, which are implemented in the form of useHook. + +## Core useHook + +useRequest +useWatcher +useFetcher + +## Scenario-based request strategy + +usePagination +useSQRequest +useForm +TokenAuthentication +useAutoRequest +useCaptcha +actionDelegationMiddleware +useSerialRequest +useSerialWatcher +useRetriableRequest +useUploader +useSSE + +## High flexibility + +Thanks to the high flexibility of alova, you can use it with different request libraries in the following different JS environments (the gray part will be gradually supported in the future). + + + +## Is there any difference? + +Unlike other request libraries, alova's goal is to make requests simpler and maintain more efficient data interaction. + +We consider both developers and App users. For developers, alova provides them with a simple request API and an out-of-the-box high-performance request strategy module. For application users, they can enjoy Alova's high-performance data interaction brings a smooth experience. + +In addition, let’s take a look at the specific features: + +- API design similar to axios, allowing users to learn at a lower cost; +- 10+ out-of-the-box high-performance request strategies to make applications smoother; +- alova is lightweight, only 4kb+, which is 30%+ of axios; +- High flexibility, alova's adapter allows alova to be used in any js environment and with any UI framework (the built-in supported UI framework is `vue/react/svelte`), and provides a unified experience and perfect code migrate; +- 3 caching modes and request sharing mechanism to improve request performance and reduce server pressure; +- Highly aggregated organization of API code. The request parameters, cache behavior, response data conversion, etc. of each API will be gathered in the same code block, which has great advantages for managing a large number of APIs; + +In [alova's future](/tutorial/others/future), further request simplification will be implemented. + +:::info compared to other request libraries + +You can also check out [Comparison with other request libraries](/tutorial/others/comparison) to learn more about how alova is different. + +::: + +## Online trial + +You can run the project directly in the browser via Codesandbox [online editable examples try alovajs](/category/examples), so it's almost indistinguishable from local development without having to install anything on your machine. + +## Join alova community + +import ImgDiscord from '@site/static/img/discord.svg'; +import ImgX from '@site/static/img/x.svg'; +import ImgWechat from '@site/static/img/wechat.svg'; +import wechatQrcode from '@site/static/img/wechat_qrcode.jpg'; + +, +title: 'Discord', +desc: 'The community\'s GPT robot will answer your questions', +link: 'https://discord.gg/S47QGJgkVb', +target: '__blank' +}, +{ +Image: , +title: 'WeChat', +desc: 'Communicate in group chat and get responses faster', +link: wechatQrcode, +target: '__blank' +}, +{ +Image: , +title: 'X', +desc: 'Follow us and continue to receive the latest news', +link: 'https://x.com/alovajs', +target: '__blank' +} +]}> + +## Welcome to contribute + +Before contributing, please be sure to read the [Contribution Guide](/contributing/overview) in detail to ensure your effective contribution. + +## Start + +Next, we will start with the simplest request, then explain the request strategy, understand how alova simplifies your work, and then go into the advanced guide and the best practices summarized in actual projects. + +Let’s start learning to send our first request! + + diff --git a/versioned_docs/version-2.x/tutorial/02-getting-started/_category_.json b/versioned_docs/version-2.x/tutorial/02-getting-started/_category_.json index 6a7cbc87e..41f4c00e7 100644 --- a/versioned_docs/version-2.x/tutorial/02-getting-started/_category_.json +++ b/versioned_docs/version-2.x/tutorial/02-getting-started/_category_.json @@ -1,3 +1,3 @@ -{ - "label": "Getting Started" -} +{ + "label": "Getting Started" +} diff --git a/versioned_docs/version-2.x/tutorial/03-combine-framework/_category_.json b/versioned_docs/version-2.x/tutorial/03-combine-framework/_category_.json index 1e1d1ef7f..109e5b140 100644 --- a/versioned_docs/version-2.x/tutorial/03-combine-framework/_category_.json +++ b/versioned_docs/version-2.x/tutorial/03-combine-framework/_category_.json @@ -1,3 +1,3 @@ -{ - "label": "Combine Framework" -} +{ + "label": "Combine Framework" +} diff --git a/versioned_docs/version-2.x/tutorial/04-cache/README.md b/versioned_docs/version-2.x/tutorial/04-cache/README.md index be3949357..e8dbdd057 100644 --- a/versioned_docs/version-2.x/tutorial/04-cache/README.md +++ b/versioned_docs/version-2.x/tutorial/04-cache/README.md @@ -1,9 +1,9 @@ ---- -title: Response Cache ---- - -Response cache is a technology that caches the data returned by server to the client. It avoids sending repeated requests when the server data can be reused. It can not only respond to user requests immdediately, but also save server resources. According to different caching scenarios, alova provides 3 modes named **memory mode, cache placeholder mode, and restore mode**. Just choose the one that suits you. - -In addition, using the cache operation API, you can freely add, modify, and delete caches, as well as customize cache matching rules. - -Next, let us start to understand from the cache mode! +--- +title: Response Cache +--- + +Response cache is a technology that caches the data returned by server to the client. It avoids sending repeated requests when the server data can be reused. It can not only respond to user requests immdediately, but also save server resources. According to different caching scenarios, alova provides 3 modes named **memory mode, cache placeholder mode, and restore mode**. Just choose the one that suits you. + +In addition, using the cache operation API, you can freely add, modify, and delete caches, as well as customize cache matching rules. + +Next, let us start to understand from the cache mode! diff --git a/versioned_docs/version-2.x/tutorial/04-cache/_category_.json b/versioned_docs/version-2.x/tutorial/04-cache/_category_.json index 2360c70d8..1b6419623 100644 --- a/versioned_docs/version-2.x/tutorial/04-cache/_category_.json +++ b/versioned_docs/version-2.x/tutorial/04-cache/_category_.json @@ -1,3 +1,3 @@ -{ - "label": "Response Cache" -} +{ + "label": "Response Cache" +} diff --git a/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/README.md b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/README.md index 5b703c10d..d7b8f1bb7 100644 --- a/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/README.md +++ b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/README.md @@ -1,146 +1,146 @@ ---- -title: Sensorless data interaction - Overview ---- - -Non-inductive data interaction means that when users interact with the application, relevant content can be displayed immediately without waiting, or the operation result can be displayed without waiting when submitting information, just like interacting with local data, thereby greatly improving the smoothness of the application It allows users to not feel the lag caused by data transmission. - -This is not new. The concept of optimistic update existed before 2015. It refers to displaying the submission results to the interface before the server responds. It is based on the assumption that most submissions are successful. The opposite is a conservative update, that is, the server will display a wait state before responding until the request is completed. In terms of handling failures, the current optimistic update solution is usually handled through fallback, such as the following sample code: - -```javascript -const list = []; -const data = {}; -addTodo(data).catch(() => { - list = list.filter(item => item !== data); -}); -list.push(data); -``` - -This can cause the following problems: - -1. Rollback will increase the user's understanding and operation costs; -2. Request timing issues; -3. If subsequent requests depend on this submission, this failure will make subsequent requests meaningless; -4. Possible lost requests; - -After several months of program design and continuous iteration, alova has taken a big step in this area. In our program, the above problems have been solved, which can ensure the success of the request more stably. Although there are still technical limitations, But it has been applied in many scenarios. In our technical solution, the problems caused by network fluctuations can be reduced to a higher extent. Your application is still available in high-latency networks or even disconnected, and the latest data can still be maintained after refreshing the page. - -## Application scenarios - -Although non-inductive data interaction cannot be used on a large scale, it is very suitable in certain scenarios. During the exploration, we found at least including but not limited to the following scenarios for your reference. - -### Editor application - -Note-taking applications such as Evernote and canvas editing applications such as MasterGO need to meet the following requirements: - -1. When entering the note or drawing list, the list data will be pulled in full, and the local cache data will be used next time; -2. Real-time synchronization to the server during the editing process, and the synchronization process occurs in the background, which will not affect the normal use of users; -3. You can continue to use it even when the network is poor or disconnected; - -:::info example - -We provide a [note application example](/tutorial/example/silent-submit-notes), you can enter the experience. - -::: - -### Setup module - -The setting module composed of commonly used switches and selectors needs to realize the requirement that the user operation is synchronized to the server in real time, and the submission status is no longer displayed, but the latest status after the operation is directly displayed. - -:::info example - -We provide a [setting page example](/tutorial/example/silent-submit-setting), you can enter the experience. - -::: - -### Simple list management - -The data we fill in when creating a list item is enough for the display of the list page, which is called a simple list. For example, a student list page displays the three data of the student's name, gender, and grade. These three data are required when creating a student fill in. In a simple list the following requirements will be fulfilled: - -1. Immediately display the latest status on the list page when adding, editing and deleting list items, no need to display it after the submission is completed, and it is not limited by network fluctuations; -2. When the page is refreshed, the list page is always kept up to date; - -:::info example - -We provide a [simple list page example](/tutorial/example/silent-submit-simple-list), you can enter the experience. - -::: - -### Complex list management - -A complex list means that the data filled in when creating a list item is not enough for display on the list page, and additional data needs to be generated according to the calculation of the server. For example, a Todo list page needs to list specific executions in addition to displaying basic information. date, but only the execution date range and related rules are specified on the creation page, so the execution date is calculated and generated by the server based on the date range and rules. - -The following requirements will be fulfilled in a complex list: - -1. Immediately display the latest status on the list page when adding, editing and deleting list items, and update the data calculated by the server to this list item after the server responds; -2. When the page is refreshed, the list page is always kept up to date; - -:::info example - -Stay tuned for complex list examples... - -::: - -### Free Mode - -In the above scenarios, you may want to judge whether to use the non-inductive interaction strategy or the most common conservative request strategy based on a condition. The requirements are as follows: - -1. When the network status is good, or paying users will use the non-sensing interaction strategy, but when the network fluctuates greatly, or free users cannot enjoy the non-sensing interaction strategy; -2. Strategies can be switched freely; - -:::info example - -In the above examples, you can experience the free switching strategy - -::: - -## Not recommended application scenarios - -### Information sharing class - -The submitted information needs to be synchronized to others, such as order information. This type of information has high real-time requirements, and we should ensure that the submission is successful. - -### Complex data interaction class - -Complex data interaction refers to the mixed editing and filtering of data, such as adding, editing, deleting and filtering a certain list. In this case, Alova cannot currently support it well. In subsequent versions Will try to solve this puzzle too. - -## Technical solutions - -In the technical solution of non-inductive data interaction, alova has implemented data pre-fetching and silent submission respectively. Next, let's understand these two technical solutions. - -:::info - -Please make sure you have mastered the following chapters before reading - -- [Basic Learning](/tutorial/getting-started) - -::: - -### Data pre-fetching - -In html, you may have seen such a tag ``, which tells the browser to preload the style file when it is idle, and put it in the cache In , when you really need to use it, you can take it out of the cache. Alova also uses a similar scheme to pre-fetch the required data through [useFetcher](/tutorial/advanced/use-fetcher), and it will be stored locally. in cache. You can predict the content that the user needs to read under any circumstances, and then pre-fetch the corresponding content. For example, the content of the next page can be pre-loaded in the process page, or the user stays on a button 200ms, we can pre-fetch the data needed for the next interface, which is similar to **Next.js** page preloading. - -We provide a [preloaded example](/tutorial/example/prefetch), you can enter the experience. - -### Silent submit - -Silent submission is a mechanism of submitting and responding. In the scheme, the completion of submission will be guaranteed, so it can be regarded as a safer optimistic update scheme. Silent submission mainly uses **Silent Queue** to persist request information and ensure request timing issues. **Virtual data** is used as a placeholder for server response data, which is replaced with actual response data when the request is completed. , through these two technologies, localized data creation is realized, and operations such as editing and deleting of newly created data are realized, even if the created data has not yet been submitted successfully on the server side. In order to keep development costs to a minimum, this is done automatically in alova. - -### Quiet Queue - -Silent queues are used to ensure the timing of requests. We can create queues arbitrarily, and all requests entering the queue will be stored in the queue in the form of **SilentMethod** instances. Each **SilentMethod** not only contains request information, but also Contains related configurations for silent submission, such as _unique id_, _error retry parameters_, etc. The requests in the queue will only initiate the next request after the previous response, thus ensuring the timing of the requests in the queue. You can put dependent requests in the same queue, which also ensures data consistency. - -![Silent queue](https://user-images.githubusercontent.com/29848971/220057005-dd467392-4a43-45a7-90dc-999dd1d95531.png) - -In the scheme, three behavior modes of `queue`, `silent`, and `static` are provided respectively, which are used to distinguish what kind of behavior a request needs to perform. - -- queue: The request will enter the silent queue, but it will not be persisted. It will wait for the previous request to complete before sending the request. The response callback will be triggered after the response. It is generally used for data acquisition that depends on the previous request; -- silent: The request will enter the silent queue and be persisted, and then trigger the response callback immediately. In this behavior mode, onSuccess will receive virtual data, and onError will never be triggered. Use this pattern; -- static: the request will not enter the silent queue, nor will it be persisted, it will issue the request immediately, and this mode can be used when silent submission is disabled; - -### virtual data - -In the submit-to-response mechanism, virtual data plays an important role. It means that before the server actually responds, it is used as a placeholder for the response data, and through the tracing mechanism, even if the virtual data is distributed in various locations of the application, Can be automatically replaced with the actual response data after the response. At the same time, it also plays an important role in the silent queue. It can identify the dependencies of requests in the queue, and replace the dependent data with actual data after the dependencies respond. For example, when creating a piece of data, it will return the id of this data. When the service When the terminal has not responded, the user performs a delete operation, and the id needs to be used as the delete identifier. At this time, the delete request will depend on the creation request. Before creating a request response, the virtual data will be used as an id placeholder as a parameter for deletion, and the virtual data id will be replaced after creating a request response, so that the deletion request can be completed. - -![virtual data](https://user-images.githubusercontent.com/29848971/220005505-d30b7ae2-ddd0-4080-81a4-65c9be2cb0bd.png) - -Next, we will learn more about the characteristics of virtual data. +--- +title: Sensorless data interaction - Overview +--- + +Non-inductive data interaction means that when users interact with the application, relevant content can be displayed immediately without waiting, or the operation result can be displayed without waiting when submitting information, just like interacting with local data, thereby greatly improving the smoothness of the application It allows users to not feel the lag caused by data transmission. + +This is not new. The concept of optimistic update existed before 2015. It refers to displaying the submission results to the interface before the server responds. It is based on the assumption that most submissions are successful. The opposite is a conservative update, that is, the server will display a wait state before responding until the request is completed. In terms of handling failures, the current optimistic update solution is usually handled through fallback, such as the following sample code: + +```javascript +const list = []; +const data = {}; +addTodo(data).catch(() => { + list = list.filter(item => item !== data); +}); +list.push(data); +``` + +This can cause the following problems: + +1. Rollback will increase the user's understanding and operation costs; +2. Request timing issues; +3. If subsequent requests depend on this submission, this failure will make subsequent requests meaningless; +4. Possible lost requests; + +After several months of program design and continuous iteration, alova has taken a big step in this area. In our program, the above problems have been solved, which can ensure the success of the request more stably. Although there are still technical limitations, But it has been applied in many scenarios. In our technical solution, the problems caused by network fluctuations can be reduced to a higher extent. Your application is still available in high-latency networks or even disconnected, and the latest data can still be maintained after refreshing the page. + +## Application scenarios + +Although non-inductive data interaction cannot be used on a large scale, it is very suitable in certain scenarios. During the exploration, we found at least including but not limited to the following scenarios for your reference. + +### Editor application + +Note-taking applications such as Evernote and canvas editing applications such as MasterGO need to meet the following requirements: + +1. When entering the note or drawing list, the list data will be pulled in full, and the local cache data will be used next time; +2. Real-time synchronization to the server during the editing process, and the synchronization process occurs in the background, which will not affect the normal use of users; +3. You can continue to use it even when the network is poor or disconnected; + +:::info example + +We provide a [note application example](/tutorial/example/silent-submit-notes), you can enter the experience. + +::: + +### Setup module + +The setting module composed of commonly used switches and selectors needs to realize the requirement that the user operation is synchronized to the server in real time, and the submission status is no longer displayed, but the latest status after the operation is directly displayed. + +:::info example + +We provide a [setting page example](/tutorial/example/silent-submit-setting), you can enter the experience. + +::: + +### Simple list management + +The data we fill in when creating a list item is enough for the display of the list page, which is called a simple list. For example, a student list page displays the three data of the student's name, gender, and grade. These three data are required when creating a student fill in. In a simple list the following requirements will be fulfilled: + +1. Immediately display the latest status on the list page when adding, editing and deleting list items, no need to display it after the submission is completed, and it is not limited by network fluctuations; +2. When the page is refreshed, the list page is always kept up to date; + +:::info example + +We provide a [simple list page example](/tutorial/example/silent-submit-simple-list), you can enter the experience. + +::: + +### Complex list management + +A complex list means that the data filled in when creating a list item is not enough for display on the list page, and additional data needs to be generated according to the calculation of the server. For example, a Todo list page needs to list specific executions in addition to displaying basic information. date, but only the execution date range and related rules are specified on the creation page, so the execution date is calculated and generated by the server based on the date range and rules. + +The following requirements will be fulfilled in a complex list: + +1. Immediately display the latest status on the list page when adding, editing and deleting list items, and update the data calculated by the server to this list item after the server responds; +2. When the page is refreshed, the list page is always kept up to date; + +:::info example + +Stay tuned for complex list examples... + +::: + +### Free Mode + +In the above scenarios, you may want to judge whether to use the non-inductive interaction strategy or the most common conservative request strategy based on a condition. The requirements are as follows: + +1. When the network status is good, or paying users will use the non-sensing interaction strategy, but when the network fluctuates greatly, or free users cannot enjoy the non-sensing interaction strategy; +2. Strategies can be switched freely; + +:::info example + +In the above examples, you can experience the free switching strategy + +::: + +## Not recommended application scenarios + +### Information sharing class + +The submitted information needs to be synchronized to others, such as order information. This type of information has high real-time requirements, and we should ensure that the submission is successful. + +### Complex data interaction class + +Complex data interaction refers to the mixed editing and filtering of data, such as adding, editing, deleting and filtering a certain list. In this case, Alova cannot currently support it well. In subsequent versions Will try to solve this puzzle too. + +## Technical solutions + +In the technical solution of non-inductive data interaction, alova has implemented data pre-fetching and silent submission respectively. Next, let's understand these two technical solutions. + +:::info + +Please make sure you have mastered the following chapters before reading + +- [Basic Learning](/tutorial/getting-started) + +::: + +### Data pre-fetching + +In html, you may have seen such a tag ``, which tells the browser to preload the style file when it is idle, and put it in the cache In , when you really need to use it, you can take it out of the cache. Alova also uses a similar scheme to pre-fetch the required data through [useFetcher](/tutorial/advanced/use-fetcher), and it will be stored locally. in cache. You can predict the content that the user needs to read under any circumstances, and then pre-fetch the corresponding content. For example, the content of the next page can be pre-loaded in the process page, or the user stays on a button 200ms, we can pre-fetch the data needed for the next interface, which is similar to **Next.js** page preloading. + +We provide a [preloaded example](/tutorial/example/prefetch), you can enter the experience. + +### Silent submit + +Silent submission is a mechanism of submitting and responding. In the scheme, the completion of submission will be guaranteed, so it can be regarded as a safer optimistic update scheme. Silent submission mainly uses **Silent Queue** to persist request information and ensure request timing issues. **Virtual data** is used as a placeholder for server response data, which is replaced with actual response data when the request is completed. , through these two technologies, localized data creation is realized, and operations such as editing and deleting of newly created data are realized, even if the created data has not yet been submitted successfully on the server side. In order to keep development costs to a minimum, this is done automatically in alova. + +### Quiet Queue + +Silent queues are used to ensure the timing of requests. We can create queues arbitrarily, and all requests entering the queue will be stored in the queue in the form of **SilentMethod** instances. Each **SilentMethod** not only contains request information, but also Contains related configurations for silent submission, such as _unique id_, _error retry parameters_, etc. The requests in the queue will only initiate the next request after the previous response, thus ensuring the timing of the requests in the queue. You can put dependent requests in the same queue, which also ensures data consistency. + +![Silent queue](https://user-images.githubusercontent.com/29848971/220057005-dd467392-4a43-45a7-90dc-999dd1d95531.png) + +In the scheme, three behavior modes of `queue`, `silent`, and `static` are provided respectively, which are used to distinguish what kind of behavior a request needs to perform. + +- queue: The request will enter the silent queue, but it will not be persisted. It will wait for the previous request to complete before sending the request. The response callback will be triggered after the response. It is generally used for data acquisition that depends on the previous request; +- silent: The request will enter the silent queue and be persisted, and then trigger the response callback immediately. In this behavior mode, onSuccess will receive virtual data, and onError will never be triggered. Use this pattern; +- static: the request will not enter the silent queue, nor will it be persisted, it will issue the request immediately, and this mode can be used when silent submission is disabled; + +### virtual data + +In the submit-to-response mechanism, virtual data plays an important role. It means that before the server actually responds, it is used as a placeholder for the response data, and through the tracing mechanism, even if the virtual data is distributed in various locations of the application, Can be automatically replaced with the actual response data after the response. At the same time, it also plays an important role in the silent queue. It can identify the dependencies of requests in the queue, and replace the dependent data with actual data after the dependencies respond. For example, when creating a piece of data, it will return the id of this data. When the service When the terminal has not responded, the user performs a delete operation, and the id needs to be used as the delete identifier. At this time, the delete request will depend on the creation request. Before creating a request response, the virtual data will be used as an id placeholder as a parameter for deletion, and the virtual data id will be replaced after creating a request response, so that the deletion request can be completed. + +![virtual data](https://user-images.githubusercontent.com/29848971/220005505-d30b7ae2-ddd0-4080-81a4-65c9be2cb0bd.png) + +Next, we will learn more about the characteristics of virtual data. diff --git a/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/_category_.json b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/_category_.json index 89d03fd9e..42291c7e5 100644 --- a/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/_category_.json +++ b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/_category_.json @@ -1,3 +1,3 @@ -{ - "label": "Sensorless data interaction" -} +{ + "label": "Sensorless data interaction" +} diff --git a/versioned_docs/version-2.x/tutorial/05-strategy/_category_.json b/versioned_docs/version-2.x/tutorial/05-strategy/_category_.json index 03345525b..34e0bcbdb 100644 --- a/versioned_docs/version-2.x/tutorial/05-strategy/_category_.json +++ b/versioned_docs/version-2.x/tutorial/05-strategy/_category_.json @@ -1,3 +1,3 @@ -{ - "label": "Strategy" -} +{ + "label": "Strategy" +} diff --git a/versioned_docs/version-2.x/tutorial/06-advanced/_category_.json b/versioned_docs/version-2.x/tutorial/06-advanced/_category_.json index 3462df72e..aa8953a08 100644 --- a/versioned_docs/version-2.x/tutorial/06-advanced/_category_.json +++ b/versioned_docs/version-2.x/tutorial/06-advanced/_category_.json @@ -1,3 +1,3 @@ -{ - "label": "Advanced" -} +{ + "label": "Advanced" +} diff --git a/versioned_docs/version-2.x/tutorial/07-best-practice/_category_.json b/versioned_docs/version-2.x/tutorial/07-best-practice/_category_.json index fe9037f89..d36db35d3 100644 --- a/versioned_docs/version-2.x/tutorial/07-best-practice/_category_.json +++ b/versioned_docs/version-2.x/tutorial/07-best-practice/_category_.json @@ -1,6 +1,6 @@ -{ - "label": "Best practice", - "link": { - "type": "generated-index" - } -} +{ + "label": "Best practice", + "link": { + "type": "generated-index" + } +} diff --git a/versioned_docs/version-2.x/tutorial/08-request-adapter/_category_.json b/versioned_docs/version-2.x/tutorial/08-request-adapter/_category_.json index 2c4f02c49..faabff07b 100644 --- a/versioned_docs/version-2.x/tutorial/08-request-adapter/_category_.json +++ b/versioned_docs/version-2.x/tutorial/08-request-adapter/_category_.json @@ -1,6 +1,6 @@ -{ - "label": "Request adapter", - "link": { - "type": "generated-index" - } -} +{ + "label": "Request adapter", + "link": { + "type": "generated-index" + } +} diff --git a/versioned_docs/version-2.x/tutorial/09-framework/_category_.json b/versioned_docs/version-2.x/tutorial/09-framework/_category_.json index 6edd9139c..fb1ff2abe 100644 --- a/versioned_docs/version-2.x/tutorial/09-framework/_category_.json +++ b/versioned_docs/version-2.x/tutorial/09-framework/_category_.json @@ -1,6 +1,6 @@ -{ - "label": "Framework", - "link": { - "type": "generated-index" - } -} +{ + "label": "Framework", + "link": { + "type": "generated-index" + } +} diff --git a/versioned_docs/version-2.x/tutorial/10-custom/_category_.json b/versioned_docs/version-2.x/tutorial/10-custom/_category_.json index 505fadaab..6298d8ec1 100644 --- a/versioned_docs/version-2.x/tutorial/10-custom/_category_.json +++ b/versioned_docs/version-2.x/tutorial/10-custom/_category_.json @@ -1,6 +1,6 @@ -{ - "label": "Custom", - "link": { - "type": "generated-index" - } -} +{ + "label": "Custom", + "link": { + "type": "generated-index" + } +} diff --git a/versioned_docs/version-2.x/tutorial/11-others/_category_.json b/versioned_docs/version-2.x/tutorial/11-others/_category_.json index 07d61bd10..c562b25fb 100644 --- a/versioned_docs/version-2.x/tutorial/11-others/_category_.json +++ b/versioned_docs/version-2.x/tutorial/11-others/_category_.json @@ -1,6 +1,6 @@ -{ - "label": "Others", - "link": { - "type": "generated-index" - } -} +{ + "label": "Others", + "link": { + "type": "generated-index" + } +} diff --git a/versions.json b/versions.json index a61da5c98..a7e13bf41 100644 --- a/versions.json +++ b/versions.json @@ -1,3 +1 @@ -[ - "2.x" -] +["2.x"] From 03a031abadfb327cb32d24e28efa5913a7245f83 Mon Sep 17 00:00:00 2001 From: JOU Amjs Date: Sun, 23 Jun 2024 23:44:31 +0800 Subject: [PATCH 10/11] docs: update README.md --- docs/tutorial/02-getting-started/01-introduce.md | 6 +++++- .../current/tutorial/02-getting-started/01-introduce.md | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/tutorial/02-getting-started/01-introduce.md b/docs/tutorial/02-getting-started/01-introduce.md index a9a3dbebb..485f1f521 100644 --- a/docs/tutorial/02-getting-started/01-introduce.md +++ b/docs/tutorial/02-getting-started/01-introduce.md @@ -16,9 +16,13 @@ It can simplify API consumption from 7 steps to 1 step. You only need to choose ## How to do it? +### Core functions + +alova provides basic request capabilities similar to axios. You can use it with any request library such as axios and fetch to get out-of-the-box features such as response caching and request sharing. + ### Request strategy -In actual projects, front-end requests always need to consider when to make requests, when not to make requests, how to process response data, etc. according to different scenarios to meet the performance and performance improvement of the project. This will lead to an increase in the developer's time cost and code maintenance cost. In alova, a complete set of solutions for complex request scenarios is provided, which we call **request strategy**. Only one line of code can quickly implement various complex request logics, which can not only help you improve development efficiency, but also help you improve the running efficiency of the App and reduce the pressure on the server. +Based on the core functions of alova, it also provides a complete solution for complex request scenarios, which we call **request strategy**. With only one line of code, you can quickly implement various complex request logics, which can not only help you improve development efficiency, but also help you improve the running efficiency of the App and reduce the pressure on the server. For example, `useRequest` can automatically manage the request status, **`loading/error/data` is responsive data**, which can be directly bound in the view in UI frameworks such as react, vue, and svelte, and these responsive data will be automatically maintained according to the request status. diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/01-introduce.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/01-introduce.md index c833acff3..b28ba126b 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/01-introduce.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/01-introduce.md @@ -14,9 +14,13 @@ alova 是一个创新的下一代请求工具,从前后端协作和 API 消费 ## 如何做的? +### 核心功能 + +alova 提供了基础的,与 axios 相似的基础请求能力,你可以配合 axios、fetch 等任何请求库使用,获得响应缓存、请求共享等开箱即用的特性。 + ### 请求策略 -在实际项目中,前端请求总是需要根据不同场景考虑应该什么时候发出请求、什么时候不能发出请求、如何处理响应数据等才能满足项目的表现、性能的提高,这将导致开发人员时间成本和代码维护成本的增加,在 alova 中提供了一套完整的应对复杂请求场景的方案,我们称之为**请求策略**,只需一行代码就能快速实现各种复杂的请求逻辑,不仅能帮你提升开发效率,还能帮你提升 App 的运行效率,降低服务端压力。 +基于 alova 的核心功能,还提供了完整的应对复杂请求场景的方案,我们称之为**请求策略**,只需一行代码就能快速实现各种复杂的请求逻辑,不仅能帮你提升开发效率,还能帮你提升 App 的运行效率,降低服务端压力。 例如,`useRequest`可以自动管理请求状态,**`loading/error/data` 是响应式的数据**,在 react、vue、svelte 等 UI 框架中可以直接在视图中绑定它们,而且会根据请求状态自动维护它这些响应式数据。 From fc375dc1a4043a4d061b74e0a6acfe35ed100e02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=83=A1=E9=95=87?= Date: Mon, 24 Jun 2024 10:07:16 +0800 Subject: [PATCH 11/11] docs: beta install correct, beta tips add --- docs/release-notes-v3.md | 4 ++-- docs/resource/01-request-adapter/01-alova-mock.md | 4 ++-- .../01-request-adapter/02-alova-adapter-xhr.md | 4 ++-- .../01-request-adapter/03-alova-adapter-axios.md | 4 ++-- .../01-request-adapter/04-alova-adapter-taro.md | 4 ++-- .../01-request-adapter/05-alova-adapter-uniapp.md | 4 ++-- docs/resource/02-storage-adapter/01-psc.md | 2 +- docs/resource/03-framework/07-vue-options.md | 4 ++-- docs/tutorial/02-getting-started/02-quick-start.md | 14 ++++++++++---- .../current/release-notes-v3.md | 4 ++-- .../resource/01-request-adapter/01-alova-mock.md | 4 ++-- .../01-request-adapter/02-alova-adapter-xhr.md | 4 ++-- .../01-request-adapter/03-alova-adapter-axios.md | 4 ++-- .../01-request-adapter/04-alova-adapter-taro.md | 4 ++-- .../01-request-adapter/05-alova-adapter-uniapp.md | 4 ++-- .../current/resource/02-storage-adapter/01-psc.md | 2 +- .../resource/03-framework/07-vue-options.md | 4 ++-- .../tutorial/02-getting-started/02-quick-start.md | 14 ++++++++++---- 18 files changed, 50 insertions(+), 38 deletions(-) diff --git a/docs/release-notes-v3.md b/docs/release-notes-v3.md index c7eefee76..bc02d60b5 100644 --- a/docs/release-notes-v3.md +++ b/docs/release-notes-v3.md @@ -1,10 +1,10 @@ --- -title: 'alova v3.0 Release Notes' +title: alova v3.0 Release Notes --- :::warning beta reminder -The release log of alova@3.0 is currently in the beta stage and may be changed later. Please use it with caution. +alova@3.0 is currently in the beta stage, and some functions may be changed. If you find a bug, please let us know in [Github issues](https://github.com/alovajs/alova/issues/new/choose), and we will solve it as soon as possible. ::: diff --git a/docs/resource/01-request-adapter/01-alova-mock.md b/docs/resource/01-request-adapter/01-alova-mock.md index cb921f5b4..4f6d06acf 100644 --- a/docs/resource/01-request-adapter/01-alova-mock.md +++ b/docs/resource/01-request-adapter/01-alova-mock.md @@ -20,14 +20,14 @@ This mock plug-in is an alova request adapter. Different from the traditional Pr ```bash -npm install @alova/mock --save +npm install @alova/mock@beta --save ``` ```bash -yarn add @alova/mock +yarn add @alova/mock@beta ``` diff --git a/docs/resource/01-request-adapter/02-alova-adapter-xhr.md b/docs/resource/01-request-adapter/02-alova-adapter-xhr.md index 3a280593c..3bacd286d 100644 --- a/docs/resource/01-request-adapter/02-alova-adapter-xhr.md +++ b/docs/resource/01-request-adapter/02-alova-adapter-xhr.md @@ -11,14 +11,14 @@ import TabItem from '@theme/TabItem'; ```bash -npm install @alova/adapter-xhr --save +npm install @alova/adapter-xhr@beta --save ``` ```bash -yarn add @alova/adapter-xhr +yarn add @alova/adapter-xhr@beta ``` diff --git a/docs/resource/01-request-adapter/03-alova-adapter-axios.md b/docs/resource/01-request-adapter/03-alova-adapter-axios.md index 891430935..3d3cae9ae 100644 --- a/docs/resource/01-request-adapter/03-alova-adapter-axios.md +++ b/docs/resource/01-request-adapter/03-alova-adapter-axios.md @@ -11,14 +11,14 @@ import TabItem from '@theme/TabItem'; ```bash -npm install @alova/adapter-axios --save +npm install @alova/adapter-axios@beta --save ``` ```bash -yarn add @alova/adapter-axios +yarn add @alova/adapter-axios@beta ``` diff --git a/docs/resource/01-request-adapter/04-alova-adapter-taro.md b/docs/resource/01-request-adapter/04-alova-adapter-taro.md index 44edb2d0a..995938fd7 100644 --- a/docs/resource/01-request-adapter/04-alova-adapter-taro.md +++ b/docs/resource/01-request-adapter/04-alova-adapter-taro.md @@ -17,14 +17,14 @@ This plugin only supports the taro application of react 16.8+, vue3 version. ```bash -npm install @alova/adapter-taro --save +npm install @alova/adapter-taro@beta --save ``` ```bash -yarn add @alova/adapter-taro +yarn add @alova/adapter-taro@beta ``` diff --git a/docs/resource/01-request-adapter/05-alova-adapter-uniapp.md b/docs/resource/01-request-adapter/05-alova-adapter-uniapp.md index a23406852..365c42430 100644 --- a/docs/resource/01-request-adapter/05-alova-adapter-uniapp.md +++ b/docs/resource/01-request-adapter/05-alova-adapter-uniapp.md @@ -17,14 +17,14 @@ This plugin only supports vue3 version of uniapp application. ```bash -npm install @alova/adapter-uniapp --save +npm install @alova/adapter-uniapp@beta --save ``` ```bash -yarn add @alova/adapter-uniapp +yarn add @alova/adapter-uniapp@beta ``` diff --git a/docs/resource/02-storage-adapter/01-psc.md b/docs/resource/02-storage-adapter/01-psc.md index 436d01410..f4659fd39 100644 --- a/docs/resource/02-storage-adapter/01-psc.md +++ b/docs/resource/02-storage-adapter/01-psc.md @@ -15,7 +15,7 @@ Only supports Alova 3.0 and above. ## Install ```bash -npm install @alova/psc --save +npm install @alova/psc@beta --save ``` ## Use in Node.js diff --git a/docs/resource/03-framework/07-vue-options.md b/docs/resource/03-framework/07-vue-options.md index ef376061a..884be7b36 100644 --- a/docs/resource/03-framework/07-vue-options.md +++ b/docs/resource/03-framework/07-vue-options.md @@ -22,14 +22,14 @@ Usually, use hook can only be used in vue's setup, but through the auxiliary fun ```bash -npm install alova @alova/vue-options --save +npm install alova @alova/vue-options@beta --save ``` ```bash -yarn add alova @alova/vue-options +yarn add alova @alova/vue-options@beta ``` diff --git a/docs/tutorial/02-getting-started/02-quick-start.md b/docs/tutorial/02-getting-started/02-quick-start.md index 8bce9680d..c51a3b249 100644 --- a/docs/tutorial/02-getting-started/02-quick-start.md +++ b/docs/tutorial/02-getting-started/02-quick-start.md @@ -9,6 +9,12 @@ import EmbedSandpack from "@site/src/components/EmbedSandpack"; import quickStartGET from '!!raw-loader!@site/codesandbox/01-getting-started/02-first-request/get.js'; import quickStartPOST from '!!raw-loader!@site/codesandbox/01-getting-started/02-first-request/post.js'; +:::warning beta reminder + +alova@3.0 is currently in the beta stage, and some functions may be changed. If you find a bug, please let us know in [Github issues](https://github.com/alovajs/alova/issues/new/choose), and we will solve it as soon as possible. + +::: + :::tip Example Tips If you haven't learned about alova yet, it is recommended that you read [introduce alova](/next/tutorial/getting-started/introduce) first. @@ -21,28 +27,28 @@ If you haven't learned about alova yet, it is recommended that you read [introdu ```bash -npm install alova --save +npm install alova@beta --save ``` ```bash -yarn add alova +yarn add alova@beta ``` ```bash -pnpm add alova +pnpm add alova@beta ``` ```bash -bun add alova +bun add alova@beta ``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/release-notes-v3.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/release-notes-v3.md index a16d7b449..ad36bc377 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/release-notes-v3.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/release-notes-v3.md @@ -1,10 +1,10 @@ --- -title: 'alova v3.0 Release Notes' +title: alova v3.0 发布日志 --- :::warning beta 提醒 -alova@3.0 的发布日志,目前处于 beta 阶段,后续可能有所改动,请谨慎使用。 +alova@3.0 目前处于 beta 阶段,小部分功能可能会有所改动,如果发现 bug 请在[Github issues](https://github.com/alovajs/alova/issues/new/choose)中告诉我们,我们会在第一时间解决。 ::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/01-alova-mock.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/01-alova-mock.md index fe384e083..29b0a6265 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/01-alova-mock.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/01-alova-mock.md @@ -20,14 +20,14 @@ import TabItem from '@theme/TabItem'; ```bash -npm install @alova/mock --save +npm install @alova/mock@beta --save ``` ```bash -yarn add @alova/mock +yarn add @alova/mock@beta ``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/02-alova-adapter-xhr.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/02-alova-adapter-xhr.md index b5d1ae7de..96b99cd05 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/02-alova-adapter-xhr.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/02-alova-adapter-xhr.md @@ -11,14 +11,14 @@ import TabItem from '@theme/TabItem'; ```bash -npm install @alova/adapter-xhr --save +npm install @alova/adapter-xhr@beta --save ``` ```bash -yarn add @alova/adapter-xhr +yarn add @alova/adapter-xhr@beta ``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/03-alova-adapter-axios.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/03-alova-adapter-axios.md index 446ea05e3..cfd83eb95 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/03-alova-adapter-axios.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/03-alova-adapter-axios.md @@ -11,14 +11,14 @@ import TabItem from '@theme/TabItem'; ```bash -npm install @alova/adapter-axios --save +npm install @alova/adapter-axios@beta --save ``` ```bash -yarn add @alova/adapter-axios +yarn add @alova/adapter-axios@beta ``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/04-alova-adapter-taro.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/04-alova-adapter-taro.md index 0d5078460..17ec680db 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/04-alova-adapter-taro.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/04-alova-adapter-taro.md @@ -17,14 +17,14 @@ import TabItem from '@theme/TabItem'; ```bash -npm install @alova/adapter-taro --save +npm install @alova/adapter-taro@beta --save ``` ```bash -yarn add @alova/adapter-taro +yarn add @alova/adapter-taro@beta ``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/05-alova-adapter-uniapp.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/05-alova-adapter-uniapp.md index 40e14801a..eb8c511b9 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/05-alova-adapter-uniapp.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/01-request-adapter/05-alova-adapter-uniapp.md @@ -17,14 +17,14 @@ import TabItem from '@theme/TabItem'; ```bash -npm install @alova/adapter-uniapp --save +npm install @alova/adapter-uniapp@beta --save ``` ```bash -yarn add @alova/adapter-uniapp +yarn add @alova/adapter-uniapp@beta ``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/02-storage-adapter/01-psc.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/02-storage-adapter/01-psc.md index 6492b427f..24c44293f 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/02-storage-adapter/01-psc.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/02-storage-adapter/01-psc.md @@ -15,7 +15,7 @@ title: 进程共享适配器 ## 安装 ```bash -npm install @alova/ psc --save +npm install @alova/psc@beta --save ``` ## 在 Node.js 中使用 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/07-vue-options.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/07-vue-options.md index 8b203b31d..6faa84615 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/07-vue-options.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/resource/03-framework/07-vue-options.md @@ -21,14 +21,14 @@ import TabItem from '@theme/TabItem'; ```bash -npm install alova @alova/vue-options --save +npm install alova @alova/vue-options@beta --save ``` ```bash -yarn add alova @alova/vue-options +yarn add alova @alova/vue-options@beta ``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/02-quick-start.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/02-quick-start.md index 39fa57b50..a644c7f2a 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/02-quick-start.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/tutorial/02-getting-started/02-quick-start.md @@ -9,6 +9,12 @@ import EmbedSandpack from "@site/src/components/EmbedSandpack"; import quickStartGET from '!!raw-loader!@site/codesandbox/01-getting-started/02-first-request/get.js'; import quickStartPOST from '!!raw-loader!@site/codesandbox/01-getting-started/02-first-request/post.js'; +:::warning beta 提醒 + +alova@3.0 目前处于 beta 阶段,小部分功能可能会有所改动,如果发现 bug 请在[Github issues](https://github.com/alovajs/alova/issues/new/choose)中告诉我们,我们会在第一时间解决。 + +::: + :::tip 示例提示 如果你还未了解 alova,推荐你先阅读 [alova 介绍](/next/tutorial/getting-started/introduce)。 @@ -21,28 +27,28 @@ import quickStartPOST from '!!raw-loader!@site/codesandbox/01-getting-started/02 ```bash -npm install alova --save +npm install alova@beta --save ``` ```bash -yarn add alova +yarn add alova@beta ``` ```bash -pnpm add alova +pnpm add alova@beta ``` ```bash -bun add alova +bun add alova@beta ```